~mortenoh/+junk/dhis2-detailed-import-export

« back to all changes in this revision

Viewing changes to dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/impl/DefaultDataMartService.java

  • Committer: larshelge at gmail
  • Date: 2009-03-03 16:46:36 UTC
  • Revision ID: larshelge@gmail.com-20090303164636-2sjlrquo7ib1gf7r
Initial check-in

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.hisp.dhis.datamart.impl;
 
2
 
 
3
/*
 
4
 * Copyright (c) 2004-2007, University of Oslo
 
5
 * All rights reserved.
 
6
 *
 
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.
 
17
 *
 
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.
 
28
 */
 
29
 
 
30
import java.util.ArrayList;
 
31
import java.util.Collection;
 
32
import java.util.HashSet;
 
33
import java.util.Set;
 
34
 
 
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;
 
57
 
 
58
import static org.hisp.dhis.datamart.util.ParserUtil.getDataElementIdsInExpression;
 
59
 
 
60
/**
 
61
 * @author Lars Helge Overland
 
62
 * @version $Id: DefaultDataMartService.java 6260 2008-11-11 15:58:43Z larshelg $
 
63
 */
 
64
public class DefaultDataMartService
 
65
    extends DataMartInternalProcess
 
66
{
 
67
    private static final Log log = LogFactory.getLog( DefaultDataMartService.class );
 
68
    
 
69
    // -------------------------------------------------------------------------
 
70
    // Dependencies
 
71
    // -------------------------------------------------------------------------
 
72
 
 
73
    protected AggregationCache aggregationCache;
 
74
        
 
75
    public void setAggregationCache( AggregationCache aggregationCache )
 
76
    {
 
77
        this.aggregationCache = aggregationCache;
 
78
    }
 
79
    
 
80
    private DataMartStore dataMartStore;
 
81
 
 
82
    public void setDataMartStore( DataMartStore dataMartStore )
 
83
    {
 
84
        this.dataMartStore = dataMartStore;
 
85
    }
 
86
 
 
87
    private CrossTabService crossTabService;
 
88
 
 
89
    public void setCrossTabService( CrossTabService crossTabService )
 
90
    {
 
91
        this.crossTabService = crossTabService;
 
92
    }
 
93
    
 
94
    private DataElementService dataElementService;
 
95
 
 
96
    public void setDataElementService( DataElementService dataElementService )
 
97
    {
 
98
        this.dataElementService = dataElementService;
 
99
    }
 
100
    
 
101
    private IndicatorService indicatorService;
 
102
 
 
103
    public void setIndicatorService( IndicatorService indicatorService )
 
104
    {
 
105
        this.indicatorService = indicatorService;
 
106
    }
 
107
    
 
108
    private PeriodService periodService;
 
109
 
 
110
    public void setPeriodService( PeriodService periodService )
 
111
    {
 
112
        this.periodService = periodService;
 
113
    }
 
114
    
 
115
    private DataElementCategoryOptionComboService categoryOptionComboService;
 
116
 
 
117
    public void setCategoryOptionComboService( DataElementCategoryOptionComboService categoryOptionComboService )
 
118
    {
 
119
        this.categoryOptionComboService = categoryOptionComboService;
 
120
    }
 
121
    
 
122
    private DataElementDataMart dataElementDataMart;
 
123
 
 
124
    public void setDataElementDataMart( DataElementDataMart dataElementDataMart )
 
125
    {
 
126
        this.dataElementDataMart = dataElementDataMart;
 
127
    }
 
128
 
 
129
    private IndicatorDataMart indicatorDataMart;
 
130
 
 
131
    public void setIndicatorDataMart( IndicatorDataMart indicatorDataMart )
 
132
    {
 
133
        this.indicatorDataMart = indicatorDataMart;
 
134
    }
 
135
    
 
136
    private CalculatedDataElementDataMart calculatedDataElementDataMart;
 
137
 
 
138
    public void setCalculatedDataElementDataMart( CalculatedDataElementDataMart calculatedDataElementDataMart )
 
139
    {
 
140
        this.calculatedDataElementDataMart = calculatedDataElementDataMart;
 
141
    }
 
142
    
 
143
    private DataElementAggregator sumIntAggregator;
 
144
 
 
145
    public void setSumIntAggregator( DataElementAggregator sumIntDataElementAggregator )
 
146
    {
 
147
        this.sumIntAggregator = sumIntDataElementAggregator;
 
148
    }
 
149
 
 
150
    private DataElementAggregator averageIntAggregator;
 
151
 
 
152
    public void setAverageIntAggregator( DataElementAggregator averageIntDataElementAggregator )
 
153
    {
 
154
        this.averageIntAggregator = averageIntDataElementAggregator;
 
155
    }
 
156
 
 
157
    private DataElementAggregator sumBoolAggregator;
 
158
 
 
159
    public void setSumBoolAggregator( DataElementAggregator sumBooleanDataElementAggregator )
 
160
    {
 
161
        this.sumBoolAggregator = sumBooleanDataElementAggregator;
 
162
    }
 
163
 
 
164
    private DataElementAggregator averageBoolAggregator;
 
165
 
 
166
    public void setAverageBoolAggregator( DataElementAggregator averageBooleanDataElementAggregator )
 
167
    {
 
168
        this.averageBoolAggregator = averageBooleanDataElementAggregator;
 
169
    }
 
170
 
 
171
    // -------------------------------------------------------------------------
 
172
    // DataMartInternalProcess implementation
 
173
    // -------------------------------------------------------------------------
 
174
 
 
175
    public int export( final Collection<Integer> dataElementIds, final Collection<Integer> indicatorIds,
 
176
        final Collection<Integer> periodIds, final Collection<Integer> organisationUnitIds )
 
177
    {
 
178
        int count = 0;
 
179
                
 
180
        log.info( "Export process started" );
 
181
        
 
182
        TimeUtils.start();
 
183
 
 
184
        setMessage( "deleting_existing_aggregated_data" );
 
185
 
 
186
        // ---------------------------------------------------------------------
 
187
        // Delete existing aggregated data
 
188
        // ---------------------------------------------------------------------
 
189
 
 
190
        dataMartStore.deleteAggregatedDataValues( dataElementIds, periodIds, organisationUnitIds );
 
191
        
 
192
        dataMartStore.deleteAggregatedIndicatorValues( indicatorIds, periodIds, organisationUnitIds );
 
193
        
 
194
        log.info( "Deleted existing aggregated data: " + TimeUtils.getHMS() );
 
195
 
 
196
        setMessage( "crosstabulating_data" );
 
197
 
 
198
        // ---------------------------------------------------------------------
 
199
        // Crosstabulate data
 
200
        // ---------------------------------------------------------------------
 
201
 
 
202
        final Set<Integer> nonCalculatedDataElementIds = filterCalculatedDataElementIds( dataElementIds, false );
 
203
        final Set<Integer> calculatedDataElementIds = filterCalculatedDataElementIds( dataElementIds, true );
 
204
        
 
205
        final Set<Integer> dataElementInIndicatorIds = getDataElementIdsInIndicators( indicatorIds );
 
206
        final Set<Integer> dataElementInCalculatedDataElementIds = getDataElementIdsInCalculatedDataElements( calculatedDataElementIds );
 
207
        
 
208
        final Set<Integer> allDataElementIds = new HashSet<Integer>();
 
209
        allDataElementIds.addAll( nonCalculatedDataElementIds );
 
210
        allDataElementIds.addAll( dataElementInIndicatorIds );
 
211
        allDataElementIds.addAll( dataElementInCalculatedDataElementIds );
 
212
        
 
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 ) );
 
