← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19808: Event analytics. Reimplemented program indicators. Evaluating per event before performing aggrega...

 

Merge authors:
  Lars Helge Øverland (larshelge)
------------------------------------------------------------
revno: 19808 [merge]
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2015-08-20 22:55:35 -0400
message:
  Event analytics. Reimplemented program indicators. Evaluating per event before performing aggregation.
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/AggregationType.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/DimensionType.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java
  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/QueryPlanner.java
  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/DefaultQueryPlanner.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/i18n_global.properties
  dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/program/hibernate/ProgramIndicator.hbm.xml
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/AddProgramIndicatorAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/UpdateProgramIndicatorAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/addProgramIndicator.vm
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/updateProgramIndicator.vm


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/AggregationType.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/AggregationType.java	2015-08-20 20:05:43 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/analytics/AggregationType.java	2015-08-21 01:47:02 +0000
@@ -43,6 +43,7 @@
     MAX( "max" ),
     NONE( "none" ),
     DEFAULT( "default" ),
+    CUSTOM( "custom" ),
 
     // Internal types
     
@@ -63,7 +64,7 @@
     {
         return value;
     }
-
+    
     public static AggregationType fromValue( String value )
     {
         for ( AggregationType type : AggregationType.values() )

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/DimensionType.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/DimensionType.java	2015-07-15 06:49:50 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/DimensionType.java	2015-08-21 01:47:02 +0000
@@ -36,6 +36,7 @@
     DATA_X,
     PROGRAM_DATAELEMENT,
     PROGRAM_ATTRIBUTE,
+    PROGRAM_INDICATOR,
     DATA_COLLAPSED,
     CATEGORY_OPTION_COMBO,
     ATTRIBUTE_OPTION_COMBO,

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java	2015-08-20 15:49:48 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java	2015-08-21 01:47:02 +0000
@@ -36,6 +36,7 @@
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 
+import org.hisp.dhis.analytics.AggregationType;
 import org.hisp.dhis.common.BaseIdentifiableObject;
 import org.hisp.dhis.common.BaseNameableObject;
 import org.hisp.dhis.common.DxfNamespaces;
@@ -87,6 +88,8 @@
     private String expression;
     
     private String filter;
+    
+    private AggregationType aggregationType;
 
     /**
      * Number of decimals to use for indicator value, null implies default.
@@ -103,7 +106,6 @@
 
     public ProgramIndicator()
     {
-
     }
 
     // -------------------------------------------------------------------------
@@ -119,6 +121,14 @@
     {
         return decimals != null && decimals >= 0;
     }
+    
+    /**
+     * Returns aggregation type, if not exists returns AVERAGE.
+     */
+    public AggregationType getAggregationTypeFallback()
+    {
+        return aggregationType != null ? aggregationType : AggregationType.AVERAGE;
+    }
 
     // -------------------------------------------------------------------------
     // Getters && Setters
@@ -180,6 +190,19 @@
     @JsonProperty
     @JsonView( { DetailedView.class, ExportView.class } )
     @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 )
+    public AggregationType getAggregationType()
+    {
+        return aggregationType;
+    }
+
+    public void setAggregationType( AggregationType aggregationType )
+    {
+        this.aggregationType = aggregationType;
+    }
+
+    @JsonProperty
+    @JsonView( { DetailedView.class, ExportView.class } )
+    @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 )
     public Integer getDecimals()
     {
         return decimals;

=== 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	2015-08-10 21:57:49 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java	2015-08-21 01:47:02 +0000
@@ -244,11 +244,6 @@
      */
     protected transient Map<OrganisationUnit, Integer> dataApprovalLevels = new HashMap<>();
     
-    /**
-     * Filter expression.
-     */
-    protected transient String filterExpression;
-    
     // -------------------------------------------------------------------------
     // Constructors
     // -------------------------------------------------------------------------
@@ -287,7 +282,6 @@
         params.dataPeriodType = this.dataPeriodType;
         params.skipPartitioning = this.skipPartitioning;
         params.dataApprovalLevels = new HashMap<>( this.dataApprovalLevels );
-        params.filterExpression = this.filterExpression;
         
         return params;
     }
