=== added file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AggregationType.java' --- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AggregationType.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AggregationType.java 2012-12-27 19:15:15 +0000 @@ -0,0 +1,33 @@ +package org.hisp.dhis.analytics; + +/* + * Copyright (c) 2004-2012, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +public enum AggregationType +{ + SUM, AVERAGE_AGGREGATION, AVERAGE_DISAGGREGATION +} === modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java' --- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java 2012-12-21 16:50:36 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java 2012-12-27 19:15:15 +0000 @@ -67,6 +67,8 @@ private transient int organisationUnitLevel; + private AggregationType aggregationType; + // ------------------------------------------------------------------------- // Constructors // ------------------------------------------------------------------------- @@ -91,6 +93,7 @@ this.tableName = params.getTableName(); this.periodType = params.getPeriodType(); this.organisationUnitLevel = params.getOrganisationUnitLevel(); + this.aggregationType = params.getAggregationType(); } // ------------------------------------------------------------------------- @@ -163,6 +166,14 @@ return dimensions.containsKey( PERIOD_DIM_ID ) || filters.containsKey( PERIOD_DIM_ID ); } + /** + * Indicates whether this object is of the given aggregation type. + */ + public boolean isAggregationType( AggregationType aggregationType ) + { + return this.aggregationType != null && this.aggregationType.equals( aggregationType ); + } + @Override public int hashCode() { @@ -390,5 +401,15 @@ public void setFilterOrganisationUnits( List organisationUnits ) { filters.put( ORGUNIT_DIM_ID, organisationUnits ); - } + } + + public AggregationType getAggregationType() + { + return aggregationType; + } + + public void setAggregationType( AggregationType aggregationType ) + { + this.aggregationType = aggregationType; + } } === modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java' --- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java 2012-12-22 12:26:34 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java 2012-12-27 19:15:15 +0000 @@ -50,14 +50,9 @@ { private static final String VALUE_HEADER_NAME = "Value"; - //TODO aggregation levels - // add org unit level as column in tables + //TODO average operator aggregation //TODO indicator aggregation //TODO category sub-totals and totals - //TODO use data mart when query can be satisfied - //TODO create data mart for average, less-than yearly data elements - // aggregate in time dimension only - // insert into standard analytics table? @Autowired private AnalyticsManager analyticsManager; === modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java' --- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java 2012-12-21 16:50:36 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java 2012-12-27 19:15:15 +0000 @@ -27,13 +27,19 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import static org.hisp.dhis.dataelement.DataElement.AGGREGATION_OPERATOR_AVERAGE; +import static org.hisp.dhis.dataelement.DataElement.AGGREGATION_OPERATOR_SUM; + import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.hisp.dhis.analytics.AggregationType; import org.hisp.dhis.analytics.DataQueryParams; import org.hisp.dhis.analytics.QueryPlanner; import org.hisp.dhis.analytics.table.PartitionUtils; +import org.hisp.dhis.dataelement.DataElement; +import org.hisp.dhis.dataelement.DataElementService; import org.hisp.dhis.organisationunit.OrganisationUnitService; import org.hisp.dhis.period.PeriodType; import org.hisp.dhis.system.util.ListMap; @@ -50,6 +56,9 @@ @Autowired private OrganisationUnitService organisationUnitService; + @Autowired + private DataElementService dataElementService; + // ------------------------------------------------------------------------- // DefaultQueryPlanner implementation // ------------------------------------------------------------------------- @@ -72,18 +81,26 @@ for ( DataQueryParams byPartition : groupedByPartition ) { - List groupedByPeriodType = groupByPeriodType( byPartition ); + List groupedByOrgUnitLevel = groupByOrgUnitLevel( byPartition ); - for ( DataQueryParams byPeriodType : groupedByPeriodType ) + for ( DataQueryParams byOrgUnitLevel : groupedByOrgUnitLevel ) { - List groupedByOrgUnitLevel = groupByOrgUnitLevel( byPeriodType ); + List groupedByPeriodType = groupByPeriodType( byOrgUnitLevel ); - for ( DataQueryParams byOrgUnitLevel : groupedByOrgUnitLevel ) + for ( DataQueryParams byPeriodType : groupedByPeriodType ) { - byOrgUnitLevel.setTableName( byPartition.getTableName() ); - byOrgUnitLevel.setPeriodType( byPeriodType.getPeriodType() ); + List groupedByAggregationType = groupByAggregationType( byPeriodType ); - queries.add( byOrgUnitLevel ); + for ( DataQueryParams byAggregationType : groupedByAggregationType ) + { + byAggregationType.setTableName( byPartition.getTableName() ); + byAggregationType.setOrganisationUnitLevel( byOrgUnitLevel.getOrganisationUnitLevel() ); + byAggregationType.setPeriodType( byPeriodType.getPeriodType() ); + + //TODO split on data element period type for average disaggregation + + queries.add( byAggregationType ); + } } } } @@ -250,6 +267,31 @@ return queries; } + private List groupByAggregationType( DataQueryParams params ) + { + List queries = new ArrayList(); + + if ( params.getDatElements() == null || params.getDatElements().isEmpty() ) + { + queries.add( new DataQueryParams( params ) ); + return queries; + } + + PeriodType periodType = PeriodType.getPeriodTypeByName( params.getPeriodType() ); + + ListMap aggregationTypeDataElementMap = getAggregationTypeDataElementMap( params.getDatElements(), periodType ); + + for ( AggregationType aggregationType : aggregationTypeDataElementMap.keySet() ) + { + DataQueryParams query = new DataQueryParams( params ); + query.setDataElements( aggregationTypeDataElementMap.get( aggregationType ) ); + query.setAggregationType( aggregationType ); + queries.add( query ); + } + + return queries; + } + /** * Replaces the period filter with individual filters for each period type. */ @@ -294,7 +336,7 @@ for ( String period : isoPeriods ) { - String periodTypeName = PeriodType.getPeriodTypeFromIsoString( period ).getName().toLowerCase(); + String periodTypeName = PeriodType.getPeriodTypeFromIsoString( period ).getName(); map.putValue( periodTypeName, period ); } @@ -337,4 +379,38 @@ return map; } + + /** + * Creates a mapping between the aggregation type and data element for the + * given data elements and period type. + */ + private ListMap getAggregationTypeDataElementMap( Collection dataElements, PeriodType aggregationPeriodType ) + { + ListMap map = new ListMap(); + + for ( String element : dataElements ) + { + DataElement dataElement = dataElementService.getDataElement( element ); + + if ( AGGREGATION_OPERATOR_SUM.equals( dataElement.getAggregationOperator() ) ) + { + map.putValue( AggregationType.SUM, element ); + } + else if ( AGGREGATION_OPERATOR_AVERAGE.equals( dataElement.getAggregationOperator() ) ) + { + PeriodType dataPeriodType = dataElement.getPeriodType(); + + if ( dataPeriodType == null || aggregationPeriodType.getFrequencyOrder() >= dataPeriodType.getFrequencyOrder() ) + { + map.putValue( AggregationType.AVERAGE_AGGREGATION, element ); + } + else + { + map.putValue( AggregationType.AVERAGE_DISAGGREGATION, element ); + } + } + } + + return map; + } } === modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java' --- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java 2012-12-21 16:50:36 +0000 +++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/JdbcAnalyticsManager.java 2012-12-27 19:15:15 +0000 @@ -30,6 +30,7 @@ import static org.hisp.dhis.analytics.DataQueryParams.VALUE_ID; import static org.hisp.dhis.system.util.TextUtils.getCommaDelimitedString; import static org.hisp.dhis.system.util.TextUtils.getQuotedCommaDelimitedString; +import static org.hisp.dhis.analytics.AggregationType.*; import java.util.HashMap; import java.util.List; @@ -40,6 +41,7 @@ import org.apache.commons.logging.LogFactory; import org.hisp.dhis.analytics.AnalyticsManager; import org.hisp.dhis.analytics.DataQueryParams; +import org.hisp.dhis.period.PeriodType; import org.hisp.dhis.system.util.SqlHelper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; @@ -76,9 +78,13 @@ SqlHelper sqlHelper = new SqlHelper(); - String sql = - "select " + getCommaDelimitedString( dimensions ) + ", sum(value) as value " + - "from " + params.getTableName() + " "; + String sql = "select " + getCommaDelimitedString( dimensions ) + ", "; + + int days = PeriodType.getPeriodTypeByName( params.getPeriodType() ).getFrequencyOrder(); + + sql += params.isAggregationType( AVERAGE_AGGREGATION ) ? "sum(daysxvalue) / " + days : "sum(value)"; + + sql += " as value from " + params.getTableName() + " "; for ( String dim : dimensions ) {