216
        
 
217
        final Collection<Operand> emptyOperands = crossTabService.populateCrossTabTable( allDataElementOperands, getIntersectingIds( periodIds ), 
 
218
            getIdsWithChildren( organisationUnitIds ) );
 
219
 
 
220
        log.info( "Populated crosstab table: " + TimeUtils.getHMS() );
 
221
                
 
222
        crossTabService.trimCrossTabTable( emptyOperands );
 
223
 
 
224
        log.info( "Trimmed crosstab table: " + TimeUtils.getHMS() );
 
225
        
 
226
        final Collection<DataElement> dataElements = dataElementService.getDataElements( nonCalculatedDataElementIds );
 
227
        
 
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 );
 
232
 
 
233
        // ---------------------------------------------------------------------
 
234
        // Data element export
 
235
        // ---------------------------------------------------------------------
 
236
 
 
237
        setMessage( "exporting_data_for_data_elements" );
 
238
 
 
239
        if ( sumIntOperands.size() > 0 )
 
240
        {
 
241
            count += dataElementDataMart.exportDataValues( sumIntOperands, periodIds, organisationUnitIds, sumIntAggregator );
 
242
        
 
243
            log.info( "Exported values for data elements with sum aggregation operator of type number (" + sumIntOperands.size() + "): " + TimeUtils.getHMS() );
 
244
        }
 