@@ -1003,15 +997,7 @@
     {
         return programStage != null;
     }
-    
-    /**
-     * Indicates whether this object has a filter expression.
-     */
-    public boolean hasFilterExpression()
-    {
-        return filterExpression != null;
-    }
-    
+        
     // -------------------------------------------------------------------------
     // Static methods
     // -------------------------------------------------------------------------
@@ -1483,16 +1469,6 @@
         this.dataApprovalLevels = dataApprovalLevels;
     }
 
-    public String getFilterExpression()
-    {
-        return filterExpression;
-    }
-
-    public void setFilterExpression( String filterExpression )
-    {
-        this.filterExpression = filterExpression;
-    }
-
     // -------------------------------------------------------------------------
     // Get and set helpers for dimensions or filter
     // -------------------------------------------------------------------------

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/QueryPlanner.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/QueryPlanner.java	2015-07-10 15:56:26 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/QueryPlanner.java	2015-08-21 01:47:02 +0000
@@ -105,13 +105,6 @@
     List<DataQueryParams> groupByOrgUnitLevel( DataQueryParams params );
     
     /**
-     * If program indicators appear as dimensions; groups the given query into
-     * sub queries based on the filter of the program indicators. Sets the 
-     * program indicator filter on queries which contain program indicators.
-     */
-    List<DataQueryParams> groupByFilterExpression( DataQueryParams params );
-
-    /**
      * Groups the given query into sub queries based on its periods and which 
      * partition it should be executed against. Sets the partition table name on
      * each query. Queries are grouped based on periods if appearing as a 

=== 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	2015-08-18 11:56:04 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java	2015-08-21 01:47:02 +0000
@@ -56,6 +56,7 @@
 import static org.hisp.dhis.common.IdentifiableObjectUtils.getUids;
 import static org.hisp.dhis.common.NameableObjectUtils.asList;
 import static org.hisp.dhis.common.NameableObjectUtils.asTypedList;
+import static org.hisp.dhis.commons.collection.ListUtils.sort;
 import static org.hisp.dhis.commons.util.TextUtils.splitSafe;
 import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_LEVEL;
 import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_ORGUNIT_GROUP;
@@ -67,7 +68,6 @@
 import static org.hisp.dhis.period.PeriodType.getPeriodTypeFromIsoString;
 import static org.hisp.dhis.reporttable.ReportTable.IRT2D;
 import static org.hisp.dhis.reporttable.ReportTable.addIfEmpty;
-import static org.hisp.dhis.commons.collection.ListUtils.sort;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -101,7 +101,6 @@
 import org.hisp.dhis.common.CombinationGenerator;
 import org.hisp.dhis.common.DataDimensionItem;
 import org.hisp.dhis.common.DataDimensionItem.DataDimensionItemType;
-import org.hisp.dhis.common.comparator.IdentifiableObjectNameComparator;
 import org.hisp.dhis.common.DimensionType;
 import org.hisp.dhis.common.DimensionalObject;
 import org.hisp.dhis.common.DimensionalObjectUtils;
@@ -115,6 +114,7 @@
 import org.hisp.dhis.common.MapMap;
 import org.hisp.dhis.common.NameableObject;
 import org.hisp.dhis.common.NameableObjectUtils;
+import org.hisp.dhis.common.comparator.IdentifiableObjectNameComparator;
 import org.hisp.dhis.commons.collection.ListUtils;
 import org.hisp.dhis.commons.collection.UniqueArrayList;
 import org.hisp.dhis.commons.util.DebugUtils;
@@ -136,7 +136,6 @@
 import org.hisp.dhis.period.RelativePeriodEnum;
 import org.hisp.dhis.period.RelativePeriods;
 import org.hisp.dhis.period.comparator.AscendingPeriodEndDateComparator;
-import org.hisp.dhis.program.ProgramIndicator;
 import org.hisp.dhis.program.ProgramIndicatorService;
 import org.hisp.dhis.program.ProgramService;
 import org.hisp.dhis.program.ProgramStageService;
@@ -255,9 +254,7 @@
 
         addDataSetValues( params, grid );
         
-        addProgramIndicatorValues( params, grid );
-        
-        addProgramDataElementAttributeValues( params, grid );
+        addProgramDataElementAttributeIndicatorValues( params, grid );
 
         addDynamicDimensionValues( params, grid );
 
@@ -510,85 +507,19 @@
     }
 
     /**
-     * Adds program indicator values to the given grid based on the given data query
-     * parameters.
-     *
-     * @param params the data query parameters.
-     * @param grid the grid.
-     */
-    private void addProgramIndicatorValues( DataQueryParams params, Grid grid )
-    {
-        if ( !params.getProgramIndicators().isEmpty() && !params.isSkipData() )
-        {
-            // -----------------------------------------------------------------
-            // Run queries with different filter expression separately
-            // -----------------------------------------------------------------
-
-            List<DataQueryParams> queries = queryPlanner.groupByFilterExpression( params );
-            
-            for ( DataQueryParams query : queries )
-            {
-                DataQueryParams dataSourceParams = query.instance();
-                dataSourceParams.retainDataDimension( DataDimensionItemType.PROGRAM_INDICATOR );
-                
-                List<ProgramIndicator> indicators = asTypedList( dataSourceParams.getProgramIndicators() );
-                
-                //TODO constants
-    
-                // -----------------------------------------------------------------
-                // Get indicator values
-                // -----------------------------------------------------------------
-    
-                List<List<DimensionItem>> dimensionItemPermutations = dataSourceParams.getDimensionItemPermutations();
-                
-                Map<String, Map<String, Double>> permutationOperandValueMap = getProgramPermutationOperandValueMap( dataSourceParams );
-    
-                for ( ProgramIndicator indicator : indicators )
-                {
-                    for ( List<DimensionItem> dimensionItems : dimensionItemPermutations )
-                    {
-                        String permKey = DimensionItem.asItemKey( dimensionItems );
-                        
-                        Map<String, Double> valueMap = permutationOperandValueMap.get( permKey );
-    
-                        if ( valueMap == null )
-                        {
-                            continue;
-                        }
-    
-                        Double value = programIndicatorService.getProgramIndicatorValue( indicator, valueMap );
-                        
-                        if ( value != null )
-                        {
-                            List<DimensionItem> row = new ArrayList<>( dimensionItems );
-    
-                            row.add( DX_INDEX, new DimensionItem( DATA_X_DIM_ID, indicator ) );
-                            
-                            Double roundedValue = indicator.hasDecimals() ? MathUtils.getRounded( value, indicator.getDecimals() ) : MathUtils.getRounded( value );
-                            
-                            grid.addRow();
-                            grid.addValues( DimensionItem.getItemIdentifiers( row ) );
-                            grid.addValue( dataSourceParams.isSkipRounding() ? value : roundedValue );
-                        }
-                    }
-                }
-            }
-        }
-    }
-    
-    /**
      * Adds program data element values to the given grid based on the given data
      * query parameters.
      * 
      * @param params the data query parameters.
      * @param grid the grid.
      */
