1
package org.hisp.dhis.datamart.impl;
4
* Copyright (c) 2004-2007, University of Oslo
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions are met:
9
* * Redistributions of source code must retain the above copyright notice, this
10
* list of conditions and the following disclaimer.
11
* * Redistributions in binary form must reproduce the above copyright notice,
12
* this list of conditions and the following disclaimer in the documentation
13
* and/or other materials provided with the distribution.
14
* * Neither the name of the HISP project nor the names of its contributors may
15
* be used to endorse or promote products derived from this software without
16
* specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
import java.util.ArrayList;
31
import java.util.Collection;
32
import java.util.HashSet;
35
import org.apache.commons.logging.Log;
36
import org.apache.commons.logging.LogFactory;
37
import org.hisp.dhis.dataelement.CalculatedDataElement;
38
import org.hisp.dhis.dataelement.DataElement;
39
import org.hisp.dhis.dataelement.DataElementCategoryOptionComboService;
40
import org.hisp.dhis.dataelement.DataElementService;
41
import org.hisp.dhis.dataelement.Operand;
42
import org.hisp.dhis.datamart.DataMartInternalProcess;
43
import org.hisp.dhis.datamart.DataMartStore;
44
import org.hisp.dhis.datamart.aggregation.cache.AggregationCache;
45
import org.hisp.dhis.datamart.aggregation.dataelement.DataElementAggregator;
46
import org.hisp.dhis.datamart.calculateddataelement.CalculatedDataElementDataMart;
47
import org.hisp.dhis.datamart.crosstab.CrossTabService;
48
import org.hisp.dhis.datamart.dataelement.DataElementDataMart;
49
import org.hisp.dhis.datamart.indicator.IndicatorDataMart;
50
import org.hisp.dhis.indicator.Indicator;
51
import org.hisp.dhis.indicator.IndicatorService;
52
import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
53
import org.hisp.dhis.period.Period;
54
import org.hisp.dhis.period.PeriodService;
55
import org.hisp.dhis.system.util.ConversionUtils;
56
import org.hisp.dhis.system.util.TimeUtils;
58
import static org.hisp.dhis.datamart.util.ParserUtil.getDataElementIdsInExpression;
61
* @author Lars Helge Overland
62
* @version $Id: DefaultDataMartService.java 6260 2008-11-11 15:58:43Z larshelg $
64
public class DefaultDataMartService
65
extends DataMartInternalProcess
67
private static final Log log = LogFactory.getLog( DefaultDataMartService.class );
69
// -------------------------------------------------------------------------
71
// -------------------------------------------------------------------------
73
protected AggregationCache aggregationCache;
75
public void setAggregationCache( AggregationCache aggregationCache )
77
this.aggregationCache = aggregationCache;
80
private DataMartStore dataMartStore;
82
public void setDataMartStore( DataMartStore dataMartStore )
84
this.dataMartStore = dataMartStore;
87
private CrossTabService crossTabService;
89
public void setCrossTabService( CrossTabService crossTabService )
91
this.crossTabService = crossTabService;
94
private DataElementService dataElementService;
96
public void setDataElementService( DataElementService dataElementService )
98
this.dataElementService = dataElementService;
101
private IndicatorService indicatorService;
103
public void setIndicatorService( IndicatorService indicatorService )
105
this.indicatorService = indicatorService;
108
private PeriodService periodService;
110
public void setPeriodService( PeriodService periodService )
112
this.periodService = periodService;
115
private DataElementCategoryOptionComboService categoryOptionComboService;
117
public void setCategoryOptionComboService( DataElementCategoryOptionComboService categoryOptionComboService )
119
this.categoryOptionComboService = categoryOptionComboService;
122
private DataElementDataMart dataElementDataMart;
124
public void setDataElementDataMart( DataElementDataMart dataElementDataMart )
126
this.dataElementDataMart = dataElementDataMart;
129
private IndicatorDataMart indicatorDataMart;
131
public void setIndicatorDataMart( IndicatorDataMart indicatorDataMart )
133
this.indicatorDataMart = indicatorDataMart;
136
private CalculatedDataElementDataMart calculatedDataElementDataMart;
138
public void setCalculatedDataElementDataMart( CalculatedDataElementDataMart calculatedDataElementDataMart )
140
this.calculatedDataElementDataMart = calculatedDataElementDataMart;
143
private DataElementAggregator sumIntAggregator;
145
public void setSumIntAggregator( DataElementAggregator sumIntDataElementAggregator )
147
this.sumIntAggregator = sumIntDataElementAggregator;
150
private DataElementAggregator averageIntAggregator;
152
public void setAverageIntAggregator( DataElementAggregator averageIntDataElementAggregator )
154
this.averageIntAggregator = averageIntDataElementAggregator;
157
private DataElementAggregator sumBoolAggregator;
159
public void setSumBoolAggregator( DataElementAggregator sumBooleanDataElementAggregator )
161
this.sumBoolAggregator = sumBooleanDataElementAggregator;
164
private DataElementAggregator averageBoolAggregator;
166
public void setAverageBoolAggregator( DataElementAggregator averageBooleanDataElementAggregator )
168
this.averageBoolAggregator = averageBooleanDataElementAggregator;
171
// -------------------------------------------------------------------------
172
// DataMartInternalProcess implementation
173
// -------------------------------------------------------------------------
175
public int export( final Collection<Integer> dataElementIds, final Collection<Integer> indicatorIds,
176
final Collection<Integer> periodIds, final Collection<Integer> organisationUnitIds )
180
log.info( "Export process started" );
184
setMessage( "deleting_existing_aggregated_data" );
186
// ---------------------------------------------------------------------
187
// Delete existing aggregated data
188
// ---------------------------------------------------------------------
190
dataMartStore.deleteAggregatedDataValues( dataElementIds, periodIds, organisationUnitIds );
192
dataMartStore.deleteAggregatedIndicatorValues( indicatorIds, periodIds, organisationUnitIds );
194
log.info( "Deleted existing aggregated data: " + TimeUtils.getHMS() );
196
setMessage( "crosstabulating_data" );
198
// ---------------------------------------------------------------------
199
// Crosstabulate data
200
// ---------------------------------------------------------------------
202
final Set<Integer> nonCalculatedDataElementIds = filterCalculatedDataElementIds( dataElementIds, false );
203
final Set<Integer> calculatedDataElementIds = filterCalculatedDataElementIds( dataElementIds, true );
205
final Set<Integer> dataElementInIndicatorIds = getDataElementIdsInIndicators( indicatorIds );
206
final Set<Integer> dataElementInCalculatedDataElementIds = getDataElementIdsInCalculatedDataElements( calculatedDataElementIds );
208
final Set<Integer> allDataElementIds = new HashSet<Integer>();
209
allDataElementIds.addAll( nonCalculatedDataElementIds );
210
allDataElementIds.addAll( dataElementInIndicatorIds );
211
allDataElementIds.addAll( dataElementInCalculatedDataElementIds );
213
final Collection<Operand> allDataElementOperands = categoryOptionComboService.getOperands( dataElementService.getDataElements( allDataElementIds ) );
214
final Collection<Operand> dataElementInIndicatorOperands = categoryOptionComboService.getOperands( dataElementService.getDataElements( dataElementInIndicatorIds ) );
215
final Collection<Operand> dataElementInCalculatedDataElementOperands = categoryOptionComboService.getOperands( dataElementService.getDataElements( dataElementInCalculatedDataElementIds ) );
217
final Collection<Operand> emptyOperands = crossTabService.populateCrossTabTable( allDataElementOperands, getIntersectingIds( periodIds ),
218
getIdsWithChildren( organisationUnitIds ) );
220
log.info( "Populated crosstab table: " + TimeUtils.getHMS() );
222
crossTabService.trimCrossTabTable( emptyOperands );
224
log.info( "Trimmed crosstab table: " + TimeUtils.getHMS() );
226
final Collection<DataElement> dataElements = dataElementService.getDataElements( nonCalculatedDataElementIds );
228
final Collection<Operand> sumIntOperands = getOperands( dataElements, DataElement.AGGREGATION_OPERATOR_SUM, DataElement.TYPE_INT );
229
final Collection<Operand> averageIntOperands = getOperands( dataElements, DataElement.AGGREGATION_OPERATOR_AVERAGE, DataElement.TYPE_INT );
230
final Collection<Operand> sumBooleanOperands = getOperands( dataElements, DataElement.AGGREGATION_OPERATOR_SUM, DataElement.TYPE_BOOL );
231
final Collection<Operand> averageBooleanOperands = getOperands( dataElements, DataElement.AGGREGATION_OPERATOR_AVERAGE, DataElement.TYPE_BOOL );
233
// ---------------------------------------------------------------------
234
// Data element export
235
// ---------------------------------------------------------------------
237
setMessage( "exporting_data_for_data_elements" );
239
if ( sumIntOperands.size() > 0 )
241
count += dataElementDataMart.exportDataValues( sumIntOperands, periodIds, organisationUnitIds, sumIntAggregator );
243
log.info( "Exported values for data elements with sum aggregation operator of type number (" + sumIntOperands.size() + "): " + TimeUtils.getHMS() );
246
if ( averageIntOperands.size() > 0 )
248
count += dataElementDataMart.exportDataValues( averageIntOperands, periodIds, organisationUnitIds, averageIntAggregator );
250
log.info( "Exported values for data elements with average aggregation operator of type number (" + averageIntOperands.size() + "): " + TimeUtils.getHMS() );
253
if ( sumBooleanOperands.size() > 0 )
255
count += dataElementDataMart.exportDataValues( sumBooleanOperands, periodIds, organisationUnitIds, sumBoolAggregator );
257
log.info( "Exported values for data elements with sum aggregation operator of type yes/no (" + sumBooleanOperands.size() + "): " + TimeUtils.getHMS() );
260
if ( averageBooleanOperands.size() > 0 )
262
count += dataElementDataMart.exportDataValues( averageBooleanOperands, periodIds, organisationUnitIds, averageBoolAggregator );
264
log.info( "Exported values for data elements with average aggregation operator of type yes/no (" + averageBooleanOperands.size() + "): " + TimeUtils.getHMS() );
267
setMessage( "exporting_data_for_indicators" );
269
// ---------------------------------------------------------------------
271
// ---------------------------------------------------------------------
273
if ( indicatorIds != null && indicatorIds.size() > 0 )
275
count += indicatorDataMart.exportIndicatorValues( indicatorIds, periodIds, organisationUnitIds, dataElementInIndicatorOperands );
277
log.info( "Exported values for indicators (" + indicatorIds.size() + "): " + TimeUtils.getHMS() );
280
setMessage( "exporting_data_for_calculated_data_elements" );
282
// ---------------------------------------------------------------------
283
// Calculated data element export
284
// ---------------------------------------------------------------------
286
if ( calculatedDataElementIds != null && calculatedDataElementIds.size() > 0 )
288
count += calculatedDataElementDataMart.exportCalculatedDataElements( calculatedDataElementIds, periodIds, organisationUnitIds, dataElementInCalculatedDataElementOperands );
290
log.info( "Exported values for calculated data elements (" + calculatedDataElementIds.size() + "): " + TimeUtils.getHMS() );
293
crossTabService.dropCrossTabTable();
295
log.info( "Export process completed: " + TimeUtils.getHMS() );
299
setMessage( "export_process_done" );
301
aggregationCache.clearCache();
306
// -------------------------------------------------------------------------
307
// Supportive methods
308
// -------------------------------------------------------------------------
311
* Sorts calculated data elements from non-calculated based on the given collection
312
* of identifiers and flag.
314
private Set<Integer> filterCalculatedDataElementIds( final Collection<Integer> dataElementIds, boolean calculated )
316
final Set<Integer> identifiers = new HashSet<Integer>();
318
for ( final Integer id : dataElementIds )
320
final DataElement element = dataElementService.getDataElement( id );
322
if ( ( element instanceof CalculatedDataElement ) == calculated )
324
identifiers.add( id );
332
* Returns all data element identifiers included in the indicators in the given
333
* identifier collection.
335
private Set<Integer> getDataElementIdsInIndicators( final Collection<Integer> indicatorIds )
337
final Set<Integer> identifiers = new HashSet<Integer>( indicatorIds.size() );
339
for ( final Integer id : indicatorIds )
341
final Indicator indicator = indicatorService.getIndicator( id );
343
identifiers.addAll( getDataElementIdsInExpression( indicator.getNumerator() ) );
344
identifiers.addAll( getDataElementIdsInExpression( indicator.getDenominator() ) );
351
* Returns all data element identifiers included in the calculated data elements
352
* in the given identifier collection.
354
private Set<Integer> getDataElementIdsInCalculatedDataElements( final Collection<Integer> calculatedDataElementIds )
356
final Set<Integer> identifiers = new HashSet<Integer>();
358
for ( final Integer id : calculatedDataElementIds )
360
final DataElement element = dataElementService.getDataElement( id );
362
if ( element instanceof CalculatedDataElement )
364
final CalculatedDataElement calculatedElement = (CalculatedDataElement) element;
366
final Set<Integer> dataElementIds = getDataElementIdsInExpression( calculatedElement.getExpression().getExpression() );
368
if ( dataElementIds != null )
370
identifiers.addAll( dataElementIds );
379
* Returns the idenfifiers in given collection including all of its children.
381
private Collection<Integer> getIdsWithChildren( final Collection<Integer> organisationUnitIds )
383
final OrganisationUnitHierarchy hierarchy = aggregationCache.getLatestOrganisationUnitHierarchy();
385
final Set<Integer> identifers = new HashSet<Integer>( organisationUnitIds.size() );
387
for ( final Integer id : organisationUnitIds )
389
identifers.addAll( aggregationCache.getChildren( hierarchy, id ) );
396
* Returns the identifiers of the periods in the given collection including
397
* all intersecting periods.
399
private Collection<Integer> getIntersectingIds( final Collection<Integer> periodIds )
401
final Set<Integer> identifiers = new HashSet<Integer>( periodIds.size() );
403
for ( final Integer id : periodIds )
405
final Period period = periodService.getPeriod( id );
407
final Collection<Period> periods = periodService.getIntersectingPeriods( period.getStartDate(), period.getEndDate() );
409
identifiers.addAll( ConversionUtils.getIdentifiers( Period.class, periods ) );
416
* Sorts out the data element identifers of the given aggregation operator and
419
private Collection<Operand> getOperands( final Collection<DataElement> dataElements, String aggregationOperator, String type )
421
final Collection<DataElement> section = new ArrayList<DataElement>();
423
for ( final DataElement element : dataElements )
425
if ( element.getAggregationOperator().equals( aggregationOperator ) && element.getType().equals( type ) )
427
section.add( element );
431
return categoryOptionComboService.getOperands( section );