245
 
 
246
        if ( averageIntOperands.size() > 0 )
 
247
        {            
 
248
            count += dataElementDataMart.exportDataValues( averageIntOperands, periodIds, organisationUnitIds, averageIntAggregator );
 
249
        
 
250
            log.info( "Exported values for data elements with average aggregation operator of type number (" + averageIntOperands.size() + "): " + TimeUtils.getHMS() );
 
251
        }
 
252
 
 
253
        if ( sumBooleanOperands.size() > 0 )
 
254
        {
 
255
            count += dataElementDataMart.exportDataValues( sumBooleanOperands, periodIds, organisationUnitIds, sumBoolAggregator );
 
256
            
 
257
            log.info( "Exported values for data elements with sum aggregation operator of type yes/no (" + sumBooleanOperands.size() + "): " + TimeUtils.getHMS() );
 
258
        }
 
259
 
 
260
        if ( averageBooleanOperands.size() > 0 )
 
261
        {
 
262
            count += dataElementDataMart.exportDataValues( averageBooleanOperands, periodIds, organisationUnitIds, averageBoolAggregator );
 
263
            
 
264
            log.info( "Exported values for data elements with average aggregation operator of type yes/no (" + averageBooleanOperands.size() + "): " + TimeUtils.getHMS() );
 
265
        }
 
266
 
 
267
        setMessage( "exporting_data_for_indicators" );
 
268
 
 
269
        // ---------------------------------------------------------------------
 
270
        // Indicator export
 
271
        // ---------------------------------------------------------------------
 
272
 
 
273
        if ( indicatorIds != null && indicatorIds.size() > 0 )
 
274
        {
 
275
            count += indicatorDataMart.exportIndicatorValues( indicatorIds, periodIds, organisationUnitIds, dataElementInIndicatorOperands );
 
276
            
 
277
            log.info( "Exported values for indicators (" + indicatorIds.size() + "): " + TimeUtils.getHMS() );
 
278
        }
 
279
 
 
280
        setMessage( "exporting_data_for_calculated_data_elements" );
 
281
 
 
282
        // ---------------------------------------------------------------------
 
283
        // Calculated data element export
 
284
        // ---------------------------------------------------------------------
 
285
 
 
286
        if ( calculatedDataElementIds != null && calculatedDataElementIds.size() > 0 )
 
287
        {
 
288
            count += calculatedDataElementDataMart.exportCalculatedDataElements( calculatedDataElementIds, periodIds, organisationUnitIds, dataElementInCalculatedDataElementOperands );
 
289
            
 
290
            log.info( "Exported values for calculated data elements (" + calculatedDataElementIds.size() + "): " + TimeUtils.getHMS() );
 
291
        }
 
292
 
 
293
        crossTabService.dropCrossTabTable();
 
294
        
 
295
        log.info( "Export process completed: " + TimeUtils.getHMS() );
 
296
        
 
297
        TimeUtils.stop();
 
298
 
 
299
        setMessage( "export_process_done" );
 
300
        
 
301
        aggregationCache.clearCache();
 
302
        
 
303
        return count;
 
304
    }
 
305
 
 
306
    // -------------------------------------------------------------------------
 
307
    // Supportive methods
 
308
    // -------------------------------------------------------------------------
 
309
 
 
310
    /**
 
311
     * Sorts calculated data elements from non-calculated based on the given collection
 
312
     * of identifiers and flag.
 
313
     */
 
314
    private Set<Integer> filterCalculatedDataElementIds( final Collection<Integer> dataElementIds, boolean calculated )
 
315
    {
 
316
        final Set<Integer> identifiers = new HashSet<Integer>();
 
317
        
 
318
        for ( final Integer id : dataElementIds )
 
319
        {
 
320
            final DataElement element = dataElementService.getDataElement( id );
 
321
            
 
322
            if ( ( element instanceof CalculatedDataElement ) == calculated )
 
323
            {
 
324
                identifiers.add( id );
 
325
            }
 
326
        }
 
327
        
 
328
        return identifiers;
 
329
    }
 