-    private void addProgramDataElementAttributeValues( DataQueryParams params, Grid grid )
+    private void addProgramDataElementAttributeIndicatorValues( DataQueryParams params, Grid grid )
     {
-        if ( !params.getAllProgramDataElementsAndAttributes().isEmpty() && !params.isSkipData() )
+        if ( ( !params.getAllProgramDataElementsAndAttributes().isEmpty() || !params.getProgramIndicators().isEmpty() ) && !params.isSkipData() )
         {
             DataQueryParams dataSourceParams = params.instance();
-            dataSourceParams.retainDataDimensions( DataDimensionItemType.PROGRAM_DATA_ELEMENT, DataDimensionItemType.PROGRAM_ATTRIBUTE );
+            dataSourceParams.retainDataDimensions( DataDimensionItemType.PROGRAM_DATA_ELEMENT, 
+                DataDimensionItemType.PROGRAM_ATTRIBUTE, DataDimensionItemType.PROGRAM_INDICATOR );
             
             EventQueryParams eventQueryParams = EventQueryParams.fromDataQueryParams( dataSourceParams );
             
@@ -1376,41 +1307,6 @@
     }
     
     /**
-     * Returns a mapping of permutation keys and mappings of program data elements
-     * / program attributes and values based on the given query.
-     * 
-     * @param params the data query parameters.
-     */
-    private Map<String, Map<String, Double>> getProgramPermutationOperandValueMap( DataQueryParams params )
-    {        
-        Map<String, Map<String, Double>> permutationMap = new HashMap<>();
-             
-        List<ProgramIndicator> programIndicators = asTypedList( params.getProgramIndicators() );
-        List<NameableObject> dataElements = asList( programIndicatorService.getDataElementsInIndicators( programIndicators ) );
-        List<NameableObject> attributes = asList( programIndicatorService.getAttributesInIndicators( programIndicators ) );
-        List<NameableObject> dataItems = ListUtils.union( dataElements, attributes );
-        
-        if ( !dataElements.isEmpty() || !attributes.isEmpty() )
-        {
-            DataQueryParams dataSourceParams = params.instance().removeDimension( DATA_X_DIM_ID );
-            
-            dataSourceParams.getDimensions().add( DX_INDEX, new BaseDimensionalObject( 
-                DATA_X_DIM_ID, DimensionType.DATA_X, dataItems ) );
-            
-            EventQueryParams eventQueryParams = EventQueryParams.fromDataQueryParams( dataSourceParams );
-            eventQueryParams.setSkipRounding( true );
-            
-            Grid grid = eventAnalyticsService.getAggregatedEventData( eventQueryParams );
-            
-            Map<String, Double> valueMap = grid.getAsMap( grid.getWidth() - 1, DIMENSION_SEP );
-            
-            permutationMap.putAll( DataQueryParams.getPermutationProgramValueMap( valueMap ) );
-        }
-        
-        return permutationMap;
-    }
-    
-    /**
      * Returns a mapping of dimension keys and aggregated values for the data
      * element totals part of the indicators in the given query.
      *

=== 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	2015-07-10 21:29:06 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java	2015-08-21 01:47:02 +0000
@@ -72,7 +72,6 @@
 import org.hisp.dhis.organisationunit.OrganisationUnitService;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
-import org.hisp.dhis.program.ProgramIndicator;
 import org.hisp.dhis.program.ProgramIndicatorService;
 import org.hisp.dhis.setting.SystemSettingManager;
 import org.hisp.dhis.system.filter.AggregatableDataElementFilter;
@@ -539,37 +538,6 @@
         
         return queries;
     }
-
-    @Override
-    public List<DataQueryParams> groupByFilterExpression( DataQueryParams params )
-    {
-        List<DataQueryParams> queries = new ArrayList<>();
-        
-        if ( !params.getProgramIndicators().isEmpty() )
-        {
-            ListMap<String, NameableObject> filterProgramIndicatorMap = getFilterProgramIndicatorMap( params.getProgramIndicators() );
-            
-            for ( String filter : filterProgramIndicatorMap.keySet() )
-            {
-                DataQueryParams query = params.instance();
-                query.setProgramIndicators( filterProgramIndicatorMap.get( filter ) );
-                query.setFilterExpression( programIndicatorService.getAnalyticsSQl( filter ) );
-                queries.add( query );
-            }
-        }
-        else
-        {
-            queries.add( params.instance() );
-            return queries;
-        }
-
-        if ( queries.size() > 1 )
-        {
-            log.debug( "Split on filter expression: " + queries.size() );
-        }
-        
-        return queries;
-    }
     
     private List<DataQueryParams> groupByDataType( DataQueryParams params )
     {
@@ -776,26 +744,6 @@
     }
     
     /**
-     * Creates a mapping between filter and program indicator for the given
-     * program indicators.
-     */
-    private ListMap<String, NameableObject> getFilterProgramIndicatorMap( List<NameableObject> programIndicators )
-    {
-        ListMap<String, NameableObject> map = new ListMap<>();
-        
-        for ( NameableObject programIndicator : programIndicators )
-        {
-            ProgramIndicator indicator = (ProgramIndicator) programIndicator;
-            
-            String filter = indicator.getFilter();
-            
-            map.putValue( filter, indicator );
-        }
-        
-        return map;
-    }
-    
-    /**
      * Creates a mapping between data type and data element for the given data elements.
      */
     private ListMap<DataType, NameableObject> getDataTypeDataElementMap( List<NameableObject> dataElements )

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java	2015-08-20 20:05:43 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java	2015-08-21 02:55:35 +0000
@@ -28,8 +28,8 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import static org.hisp.dhis.common.DimensionalObject.PERIOD_DIM_ID;
 import static org.hisp.dhis.common.DimensionalObject.DATA_X_DIM_ID;
-import static org.hisp.dhis.common.DimensionalObject.PERIOD_DIM_ID;
 
 import java.util.ArrayList;
 import java.util.Date;
@@ -52,6 +52,7 @@
 import org.hisp.dhis.option.OptionSet;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
 import org.hisp.dhis.period.Period;
+import org.hisp.dhis.program.ProgramIndicator;
 import org.hisp.dhis.trackedentity.TrackedEntityAttribute;
 
 /**
@@ -67,8 +68,12 @@
     private List<QueryItem> items = new ArrayList<>();
     
     private List<QueryItem> itemFilters = new ArrayList<>();
-    
+        
     private DimensionalObject value;
+        
+    private List<ProgramIndicator> itemProgramIndicators = new ArrayList<>();
+
+    private ProgramIndicator programIndicator;
     
     private List<String> asc = new ArrayList<>();
     
@@ -113,7 +118,6 @@
 
         params.partitions = new Partitions( this.partitions );
         params.periodType = this.periodType;
-        params.filterExpression = this.filterExpression;
         
         params.program = this.program;
         params.programStage = this.programStage;
@@ -122,6 +126,8 @@
         params.items = new ArrayList<>( this.items );
         params.itemFilters = new ArrayList<>( this.itemFilters );
         params.value = this.value;
+        params.itemProgramIndicators = new ArrayList<>( this.itemProgramIndicators );
+        params.programIndicator = this.programIndicator;
         params.asc = new ArrayList<>( this.asc );
         params.desc = new ArrayList<>( this.desc );
         params.organisationUnitMode = this.organisationUnitMode;
@@ -159,7 +165,7 @@
             TrackedEntityAttribute element = (TrackedEntityAttribute) object;            
             QueryItem item = new QueryItem( element, element.getLegendSet(), element.getValueType(), element.getAggregationType(), element.getOptionSet() );
             params.getItems().add( item );
-        }
+        }        
 
         for ( NameableObject object : dataQueryParams.getFilterProgramDataElements() )
         {
@@ -175,6 +181,12 @@
             params.getItemFilters().add( item );
         }
 
+        for ( NameableObject object : dataQueryParams.getProgramIndicators() )
+        {
+            ProgramIndicator programIndicator = (ProgramIndicator) object;
+            params.getItemProgramIndicators().add( programIndicator );
+        }
+        
         params.setAggregateData( true );
         params.removeDimension( DATA_X_DIM_ID );
         
@@ -311,6 +323,7 @@
     public boolean isAggregationType( AggregationType aggregationType )
     {
         AggregationType type = getAggregationTypeFallback();
+        
         return type != null && type.equals( aggregationType );
     }    
     
@@ -390,7 +403,12 @@
     {
         return value != null;
     }
-        
+    
+    public boolean hasProgramIndicatorDimension()
+    {
+        return programIndicator != null;
+    }
+    
     /**
      * Indicates whether the program of this query requires registration of
      * tracked entity instances.
@@ -419,6 +437,8 @@
             "Items: " + items + ", " +
             "Item filters: " + itemFilters + ", " +
             "Value: " + value + ", " +
+            "Item program indicators: " + itemProgramIndicators + ", " +
+            "Program indicator: " + programIndicator + ", " +
             "Aggregation type: " + aggregationType + ", " +
             "Dimensions: " + dimensions + ", " +
             "Filters: " + filters + "]";
@@ -478,6 +498,26 @@
         this.value = value;
     }
 
+    public List<ProgramIndicator> getItemProgramIndicators()
+    {
+        return itemProgramIndicators;
+    }
+
+    public void setItemProgramIndicators( List<ProgramIndicator> itemProgramIndicators )
+    {
+        this.itemProgramIndicators = itemProgramIndicators;
+    }
+
+    public ProgramIndicator getProgramIndicator()
+    {
+        return programIndicator;
+    }
+
+    public void setProgramIndicator( ProgramIndicator programIndicator )
+    {
+        this.programIndicator = programIndicator;
+    }
+
     public List<String> getAsc()
     {
         return asc;

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java	2015-08-07 17:59:15 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java	2015-08-21 02:55:35 +0000
@@ -51,6 +51,7 @@
 import org.hisp.dhis.organisationunit.OrganisationUnit;
 import org.hisp.dhis.organisationunit.OrganisationUnitService;
 import org.hisp.dhis.period.Period;
+import org.hisp.dhis.program.ProgramIndicator;
 import org.hisp.dhis.setting.SystemSettingManager;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -266,9 +267,19 @@
             {
                 EventQueryParams query = params.instance();
                 query.getItems().clear();
+                query.getItemProgramIndicators().clear();
                 query.setValue( item.getItem() );
                 queries.add( query );
             }
+            
+            for ( ProgramIndicator programIndicator : params.getItemProgramIndicators() )
+            {
+                EventQueryParams query = params.instance();
+                query.getItems().clear();
+                query.getItemProgramIndicators().clear();
+                query.setProgramIndicator( programIndicator );
+                queries.add( query );
+            }
         }
         else if ( params.isCollapseDataDimensions() && !params.getItems().isEmpty() )
         {

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java	2015-08-20 20:05:43 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java	2015-08-21 02:55:35 +0000
@@ -56,8 +56,8 @@
 import org.hisp.dhis.legend.Legend;
 import org.hisp.dhis.option.Option;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
+import org.hisp.dhis.program.ProgramIndicatorService;
 import org.hisp.dhis.system.util.MathUtils;
-import org.hisp.dhis.system.util.ValidationUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.BadSqlGrammarException;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -85,6 +85,9 @@
     @Autowired
     private StatementBuilder statementBuilder;
     
+    @Autowired
+    private ProgramIndicatorService programIndicatorService;
+    
     // -------------------------------------------------------------------------
     // EventAnalyticsManager implementation
     // -------------------------------------------------------------------------
@@ -158,7 +161,14 @@
 
             if ( params.isAggregateData() )
             {
-                grid.addValue( params.getValue().getUid() );
+                if ( params.hasValueDimension() )
+                {
+                    grid.addValue( params.getValue().getUid() );
+                }
+                else if ( params.hasProgramIndicatorDimension() )
+                {
+                    grid.addValue( params.getProgramIndicator().getUid() );
+                }                
             }
             else
             {
@@ -176,7 +186,7 @@
                 grid.addValue( dimensionValue );
             }
             
-            if ( params.hasValueDimension() )
+            if ( params.hasValueDimension() || params.hasProgramIndicatorDimension() )
             {
                 double value = rowSet.getDouble( "value" );
                 grid.addValue( params.isSkipRounding() ? value : MathUtils.getRounded( value ) );
@@ -316,11 +326,21 @@
         
         if ( params.hasValueDimension() ) // && isNumeric
         {
-            String column = statementBuilder.columnQuote( params.getValue().getUid() );
-            
             String function = params.getAggregationTypeFallback().getValue();
             
-            return function + "(" + column + ")";
+            String expression = statementBuilder.columnQuote( params.getValue().getUid() );
+            
+            return function + "(" + expression + ")";
+        }
+        else if ( params.hasProgramIndicatorDimension() )
+        {
+            String function = params.getProgramIndicator().getAggregationTypeFallback().getValue();
+            
+            String expression = programIndicatorService.getAnalyticsSQl( params.getProgramIndicator().getExpression() );
+            
+            return function + "(" + expression + ")";
+            
+            //TODO check if expression is valid and safe SQL
         }
         else
         {
@@ -483,9 +503,11 @@
         // Filter expression
         // ---------------------------------------------------------------------
 
-        if ( params.hasFilterExpression() && ValidationUtils.expressionIsValidSQl( params.getFilterExpression() ) )
+        if ( params.hasProgramIndicatorDimension() && params.getProgramIndicator().hasFilter() )
         {
-            String sqlFilter = ExpressionUtils.asSql( params.getFilterExpression() );
+            String filter = programIndicatorService.getAnalyticsSQl( params.getProgramIndicator().getFilter() );
+            
+            String sqlFilter = ExpressionUtils.asSql( filter );
             
             sql += "and (" + sqlFilter + ") ";
         }

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/i18n_global.properties'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/i18n_global.properties	2015-08-18 20:08:20 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/i18n_global.properties	2015-08-21 02:55:35 +0000
@@ -557,6 +557,7 @@
 bool=Yes/No
 none=None
 yes_only = Yes Only
+custom=Custom
 
 #-- User account --------------------------------------------------------------#
 

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/program/hibernate/ProgramIndicator.hbm.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/program/hibernate/ProgramIndicator.hbm.xml	2015-08-07 15:23:36 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/program/hibernate/ProgramIndicator.hbm.xml	2015-08-21 02:55:35 +0000
@@ -28,6 +28,13 @@
 
     <property name="filter" type="text" />
 
+    <property name="aggregationType" length="40">
+		<type name="org.hibernate.type.EnumType">
+			<param name="enumClass">org.hisp.dhis.analytics.AggregationType</param>
+			<param name="type">12</param>
+		</type> 
+	</property>
+    
     <property name="decimals" />
 
     <property name="displayInForm" />

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/AddProgramIndicatorAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/AddProgramIndicatorAction.java	2015-08-07 15:23:36 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/AddProgramIndicatorAction.java	2015-08-21 02:55:35 +0000
@@ -32,6 +32,7 @@
 import java.util.regex.Pattern;
 
 import org.apache.commons.lang3.StringUtils;
+import org.hisp.dhis.analytics.AggregationType;
 import org.hisp.dhis.program.Program;
 import org.hisp.dhis.program.ProgramIndicator;
 import org.hisp.dhis.program.ProgramIndicatorService;
@@ -128,7 +129,14 @@
     {
         this.filter = filter;
     }
+
+    private String aggregationType;
     
+    public void setAggregationType( String aggregationType )
+    {
+        this.aggregationType = aggregationType;
+    }
+
     private Integer decimals;
 
     public void setDecimals( Integer decimals )
@@ -183,6 +191,7 @@
         indicator.setValueType( StringUtils.trimToNull( valueType ) );
         indicator.setExpression( StringUtils.trimToNull( expression ) );
         indicator.setFilter( StringUtils.trimToNull( filter ) );
+        indicator.setAggregationType( AggregationType.valueOf( aggregationType ) );
         indicator.setDecimals( decimals );
         indicator.setDisplayInForm( displayInForm );
         indicator.setRootDate( StringUtils.trimToNull( rootDate ) );

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/UpdateProgramIndicatorAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/UpdateProgramIndicatorAction.java	2015-08-07 15:23:36 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/java/org/hisp/dhis/trackedentity/action/programindicator/UpdateProgramIndicatorAction.java	2015-08-21 02:55:35 +0000
@@ -32,6 +32,7 @@
 import java.util.regex.Pattern;
 
 import org.apache.commons.lang.StringUtils;
+import org.hisp.dhis.analytics.AggregationType;
 import org.hisp.dhis.program.ProgramIndicator;
 import org.hisp.dhis.program.ProgramIndicatorService;
 
@@ -115,6 +116,13 @@
         this.filter = filter;
     }
 
+    private String aggregationType;
+    
+    public void setAggregationType( String aggregationType )
+    {
+        this.aggregationType = aggregationType;
+    }
+
     private Integer decimals;
 
     public void setDecimals( Integer decimals )
@@ -174,6 +182,7 @@
         indicator.setValueType( StringUtils.trimToNull( valueType ) );
         indicator.setExpression( StringUtils.trimToNull( expression ) );
         indicator.setFilter( StringUtils.trimToNull( filter ) );
+        indicator.setAggregationType( AggregationType.valueOf( aggregationType ) );
         indicator.setDecimals( decimals );
         indicator.setDisplayInForm( displayInForm );
         indicator.setRootDate( StringUtils.trimToNull( rootDate ) );

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/addProgramIndicator.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/addProgramIndicator.vm	2015-08-07 15:23:36 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/addProgramIndicator.vm	2015-08-21 02:55:35 +0000
@@ -63,6 +63,21 @@
 			<td></td>
 		</tr>
 		<tr>
+	        <td><label for="aggregationType">$i18n.getString( "aggregation_type" )</label></td>
+	        <td>
+	            <select id="aggregationType" name="aggregationType">
+	                <option value="AVERAGE">$i18n.getString( "average" )</option>
+	                <option value="SUM">$i18n.getString( "sum" )</option>
+	                <option value="COUNT">$i18n.getString( "count" )</option>
+	                <option value="STDDEV">$i18n.getString( "stddev" )</option>
+	                <option value="VARIANCE">$i18n.getString( "variance" )</option>
+	                <option value="MIN">$i18n.getString( "min" )</option>
+	                <option value="MAX">$i18n.getString( "max" )</option>
+	                <option value="MAX">$i18n.getString( "custom" )</option>
+	            </select>
+	        </td>
+	    </tr>
+		<tr>
 			<td><label for="decimals">$i18n.getString( "decimals_in_data_output" )</label></td>
 			<td>
 				<select id="decimals" name="decimals">

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/updateProgramIndicator.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/updateProgramIndicator.vm	2015-08-07 15:23:36 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/updateProgramIndicator.vm	2015-08-21 02:55:35 +0000
@@ -63,6 +63,20 @@
 		<td></td>
 	</tr>
 	<tr>
+        <td><label for="aggregationType">$i18n.getString( "aggregation_type" )</label></td>
+        <td>
+            <select id="aggregationType" name="aggregationType">
+                <option value="AVERAGE"#if( $programIndicator.aggregationType == 'AVERAGE' ) selected="selected"#end>$i18n.getString( "average" )</option>
+                <option value="SUM"#if( $programIndicator.aggregationType == 'SUM' ) selected="selected"#end>$i18n.getString( "sum" )</option>
+                <option value="COUNT"#if( $programIndicator.aggregationType == 'COUNT' ) selected="selected"#end>$i18n.getString( "count" )</option>
+                <option value="STDDEV"#if( $programIndicator.aggregationType == 'STDDEV' ) selected="selected"#end>$i18n.getString( "stddev" )</option>
+                <option value="VARIANCE"#if( $programIndicator.aggregationType == 'VARIANCE' ) selected="selected"#end>$i18n.getString( "variance" )</option>
+                <option value="MIN"#if( $programIndicator.aggregationType == 'MIN' ) selected="selected"#end>$i18n.getString( "min" )</option>
+                <option value="MAX"#if( $programIndicator.aggregationType == 'MAX' ) selected="selected"#end>$i18n.getString( "max" )</option>
+            </select>
+        </td>
+    </tr>
+	<tr>
 		<td><label for="decimals">$i18n.getString( "decimals_in_data_output" )</label></td>
 		<td>
 			<select id="decimals" name="decimals">