330
    
 
331
    /**
 
332
     * Returns all data element identifiers included in the indicators in the given 
 
333
     * identifier collection.
 
334
     */
 
335
    private Set<Integer> getDataElementIdsInIndicators( final Collection<Integer> indicatorIds )
 
336
    {
 
337
        final Set<Integer> identifiers = new HashSet<Integer>( indicatorIds.size() );
 
338
        
 
339
        for ( final Integer id : indicatorIds )
 
340
        {
 
341
            final Indicator indicator = indicatorService.getIndicator( id );
 
342
            
 
343
            identifiers.addAll( getDataElementIdsInExpression( indicator.getNumerator() ) );
 
344
            identifiers.addAll( getDataElementIdsInExpression( indicator.getDenominator() ) );            
 
345
        }
 
346
        
 
347
        return identifiers;
 
348
    }
 
349
    
 
350
    /**
 
351
     * Returns all data element identifiers included in the calculated data elements
 
352
     * in the given identifier collection.
 
353
     */
 
354
    private Set<Integer> getDataElementIdsInCalculatedDataElements( final Collection<Integer> calculatedDataElementIds )
 
355
    {
 
356
        final Set<Integer> identifiers = new HashSet<Integer>();
 
357
        
 
358
        for ( final Integer id : calculatedDataElementIds )
 
359
        {
 
360
            final DataElement element = dataElementService.getDataElement( id );
 
361
            
 
362
            if ( element instanceof CalculatedDataElement )
 
363
            {
 
364
                final CalculatedDataElement calculatedElement = (CalculatedDataElement) element;
 
365
                
 
366
                final Set<Integer> dataElementIds = getDataElementIdsInExpression( calculatedElement.getExpression().getExpression() );
 
367
                
 
368
                if ( dataElementIds != null )
 
369
                {
 
370
                    identifiers.addAll( dataElementIds );
 
371
                }
 
372
            }
 
373
        }
 
374
        
 
375
        return identifiers;
 
376
    }
 
377
        
 
378
    /**
 
379
     * Returns the idenfifiers in given collection including all of its children.
 
380
     */
 
381
    private Collection<Integer> getIdsWithChildren( final Collection<Integer> organisationUnitIds )
 
382
    {
 
383
        final OrganisationUnitHierarchy hierarchy = aggregationCache.getLatestOrganisationUnitHierarchy();
 
384
        
 
385
        final Set<Integer> identifers = new HashSet<Integer>( organisationUnitIds.size() );
 
386
        
 
387
        for ( final Integer id : organisationUnitIds )
 
388
        {
 
389
            identifers.addAll( aggregationCache.getChildren( hierarchy, id ) );
 
390
        }
 
391
        
 
392
        return identifers;
 
393
    }
 
394
    
 
395
    /**
 
396
     * Returns the identifiers of the periods in the given collection including 
 
397
     * all intersecting periods.
 
398
     */
 
399
    private Collection<Integer> getIntersectingIds( final Collection<Integer> periodIds )
 
400
    {
 
401
        final Set<Integer> identifiers = new HashSet<Integer>( periodIds.size() );
 
402
        
 
403
        for ( final Integer id : periodIds )
 
404
        {
 
405
            final Period period = periodService.getPeriod( id );
 
406
            
 
407
            final Collection<Period> periods = periodService.getIntersectingPeriods( period.getStartDate(), period.getEndDate() );
 
408
            
 
409
            identifiers.addAll( ConversionUtils.getIdentifiers( Period.class, periods ) );
 
410
        }
 
411
        
 
412
        return identifiers;
 
413
    }
 
414
    
 
415
    /**
 
416
     * Sorts out the data element identifers of the given aggregation operator and 
 
417
     * the given type.
 
418
     */
 
419
    private Collection<Operand> getOperands( final Collection<DataElement> dataElements, String aggregationOperator, String type )
 
420
    {
 
421
        final Collection<DataElement> section = new ArrayList<DataElement>();
 
422
        
 
423
        for ( final DataElement element : dataElements )
 
424
        {
 
425
            if ( element.getAggregationOperator().equals( aggregationOperator ) && element.getType().equals( type ) )
 
426
            {
 
427
                section.add( element );
 
428
            }
 
429
        }
 
430
        
 
431
        return categoryOptionComboService.getOperands( section );
 
432
    }
 
433
}