← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 7966: Data mart, crosstabbing on org units rather than data element operands

 

Merge authors:
  Lars Helge Øverland (larshelge)
------------------------------------------------------------
revno: 7966 [merge]
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Sat 2012-08-25 13:49:05 +0200
message:
  Data mart, crosstabbing on org units rather than data element operands
added:
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/OrgUnitOperand.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementOperand.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/datamart/CrossTabDataValue.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/DataMartManager.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/JdbcDataMartManager.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/cache/MemoryAggregationCache.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageBoolAggregator.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntAggregator.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntSingleValueAggregator.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/DataElementAggregator.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumBoolAggregator.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumIntAggregator.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/CrossTabService.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/DefaultCrossTabService.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/CrossTabStore.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/JDBCCrossTabStore.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/dataelement/DefaultDataElementDataMart.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/engine/DefaultDataMartEngine.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceOrgUnitTest.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceTest.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabServiceTest.java
  dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabStoreTest.java
  dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java


--
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/dataelement/DataElementOperand.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementOperand.java	2012-06-30 19:25:09 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementOperand.java	2012-08-25 11:47:17 +0000
@@ -98,6 +98,8 @@
 
     private String operandType;
 
+    private boolean hasAggregationLevels;
+    
     // -------------------------------------------------------------------------
     // Constructors
     // -------------------------------------------------------------------------
@@ -145,6 +147,14 @@
     // -------------------------------------------------------------------------
 
     /**
+     * Tests whether the operand has any aggregation levels.
+     */
+    public boolean hasAggregationLevels()
+    {
+        return aggregationLevels.size() > 0;
+    }
+    
+    /**
      * Tests whether the hierarchy level of the OrganisationUnit associated with
      * the relevant DataValue is equal to or higher than the relevant
      * aggregation level. Returns true if no aggregation levels exist.
@@ -156,7 +166,7 @@
      */
     public boolean aggregationLevelIsValid( int organisationUnitLevel, int dataValueLevel )
     {
-        if ( aggregationLevels == null || aggregationLevels.size() == 0 )
+        if ( aggregationLevels.size() == 0 )
         {
             return true;
         }
@@ -456,6 +466,16 @@
         this.operandType = operandType;
     }
 
+    public boolean isHasAggregationLevels()
+    {
+        return hasAggregationLevels;
+    }
+
+    public void setHasAggregationLevels( boolean hasAggregationLevels )
+    {
+        this.hasAggregationLevels = hasAggregationLevels;
+    }
+
     // -------------------------------------------------------------------------
     // hashCode, equals, toString, compareTo
     // -------------------------------------------------------------------------

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/datamart/CrossTabDataValue.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/datamart/CrossTabDataValue.java	2011-12-26 10:07:59 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/datamart/CrossTabDataValue.java	2012-08-25 11:47:17 +0000
@@ -30,22 +30,17 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.hisp.dhis.dataelement.DataElementOperand;
-
 /**
  * @author Lars Helge Overland
- * @version $Id: CrossTabDataValue.java 5514 2008-08-04 10:48:07Z larshelg $
  */
 public class CrossTabDataValue
 {
     private int periodId;
     
-    private int sourceId;
-    
     /**
-     * Contains Operand (data element id and category option combo id) and data value.
+     * Contains org unit identifier mapped to data value.
      */
-    private Map<DataElementOperand, String> valueMap = new HashMap<DataElementOperand, String>();
+    private Map<Integer, String> valueMap = new HashMap<Integer, String>();
 
     // -------------------------------------------------------------------------
     // Constructors
@@ -55,13 +50,6 @@
     {   
     }
     
-    public CrossTabDataValue( int periodId, int sourceId, Map<DataElementOperand, String> valueMap )
-    {
-        this.periodId = periodId;
-        this.sourceId = sourceId;
-        this.valueMap = valueMap;
-    }
-
     // -------------------------------------------------------------------------
     // Getters & setters
     // -------------------------------------------------------------------------
@@ -76,22 +64,12 @@
         this.periodId = periodId;
     }
 
-    public int getSourceId()
-    {
-        return sourceId;
-    }
-
-    public void setSourceId( int sourceId )
-    {
-        this.sourceId = sourceId;
-    }
-
-    public Map<DataElementOperand, String> getValueMap()
+    public Map<Integer, String> getValueMap()
     {
         return valueMap;
     }
 
-    public void setValueMap( Map<DataElementOperand, String> valueMap )
+    public void setValueMap( Map<Integer, String> valueMap )
     {
         this.valueMap = valueMap;
     }
@@ -106,9 +84,8 @@
         final int PRIME = 31;
         
         int result = 1;
-        
+
         result = PRIME * result + periodId;
-        result = PRIME * result + sourceId;
         
         return result;
     }
@@ -133,14 +110,12 @@
         
         final CrossTabDataValue other = (CrossTabDataValue) object;
         
-        return periodId == other.periodId && sourceId == other.sourceId;
+        return periodId == other.periodId;
     }
     
     @Override
     public String toString()
     {
-        String toString = "[period id: " + periodId + ", source id: " + sourceId + "]";
-        
-        return toString;
+        return "[" + periodId + "]";
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/DataMartManager.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/DataMartManager.java	2012-08-22 16:13:29 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/DataMartManager.java	2012-08-25 11:47:17 +0000
@@ -1,11 +1,41 @@
 package org.hisp.dhis.datamart;
 
+/*
+ * 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.
+ */
+
 import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 
 import org.hisp.dhis.dataelement.DataElementOperand;
 
+/**
+ * @author Lars Helge Overland
+ */
 public interface DataMartManager
 {
     /**
@@ -17,7 +47,14 @@
      */
     Set<DataElementOperand> getOperandsWithData( Set<DataElementOperand> operands );
 
-    Map<DataElementOperand, String> getDataValueMap( int periodId, int sourceId );
+    /**
+     * Returns a map of organisation unit identifiers and data values.
+     * 
+     * @param operand the data element operand.
+     * @param periodId the period identifier.
+     * @return a map of organisation unit identifiers and data values.
+     */
+    Map<Integer, String> getDataValueMap( DataElementOperand operand, int periodId );
     
     Map<DataElementOperand, String> getAggregatedValueMap( int periodId, int organisationUnitId );
     

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/JdbcDataMartManager.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/JdbcDataMartManager.java	2012-08-22 16:13:29 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/JdbcDataMartManager.java	2012-08-23 08:42:48 +0000
@@ -100,27 +100,26 @@
     }
 
     @Override
-    public Map<DataElementOperand, String> getDataValueMap( int periodId, int sourceId )
+    public Map<Integer, String> getDataValueMap( DataElementOperand operand, int periodId )
     {
         final StatementHolder holder = statementManager.getHolder();
             
         try
         {
             final String sql =
-                "SELECT dataelementid, categoryoptioncomboid, value " +
+                "SELECT sourceid, value " +
                 "FROM datavalue " +
-                "WHERE periodid = " + periodId + " " +
-                "AND sourceid = " + sourceId;
+                "WHERE dataelementid = " + operand.getDataElementId() + " " +
+                "AND categoryoptioncomboid = " + operand.getOptionComboId() + " " +
+                "AND periodid = " + periodId;
             
             final ResultSet resultSet = holder.getStatement().executeQuery( sql );
             
-            final Map<DataElementOperand, String> map = new HashMap<DataElementOperand, String>();
+            final Map<Integer, String> map = new HashMap<Integer, String>();
             
             while ( resultSet.next() )
             {
-                final DataElementOperand operand = new DataElementOperand( resultSet.getInt( 1 ), resultSet.getInt( 2 ) );
-                
-                map.put( operand, resultSet.getString( 3 ) );
+                map.put( resultSet.getInt( 1 ), resultSet.getString( 2 ) );
             }
             
             return map;

=== added file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/OrgUnitOperand.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/OrgUnitOperand.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/OrgUnitOperand.java	2012-08-25 11:49:05 +0000
@@ -0,0 +1,69 @@
+package org.hisp.dhis.datamart;
+
+public class OrgUnitOperand
+{
+    private int orgUnitId;
+    
+    private int orgUnitGroupId;
+    
+    private double value;
+    
+    public OrgUnitOperand()
+    {
+    }
+    
+    public OrgUnitOperand( int orgUnitId, int orgUnitGroupId, double value )
+    {
+        this.orgUnitId = orgUnitId;
+        this.orgUnitGroupId = orgUnitGroupId;
+        this.value = value;
+    }
+
+    public int getOrgUnitId()
+    {
+        return orgUnitId;
+    }
+
+    public int getOrgUnitGroupId()
+    {
+        return orgUnitGroupId;
+    }
+
+    public double getValue()
+    {
+        return value;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + orgUnitId;
+        result = prime * result + orgUnitGroupId;
+        return result;
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+        {
+            return true;
+        }
+        
+        if ( obj == null )
+        {
+            return false;
+        }
+        
+        if ( getClass() != obj.getClass() )
+        {
+            return false;
+        }
+        
+        final OrgUnitOperand other = (OrgUnitOperand) obj;
+        
+        return orgUnitId == other.orgUnitId && orgUnitGroupId == other.orgUnitGroupId;
+    }    
+}

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/cache/MemoryAggregationCache.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/cache/MemoryAggregationCache.java	2012-03-15 16:04:20 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/cache/MemoryAggregationCache.java	2012-08-25 11:47:17 +0000
@@ -50,15 +50,15 @@
     // Cache
     // -------------------------------------------------------------------------
 
-    private final ThreadLocal<Map<String, Collection<Integer>>> intersectingPeriodCache = new ThreadLocal<Map<String,Collection<Integer>>>();
-
-    private final ThreadLocal<Map<String, Collection<Integer>>> periodBetweenDatesCache = new ThreadLocal<Map<String,Collection<Integer>>>();
-
-    private final ThreadLocal<Map<String, Collection<Integer>>> periodBetweenDatesPeriodTypeCache = new ThreadLocal<Map<String,Collection<Integer>>>();
-
-    private final ThreadLocal<Map<String, Period>> periodCache = new ThreadLocal<Map<String,Period>>();
-
-    private final ThreadLocal<Map<String, Integer>> organisationUnitLevelCache = new ThreadLocal<Map<String, Integer>>();
+    private final Map<String, Collection<Integer>> intersectingPeriodCache = new HashMap<String,Collection<Integer>>();
+
+    private final Map<String, Collection<Integer>> periodBetweenDatesCache = new HashMap<String,Collection<Integer>>();
+
+    private final Map<String, Collection<Integer>> periodBetweenDatesPeriodTypeCache = new HashMap<String,Collection<Integer>>();
+
+    private final Map<String, Period> periodCache = new HashMap<String,Period>();
+
+    private final Map<String, Integer> organisationUnitLevelCache = new HashMap<String, Integer>();
     
     // -------------------------------------------------------------------------
     // Dependencies
@@ -86,22 +86,16 @@
     {
         final String key = startDate.toString() + SEPARATOR + endDate.toString();
         
-        Map<String, Collection<Integer>> cache = intersectingPeriodCache.get();
-        
         Collection<Integer> periods = null;
         
-        if ( cache != null && ( periods = cache.get( key ) ) != null )
+        if ( ( periods = intersectingPeriodCache.get( key ) ) != null )
         {
             return periods;
         }
         
         periods = ConversionUtils.getIdentifiers( Period.class, periodService.getIntersectingPeriods( startDate, endDate ) );
         
-        cache = ( cache == null ) ? new HashMap<String, Collection<Integer>>() : cache;
-        
-        cache.put( key, periods );
-        
-        intersectingPeriodCache.set( cache );
+        intersectingPeriodCache.put( key, periods );
         
         return periods;
     }
@@ -110,46 +104,34 @@
     {
         final String key = startDate.toString() + SEPARATOR + endDate.toString();
         
-        Map<String, Collection<Integer>> cache = periodBetweenDatesCache.get();
-        
         Collection<Integer> periods = null;
         
-        if ( cache != null && ( periods = cache.get( key ) ) != null )
+        if ( ( periods = periodBetweenDatesCache.get( key ) ) != null )
         {
             return periods;
         }
         
         periods = ConversionUtils.getIdentifiers( Period.class, periodService.getPeriodsBetweenDates( startDate, endDate ) );
         
-        cache = ( cache == null ) ? new HashMap<String, Collection<Integer>>() : cache;
-        
-        cache.put( key, periods );
-        
-        periodBetweenDatesCache.set( cache );
+        periodBetweenDatesCache.put( key, periods );
         
         return periods;
     }
 
-    public Collection<Integer> getPeriodsBetweenDatesPeriodType( final PeriodType periodType, final Date startDate, final Date endDate )
+    public Collection<Integer> getPeriodsBetweenDatesPeriodType( final PeriodType periodType, final Date startDate, final Date endDate ) //TODO remove?
     {
         final String key = periodType.getName() + SEPARATOR + startDate.toString() + SEPARATOR + endDate.toString();
         
-        Map<String, Collection<Integer>> cache = periodBetweenDatesPeriodTypeCache.get();
-        
         Collection<Integer> periods = null;
         
-        if ( cache != null && ( periods = cache.get( key ) ) != null )
+        if ( ( periods = periodBetweenDatesPeriodTypeCache.get( key ) ) != null )
         {
             return periods;
         }
         
         periods = ConversionUtils.getIdentifiers( Period.class, periodService.getPeriodsBetweenDates( periodType, startDate, endDate ) );
         
-        cache = ( cache == null ) ? new HashMap<String, Collection<Integer>>() : cache;
-        
-        cache.put( key, periods );
-        
-        periodBetweenDatesPeriodTypeCache.set( cache );
+        periodBetweenDatesPeriodTypeCache.put( key, periods );
         
         return periods;
     }
@@ -158,22 +140,16 @@
     {
         final String key = String.valueOf( id );
         
-        Map<String, Period> cache = periodCache.get();
-        
         Period period = null;
         
-        if ( cache != null && ( period = cache.get( key ) ) != null )
+        if ( ( period = periodCache.get( key ) ) != null )
         {
             return period;
         }
         
         period = periodService.getPeriod( id );
         
-        cache = ( cache == null ) ? new HashMap<String, Period>() : cache;
-        
-        cache.put( key, period );
-        
-        periodCache.set( cache );
+        periodCache.put( key, period );
         
         return period;
     }
@@ -182,32 +158,26 @@
     {
         final String key = String.valueOf( id );
         
-        Map<String, Integer> cache = organisationUnitLevelCache.get();
-        
         Integer level = null;
         
-        if ( cache != null && ( level = cache.get( key ) ) != null )
+        if ( ( level = organisationUnitLevelCache.get( key ) ) != null )
         {
             return level;
         }
                 
         level = organisationUnitService.getLevelOfOrganisationUnit( id );
         
-        cache = ( cache == null ) ? new HashMap<String, Integer>() : cache;
-        
-        cache.put( key, level );
-        
-        organisationUnitLevelCache.set( cache );
+        organisationUnitLevelCache.put( key, level );
         
         return level;
     }
     
     public void clearCache()
     {
-        intersectingPeriodCache.remove();
-        periodBetweenDatesCache.remove();
-        periodBetweenDatesPeriodTypeCache.remove();
-        periodCache.remove();
-        organisationUnitLevelCache.remove();
+        intersectingPeriodCache.clear();
+        periodBetweenDatesCache.clear();
+        periodBetweenDatesPeriodTypeCache.clear();
+        periodCache.clear();
+        organisationUnitLevelCache.clear();
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageBoolAggregator.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageBoolAggregator.java	2012-04-17 14:57:04 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageBoolAggregator.java	2012-08-25 11:47:17 +0000
@@ -31,20 +31,21 @@
 import static org.hisp.dhis.dataelement.DataElement.VALUE_TYPE_BOOL;
 import static org.hisp.dhis.system.util.DateUtils.getDaysInclusive;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
 
 import org.hisp.dhis.dataelement.DataElementOperand;
 import org.hisp.dhis.datamart.CrossTabDataValue;
+import org.hisp.dhis.datamart.OrgUnitOperand;
 import org.hisp.dhis.datamart.aggregation.cache.AggregationCache;
 import org.hisp.dhis.datamart.crosstab.CrossTabService;
+import org.hisp.dhis.organisationunit.OrganisationUnitGroup;
+import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
-import org.springframework.util.CollectionUtils;
+import org.hisp.dhis.system.util.MathUtils;
 
 /**
  * @author Lars Helge Overland
@@ -74,42 +75,63 @@
     // DataElementAggregator implementation
     // -------------------------------------------------------------------------
 
-    public Map<DataElementOperand, Double> getAggregatedValues( final Collection<DataElementOperand> operands, 
-        final Period period, int unitLevel, final Collection<Integer> organisationUnits, String key )
+    public List<OrgUnitOperand> getAggregatedValues( DataElementOperand operand, 
+        Period period, Collection<Integer> organisationUnits, Collection<OrganisationUnitGroup> organisationUnitGroups, OrganisationUnitHierarchy hierarchy, String key )
     {
-        if ( CollectionUtils.isEmpty( operands ) )
+        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operand, 
+            aggregationCache.getIntersectingPeriods( period.getStartDate(), period.getEndDate() ), hierarchy.getChildren( organisationUnits ), key );
+        
+        final List<OrgUnitOperand> values = new ArrayList<OrgUnitOperand>();
+
+        if ( crossTabValues.size() == 0 )
         {
-            return EMPTY_MAP;
+            return values;
         }
         
-        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operands, 
-            aggregationCache.getIntersectingPeriods( period.getStartDate(), period.getEndDate() ), organisationUnits, key );
-        
-        final Map<DataElementOperand, double[]> entries = getAggregate( crossTabValues, period.getStartDate(), 
-            period.getEndDate(), period.getStartDate(), period.getEndDate(), unitLevel ); // <Operand, [total value, total relevant days]>
-
-        final Map<DataElementOperand, Double> values = new HashMap<DataElementOperand, Double>(); // <Operand, total value>
-
-        double average = 0.0;
-        
-        for ( final Entry<DataElementOperand, double[]> entry : entries.entrySet() )
+        for ( final Integer organisationUnit : organisationUnits )
         {
-            if ( entry.getValue() != null && entry.getValue()[ 1 ] > 0 )
+            final int unitLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( organisationUnit ) : 0;
+            
+            for ( OrganisationUnitGroup group : organisationUnitGroups )
             {
-                average = entry.getValue()[ 0 ] / entry.getValue()[ 1 ] * 100; // Percentage value
-                
-                values.put( entry.getKey(), average );
+                final Collection<Integer> orgUnitChildren = hierarchy.getChildren( organisationUnit, group );
+
+                double totalValue = 0.0;
+                double totalRelevantDays = 0.0;
+                
+                for ( Integer orgUnitChild : orgUnitChildren )
+                {
+                    final int dataValueLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( orgUnitChild ) : 0;
+                    
+                    if ( operand.isHasAggregationLevels() && !operand.aggregationLevelIsValid( unitLevel, dataValueLevel ) )
+                    {
+                        continue;
+                    }
+    
+                    final double[] entry = getAggregate( orgUnitChild, crossTabValues, period.getStartDate(), period.getEndDate(), unitLevel ); // <Operand, [total value, total relevant days]>
+                    
+                    totalValue += entry[0];
+                    totalRelevantDays += entry[1];                
+                }
+                
+                if ( !MathUtils.isZero( totalRelevantDays ) )
+                {
+                    double average = totalValue / totalRelevantDays * 100; // Percentage value
+
+                    values.add( new OrgUnitOperand( organisationUnit, group != null ? group.getId() : 0, average ) );
+                }
             }
         }
         
         return values;        
     }
     
-    private Map<DataElementOperand, double[]> getAggregate( final Collection<CrossTabDataValue> crossTabValues, 
-        final Date startDate, final Date endDate, final Date aggregationStartDate, final Date aggregationEndDate, int unitLevel )
+    private double[] getAggregate( int organisationUnit, Collection<CrossTabDataValue> crossTabValues, 
+        Date startDate, Date endDate, int unitLevel )
     {
-        final Map<DataElementOperand, double[]> totalSums = new HashMap<DataElementOperand, double[]>(); // <Operand, [total value, total relevant days]>
-        
+        double totalValue = 0.0;
+        double totalRelevantDays = 0.0;
+
         for ( final CrossTabDataValue crossTabValue : crossTabValues )
         {
             final Period period = aggregationCache.getPeriod( crossTabValue.getPeriodId() );
@@ -119,68 +141,54 @@
             
             final double duration = getDaysInclusive( currentStartDate, currentEndDate );
 
-            final int dataValueLevel = aggregationCache.getLevelOfOrganisationUnit( crossTabValue.getSourceId() );
-            
             if ( duration > 0 )
             {
-                for ( final Entry<DataElementOperand, String> entry : crossTabValue.getValueMap().entrySet() ) // <Operand, value>
-                {
-                    if ( entry.getValue() != null && entry.getKey().aggregationLevelIsValid( unitLevel, dataValueLevel ) )
-                    {
-                        double value = 0.0;                        
-                        double relevantDays = 0.0;
-                        
-                        if ( currentStartDate.compareTo( startDate ) >= 0 && currentEndDate.compareTo( endDate ) <= 0 ) // Value is within period
-                        {
-                            relevantDays = getDaysInclusive( currentStartDate, currentEndDate );
-                        }
-                        else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( endDate ) >= 0 ) // Value spans whole period
-                        {
-                            relevantDays = getDaysInclusive( startDate, endDate );
-                        }
-                        else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( startDate ) >= 0 // Value spans period start
-                            && currentEndDate.compareTo( endDate ) <= 0 )
-                        {
-                            relevantDays = getDaysInclusive( startDate, currentEndDate );
-                        }
-                        else if ( currentStartDate.compareTo( startDate ) >= 0 && currentStartDate.compareTo( endDate ) <= 0 // Value spans period end
-                            && currentEndDate.compareTo( endDate ) >= 0 )
-                        {
-                            relevantDays = getDaysInclusive( currentStartDate, endDate );
-                        }
-
-                        if ( entry.getValue().toLowerCase().equals( TRUE ) )
-                        {
-                            value = relevantDays;
-                        }
-
-                        final double[] totalSum = totalSums.get( entry.getKey() );
-                        value += totalSum != null ? totalSum[0] : 0;
-                        relevantDays += totalSum != null ? totalSum[1] : 0;
-                        
-                        final double[] values = { value, relevantDays };
-                        
-                        totalSums.put( entry.getKey(), values );
-                    }
-                }
+                double value = 0.0;                        
+                double relevantDays = 0.0;
+
+                String val = crossTabValue.getValueMap().get( organisationUnit );
+
+                if ( val == null )
+                {
+                    continue;
+                }
+                
+                if ( currentStartDate.compareTo( startDate ) >= 0 && currentEndDate.compareTo( endDate ) <= 0 ) // Value is within period
+                {
+                    relevantDays = getDaysInclusive( currentStartDate, currentEndDate );
+                }
+                else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( endDate ) >= 0 ) // Value spans whole period
+                {
+                    relevantDays = getDaysInclusive( startDate, endDate );
+                }
+                else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( startDate ) >= 0 // Value spans period start
+                    && currentEndDate.compareTo( endDate ) <= 0 )
+                {
+                    relevantDays = getDaysInclusive( startDate, currentEndDate );
+                }
+                else if ( currentStartDate.compareTo( startDate ) >= 0 && currentStartDate.compareTo( endDate ) <= 0 // Value spans period end
+                    && currentEndDate.compareTo( endDate ) >= 0 )
+                {
+                    relevantDays = getDaysInclusive( currentStartDate, endDate );
+                }
+
+                if ( val.toLowerCase().equals( TRUE ) )
+                {
+                    value = relevantDays;
+                }
+
+                totalValue += value;
+                totalRelevantDays += relevantDays;
             }
         }
+
+        final double[] values = { totalValue, totalRelevantDays };
         
-        return totalSums;
+        return values;
     }
 
-    public Collection<DataElementOperand> filterOperands( final Collection<DataElementOperand> operands, final PeriodType periodType )
+    public boolean isApplicable( DataElementOperand operand, PeriodType periodType )
     {
-        final Collection<DataElementOperand> filteredOperands = new HashSet<DataElementOperand>();
-        
-        for ( final DataElementOperand operand : operands )
-        {
-            if ( operand.getValueType().equals( VALUE_TYPE_BOOL ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_AVERAGE ) )
-            {
-                filteredOperands.add( operand );
-            }
-        }
-        
-        return filteredOperands;
+        return ( operand.getValueType().equals( VALUE_TYPE_BOOL ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_AVERAGE ) );
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntAggregator.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntAggregator.java	2012-04-17 14:57:04 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntAggregator.java	2012-08-25 11:47:17 +0000
@@ -31,22 +31,23 @@
 import static org.hisp.dhis.dataelement.DataElement.VALUE_TYPE_INT;
 import static org.hisp.dhis.system.util.DateUtils.getDaysInclusive;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.hisp.dhis.dataelement.DataElementOperand;
 import org.hisp.dhis.datamart.CrossTabDataValue;
+import org.hisp.dhis.datamart.OrgUnitOperand;
 import org.hisp.dhis.datamart.aggregation.cache.AggregationCache;
 import org.hisp.dhis.datamart.crosstab.CrossTabService;
+import org.hisp.dhis.organisationunit.OrganisationUnitGroup;
+import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
-import org.springframework.util.CollectionUtils;
+import org.hisp.dhis.system.util.MathUtils;
 
 /**
  * @author Lars Helge Overland
@@ -78,33 +79,51 @@
     // DataElementAggregator implementation
     // -------------------------------------------------------------------------
 
-    public Map<DataElementOperand, Double> getAggregatedValues( final Collection<DataElementOperand> operands, 
-        final Period period, int unitLevel, final Collection<Integer> organisationUnits, String key )
+    public List<OrgUnitOperand> getAggregatedValues( DataElementOperand operand, Period period, 
+        Collection<Integer> organisationUnits, Collection<OrganisationUnitGroup> organisationUnitGroups, OrganisationUnitHierarchy hierarchy, String key )
     {
-        if ( CollectionUtils.isEmpty( operands ) )
+        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operand, 
+            aggregationCache.getIntersectingPeriods( period.getStartDate(), period.getEndDate() ), hierarchy.getChildren( organisationUnits ), key );
+        
+        final List<OrgUnitOperand> values = new ArrayList<OrgUnitOperand>();
+
+        if ( crossTabValues.size() == 0 )
         {
-            return EMPTY_MAP;
+            return values;
         }
         
-        final Map<DataElementOperand, Double> values = new HashMap<DataElementOperand, Double>(); // <Operand, total value>
-        
-        for ( final Integer unitId : organisationUnits )
+        for ( final Integer organisationUnit : organisationUnits )
         {
-            final Collection<CrossTabDataValue> crossTabValues = 
-                crossTabService.getCrossTabDataValues( operands, aggregationCache.getIntersectingPeriods( period.getStartDate(), period.getEndDate() ), unitId, key );
-            
-            final Map<DataElementOperand, double[]> entries = getAggregate( crossTabValues, period.getStartDate(), 
-                period.getEndDate(), period.getStartDate(), period.getEndDate(), unitLevel ); // <Operand, [total value, total relevant days]>
-            
-            for ( final Entry<DataElementOperand, double[]> entry : entries.entrySet() ) 
+            final int unitLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( organisationUnit ) : 0;
+            
+            for ( OrganisationUnitGroup group : organisationUnitGroups )
             {
-                if ( entry.getValue() != null && entry.getValue()[ 1 ] > 0 )
-                {
-                    double average = entry.getValue()[ 0 ] / entry.getValue()[ 1 ];
-                    
-                    average += values.containsKey( entry.getKey() ) ? values.get( entry.getKey() ) : 0;
-                    
-                    values.put( entry.getKey(), average );
+                final Collection<Integer> orgUnitChildren = hierarchy.getChildren( organisationUnit, group );
+
+                double value = 0d;
+                
+                for ( Integer orgUnitChild : orgUnitChildren )
+                {
+                    final int dataValueLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( orgUnitChild ) : 0;
+                    
+                    if ( operand.isHasAggregationLevels() && !operand.aggregationLevelIsValid( unitLevel, dataValueLevel ) )
+                    {
+                        continue;
+                    }
+                    
+                    final double[] entry = getAggregate( orgUnitChild, crossTabValues, period.getStartDate(), period.getEndDate(), unitLevel ); // <Org unit, [total value, total relevant days]>
+                    
+                    if ( !MathUtils.isZero( entry[1] ) )
+                    {
+                        double average = entry[ 0 ] / entry[ 1 ];
+                        
+                        value += average;
+                    }
+                }
+                
+                if ( !MathUtils.isZero( value ) )
+                {
+                    values.add( new OrgUnitOperand( organisationUnit, group != null ? group.getId() : 0, value ) );
                 }
             }
         }  
@@ -112,10 +131,11 @@
         return values;
     }
     
-    private Map<DataElementOperand, double[]> getAggregate( final Collection<CrossTabDataValue> crossTabValues, 
-        final Date startDate, final Date endDate, final Date aggregationStartDate, final Date aggregationEndDate, int unitLevel )
+    private double[] getAggregate( int organisationUnit, Collection<CrossTabDataValue> crossTabValues, 
+        Date startDate, Date endDate, int unitLevel )
     {
-        final Map<DataElementOperand, double[]> totalSums = new HashMap<DataElementOperand, double[]>(); // <Operand, [total value, total relevant days]>
+        double totalValue = 0d;
+        double totalRelevantDays = 0d;
 
         for ( final CrossTabDataValue crossTabValue : crossTabValues )
         {
@@ -124,78 +144,64 @@
             final Date currentStartDate = period.getStartDate();
             final Date currentEndDate = period.getEndDate();
 
-            final int dataValueLevel = aggregationCache.getLevelOfOrganisationUnit( crossTabValue.getSourceId() );
-
             final double duration = getDaysInclusive( currentStartDate, currentEndDate );
             
             if ( duration > 0 )
-            {            
-                for ( final Entry<DataElementOperand, String> entry : crossTabValue.getValueMap().entrySet() ) // <Operand, value>
-                {
-                    if ( entry.getValue() != null && entry.getKey().aggregationLevelIsValid( unitLevel, dataValueLevel )  )
-                    {
-                        double value = 0.0;
-                        double relevantDays = 0.0;               
-                        
-                        try
-                        {
-                            value = Double.parseDouble( entry.getValue() );
-                        }
-                        catch ( NumberFormatException ex )
-                        {
-                            log.warn( "Value skipped, not numeric: '" + entry.getValue() );
-                            continue;
-                        }
-                        
-                        if ( currentStartDate.compareTo( startDate ) >= 0 && currentEndDate.compareTo( endDate ) <= 0 ) // Value is within period
-                        {
-                            relevantDays = getDaysInclusive( currentStartDate, currentEndDate );
-                        }
-                        else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( endDate ) >= 0 ) // Value spans whole period
-                        {
-                            relevantDays = getDaysInclusive( startDate, endDate );
-                        }
-                        else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( startDate ) >= 0
-                            && currentEndDate.compareTo( endDate ) <= 0 ) // Value spans period start
-                        {
-                            relevantDays = getDaysInclusive( startDate, currentEndDate );
-                        }
-                        else if ( currentStartDate.compareTo( startDate ) >= 0 && currentStartDate.compareTo( endDate ) <= 0
-                            && currentEndDate.compareTo( endDate ) >= 0 ) // Value spans period end
-                        {
-                            relevantDays = getDaysInclusive( currentStartDate, endDate );
-                        }
-                        
-                        value = value * relevantDays;
-
-                        final double[] totalSum = totalSums.get( entry.getKey() );
-                        value += totalSum != null ? totalSum[0] : 0;
-                        relevantDays += totalSum != null ? totalSum[1] : 0;
-                        
-                        final double[] values = { value, relevantDays };
-                        
-                        totalSums.put( entry.getKey(), values );
-                    }
-                }
+            {
+                double value = 0.0;
+                double relevantDays = 0.0;
+
+                String val = crossTabValue.getValueMap().get( organisationUnit );
+
+                if ( val == null )
+                {
+                    continue;
+                }
+                
+                try
+                {
+                    value = Double.parseDouble( val );
+                }
+                catch ( NumberFormatException ex )
+                {
+                    log.warn( "Value skipped, not numeric: '" + val );
+                    continue;
+                }
+                
+                if ( currentStartDate.compareTo( startDate ) >= 0 && currentEndDate.compareTo( endDate ) <= 0 ) // Value is within period
+                {
+                    relevantDays = getDaysInclusive( currentStartDate, currentEndDate );
+                }
+                else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( endDate ) >= 0 ) // Value spans whole period
+                {
+                    relevantDays = getDaysInclusive( startDate, endDate );
+                }
+                else if ( currentStartDate.compareTo( startDate ) <= 0 && currentEndDate.compareTo( startDate ) >= 0
+                    && currentEndDate.compareTo( endDate ) <= 0 ) // Value spans period start
+                {
+                    relevantDays = getDaysInclusive( startDate, currentEndDate );
+                }
+                else if ( currentStartDate.compareTo( startDate ) >= 0 && currentStartDate.compareTo( endDate ) <= 0
+                    && currentEndDate.compareTo( endDate ) >= 0 ) // Value spans period end
+                {
+                    relevantDays = getDaysInclusive( currentStartDate, endDate );
+                }
+                
+                value = value * relevantDays;
+
+                totalValue += value;
+                totalRelevantDays += relevantDays;
             }
         }                    
         
-        return totalSums;
+        final double[] values = { totalValue, totalRelevantDays };
+        
+        return values;
     }
 
-    public Collection<DataElementOperand> filterOperands( final Collection<DataElementOperand> operands, final PeriodType periodType )
+    public boolean isApplicable( DataElementOperand operand, PeriodType periodType )
     {
-        final Collection<DataElementOperand> filteredOperands = new HashSet<DataElementOperand>();
-        
-        for ( final DataElementOperand operand : operands )
-        {
-            if ( operand.getValueType().equals( VALUE_TYPE_INT ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_AVERAGE ) &&
-                operand.getFrequencyOrder() < periodType.getFrequencyOrder() )
-            {
-                filteredOperands.add( operand );
-            }
-        }
-        
-        return filteredOperands;
+        return ( operand.getValueType().equals( VALUE_TYPE_INT ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_AVERAGE ) &&
+            operand.getFrequencyOrder() < periodType.getFrequencyOrder() );
     }    
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntSingleValueAggregator.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntSingleValueAggregator.java	2012-01-09 20:03:56 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/AverageIntSingleValueAggregator.java	2012-08-25 11:47:17 +0000
@@ -30,21 +30,22 @@
 import static org.hisp.dhis.dataelement.DataElement.AGGREGATION_OPERATOR_AVERAGE;
 import static org.hisp.dhis.dataelement.DataElement.VALUE_TYPE_INT;
 
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.hisp.dhis.dataelement.DataElementOperand;
 import org.hisp.dhis.datamart.CrossTabDataValue;
+import org.hisp.dhis.datamart.OrgUnitOperand;
 import org.hisp.dhis.datamart.aggregation.cache.AggregationCache;
 import org.hisp.dhis.datamart.crosstab.CrossTabService;
+import org.hisp.dhis.organisationunit.OrganisationUnitGroup;
+import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
-import org.springframework.util.CollectionUtils;
+import org.hisp.dhis.system.util.MathUtils;
 
 /**
  * @author Lars Helge Overland
@@ -76,62 +77,72 @@
     // DataElementAggregator implementation
     // -------------------------------------------------------------------------
 
-    public Map<DataElementOperand, Double> getAggregatedValues( final Collection<DataElementOperand> operands, 
-        final Period period, int unitLevel, final Collection<Integer> organisationUnits, String key )
+    public List<OrgUnitOperand> getAggregatedValues( DataElementOperand operand, Period period, 
+        Collection<Integer> organisationUnits, Collection<OrganisationUnitGroup> organisationUnitGroups, OrganisationUnitHierarchy hierarchy, String key )
     {
-        if ( CollectionUtils.isEmpty( operands ) )
+        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operand, 
+            aggregationCache.getIntersectingPeriods( period.getStartDate(), period.getEndDate() ), hierarchy.getChildren( organisationUnits ), key );
+        
+        final List<OrgUnitOperand> values = new ArrayList<OrgUnitOperand>();
+
+        if ( crossTabValues.size() == 0 )
         {
-            return EMPTY_MAP;
+            return values;
         }
         
-        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operands, 
-            aggregationCache.getIntersectingPeriods( period.getStartDate(), period.getEndDate() ), organisationUnits, key );
-        
-        final Map<DataElementOperand, Double> values = new HashMap<DataElementOperand, Double>(); // <Operand, total value>
-
-        for ( final CrossTabDataValue crossTabValue : crossTabValues )
+        for ( Integer organisationUnit : organisationUnits )
         {
-            final int dataValueLevel = aggregationCache.getLevelOfOrganisationUnit( crossTabValue.getSourceId() );
+            final int unitLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( organisationUnit ) : 0; //TODO aggregation level
             
-            for ( final Entry<DataElementOperand, String> entry : crossTabValue.getValueMap().entrySet() ) // <Operand, value>
+            for ( OrganisationUnitGroup group : organisationUnitGroups )
             {
-                if ( entry.getValue() != null && entry.getKey().aggregationLevelIsValid( unitLevel, dataValueLevel )  )
+                final Collection<Integer> orgUnitChildren = hierarchy.getChildren( organisationUnit, group ); // TODO group
+
+                double value = 0d;
+                
+                for ( Integer orgUnitChild : orgUnitChildren )
                 {
-                    double value = 0.0;
+                    final int dataValueLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( orgUnitChild ) : 0; //TODO aggregation level
                     
-                    try
-                    {
-                        value = Double.parseDouble( entry.getValue() );
-                    }
-                    catch ( NumberFormatException ex )
-                    {
-                        log.warn( "Value skipped, not numeric: '" + entry.getValue() );
+                    if ( operand.isHasAggregationLevels() && !operand.aggregationLevelIsValid( unitLevel, dataValueLevel ) )
+                    {
                         continue;
                     }
-
-                    final Double current = values.get( entry.getKey() );
-                    value += current != null ? current : 0.0;        
-                    values.put( entry.getKey(), value );
+                    
+                    for ( CrossTabDataValue crossTabValue : crossTabValues ) // Iterate periods
+                    {
+                        String val = crossTabValue.getValueMap().get( orgUnitChild ); // TODO make set of periods, avoid iteration?
+    
+                        if ( val == null )
+                        {
+                            continue;
+                        }
+                        
+                        try
+                        {
+                            value += Double.parseDouble( val );
+                        }
+                        catch ( NumberFormatException ex )
+                        {
+                            log.warn( "Value skipped, not numeric: '" + val );
+                            continue;
+                        }
+                    }
+                }
+                
+                if ( !MathUtils.isZero( value ) )
+                {
+                    values.add( new OrgUnitOperand( organisationUnit, group != null ? group.getId() : 0, value ) );
                 }
             }
-        }                    
+        }
         
         return values;
     }
     
-    public Collection<DataElementOperand> filterOperands( final Collection<DataElementOperand> operands, final PeriodType periodType )
+    public boolean isApplicable( DataElementOperand operand, PeriodType periodType )
     {
-        final Collection<DataElementOperand> filteredOperands = new HashSet<DataElementOperand>();
-        
-        for ( final DataElementOperand operand : operands )
-        {
-            if ( operand.getValueType().equals( VALUE_TYPE_INT ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_AVERAGE ) &&
-                operand.getFrequencyOrder() >= periodType.getFrequencyOrder() )
-            {
-                filteredOperands.add( operand );
-            }
-        }
-        
-        return filteredOperands;
+        return ( operand.getValueType().equals( VALUE_TYPE_INT ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_AVERAGE ) &&
+            operand.getFrequencyOrder() >= periodType.getFrequencyOrder() );
     }    
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/DataElementAggregator.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/DataElementAggregator.java	2011-12-26 10:07:59 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/DataElementAggregator.java	2012-08-25 11:47:17 +0000
@@ -29,9 +29,13 @@
 
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.hisp.dhis.dataelement.DataElementOperand;
+import org.hisp.dhis.datamart.OrgUnitOperand;
+import org.hisp.dhis.organisationunit.OrganisationUnitGroup;
+import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
 
@@ -43,8 +47,8 @@
     final String TRUE = "true";
     final Map<DataElementOperand, Double> EMPTY_MAP = new HashMap<DataElementOperand, Double>();
 
-    Map<DataElementOperand, Double> getAggregatedValues( final Collection<DataElementOperand> operands, 
-        final Period period, int unitLevel, final Collection<Integer> organisationUnits, String key );
+    List<OrgUnitOperand> getAggregatedValues( DataElementOperand operand, Period period, 
+        Collection<Integer> organisationUnits, Collection<OrganisationUnitGroup> organisationUnitGroups, OrganisationUnitHierarchy hierarchy, String key );
     
-    Collection<DataElementOperand> filterOperands( Collection<DataElementOperand> operands, PeriodType periodType );
+    boolean isApplicable( DataElementOperand operand, PeriodType periodType );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumBoolAggregator.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumBoolAggregator.java	2012-01-09 20:03:56 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumBoolAggregator.java	2012-08-25 11:47:17 +0000
@@ -30,19 +30,20 @@
 import static org.hisp.dhis.dataelement.DataElement.AGGREGATION_OPERATOR_SUM;
 import static org.hisp.dhis.dataelement.DataElement.VALUE_TYPE_BOOL;
 
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
 
 import org.hisp.dhis.dataelement.DataElementOperand;
 import org.hisp.dhis.datamart.CrossTabDataValue;
+import org.hisp.dhis.datamart.OrgUnitOperand;
 import org.hisp.dhis.datamart.aggregation.cache.AggregationCache;
 import org.hisp.dhis.datamart.crosstab.CrossTabService;
+import org.hisp.dhis.organisationunit.OrganisationUnitGroup;
+import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
-import org.springframework.util.CollectionUtils;
+import org.hisp.dhis.system.util.MathUtils;
 
 /**
  * @author Lars Helge Overland
@@ -72,37 +73,57 @@
     // DataElementAggregator implementation
     // -------------------------------------------------------------------------
 
-    public Map<DataElementOperand, Double> getAggregatedValues( final Collection<DataElementOperand> operands, 
-        final Period period, int unitLevel, final Collection<Integer> organisationUnits, String key )
-    {
-        if ( CollectionUtils.isEmpty( operands ) )
+    public List<OrgUnitOperand> getAggregatedValues( DataElementOperand operand, Period period, 
+        Collection<Integer> organisationUnits, Collection<OrganisationUnitGroup> organisationUnitGroups, OrganisationUnitHierarchy hierarchy, String key )
+    {        
+        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operand, 
+            aggregationCache.getPeriodsBetweenDates( period.getStartDate(), period.getEndDate() ), hierarchy.getChildren( organisationUnits ), key );
+
+        final List<OrgUnitOperand> values = new ArrayList<OrgUnitOperand>();
+
+        if ( crossTabValues.size() == 0 )
         {
-            return EMPTY_MAP;
+            return values;
         }
         
-        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operands, 
-            aggregationCache.getIntersectingPeriods( period.getStartDate(), period.getEndDate() ), organisationUnits, key );
-
-        final Map<DataElementOperand, Double> values = new HashMap<DataElementOperand, Double>(); // <Operand, total value>
-
-        for ( final CrossTabDataValue crossTabValue : crossTabValues )
+        for ( Integer organisationUnit : organisationUnits )
         {
-            final int dataValueLevel = aggregationCache.getLevelOfOrganisationUnit( crossTabValue.getSourceId() );
+            final int unitLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( organisationUnit ) : 0;
             
-            for ( final Entry<DataElementOperand, String> entry : crossTabValue.getValueMap().entrySet() ) // <Operand, value>
+            for ( OrganisationUnitGroup group : organisationUnitGroups )
             {
-                if ( entry.getValue() != null && entry.getKey().aggregationLevelIsValid( unitLevel, dataValueLevel ) )
-                {
-                    double value = 0.0;
-
-                    if ( entry.getValue().toLowerCase().equals( TRUE ) )
-                    {
-                        value = 1;
-                    }
-
-                    final Double current = values.get( entry.getKey() );
-                    value += current != null ? current : 0.0;        
-                    values.put( entry.getKey(), value );
+                final Collection<Integer> orgUnitChildren = hierarchy.getChildren( organisationUnit, group );
+
+                double value = 0d;
+                
+                for ( Integer orgUnitChild : orgUnitChildren )
+                {
+                    final int dataValueLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( orgUnitChild ) : 0;
+                    
+                    if ( operand.isHasAggregationLevels() && !operand.aggregationLevelIsValid( unitLevel, dataValueLevel ) )
+                    {
+                        continue;
+                    }
+                    
+                    for ( CrossTabDataValue crossTabValue : crossTabValues )
+                    {
+                        String val = crossTabValue.getValueMap().get( orgUnitChild );
+                        
+                        if ( val == null )
+                        {
+                            continue;
+                        }
+                        
+                        if ( val.toLowerCase().equals( TRUE ) )
+                        {
+                            value++;
+                        }
+                    }
+                }
+                
+                if ( !MathUtils.isZero( value ) )
+                {
+                    values.add( new OrgUnitOperand( organisationUnit, group != null ? group.getId() : 0, value ) );
                 }
             }
         }
@@ -110,19 +131,9 @@
         return values;
     }
     
-    public Collection<DataElementOperand> filterOperands( final Collection<DataElementOperand> operands, final PeriodType periodType )
+    public boolean isApplicable( DataElementOperand operand, PeriodType periodType )
     {
-        final Collection<DataElementOperand> filteredOperands = new HashSet<DataElementOperand>();
-        
-        for ( final DataElementOperand operand : operands )
-        {
-            if ( operand.getValueType().equals( VALUE_TYPE_BOOL ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_SUM ) &&
-                operand.getFrequencyOrder() <= periodType.getFrequencyOrder() ) // Ignore disaggregation
-            {
-                filteredOperands.add( operand );
-            }
-        }
-        
-        return filteredOperands;
+        return ( operand.getValueType().equals( VALUE_TYPE_BOOL ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_SUM ) &&
+            operand.getFrequencyOrder() <= periodType.getFrequencyOrder() ); // Ignore disaggregation
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumIntAggregator.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumIntAggregator.java	2012-01-09 20:03:56 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/aggregation/dataelement/SumIntAggregator.java	2012-08-25 11:47:17 +0000
@@ -30,21 +30,22 @@
 import static org.hisp.dhis.dataelement.DataElement.AGGREGATION_OPERATOR_SUM;
 import static org.hisp.dhis.dataelement.DataElement.VALUE_TYPE_INT;
 
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.hisp.dhis.dataelement.DataElementOperand;
 import org.hisp.dhis.datamart.CrossTabDataValue;
+import org.hisp.dhis.datamart.OrgUnitOperand;
 import org.hisp.dhis.datamart.aggregation.cache.AggregationCache;
 import org.hisp.dhis.datamart.crosstab.CrossTabService;
+import org.hisp.dhis.organisationunit.OrganisationUnitGroup;
+import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
-import org.springframework.util.CollectionUtils;
+import org.hisp.dhis.system.util.MathUtils;
 
 /**
  * @author Lars Helge Overland
@@ -76,42 +77,62 @@
     // DataElementAggregator implementation
     // -------------------------------------------------------------------------
 
-    public Map<DataElementOperand, Double> getAggregatedValues( final Collection<DataElementOperand> operands, 
-        final Period period, int unitLevel, final Collection<Integer> organisationUnits, String key )
+    public List<OrgUnitOperand> getAggregatedValues( DataElementOperand operand, Period period, 
+        Collection<Integer> organisationUnits, Collection<OrganisationUnitGroup> organisationUnitGroups, OrganisationUnitHierarchy hierarchy, String key )
     {
-        if ( CollectionUtils.isEmpty( operands ) )
+        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operand, 
+            aggregationCache.getPeriodsBetweenDates( period.getStartDate(), period.getEndDate() ), hierarchy.getChildren( organisationUnits ), key );
+        
+        final List<OrgUnitOperand> values = new ArrayList<OrgUnitOperand>();
+
+        if ( crossTabValues.size() == 0 )
         {
-            return EMPTY_MAP;
+            return values;
         }
         
-        final Collection<CrossTabDataValue> crossTabValues = crossTabService.getCrossTabDataValues( operands, 
-            aggregationCache.getPeriodsBetweenDates( period.getStartDate(), period.getEndDate() ), organisationUnits, key );
-        
-        final Map<DataElementOperand, Double> values = new HashMap<DataElementOperand, Double>(); // <Operand, total value>
-
-        for ( final CrossTabDataValue crossTabValue : crossTabValues )
+        for ( Integer organisationUnit : organisationUnits )
         {
-            final int dataValueLevel = aggregationCache.getLevelOfOrganisationUnit( crossTabValue.getSourceId() );
+            final int unitLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( organisationUnit ) : 0;
             
-            for ( final Entry<DataElementOperand, String> entry : crossTabValue.getValueMap().entrySet() ) // <Operand, value>
+            for ( OrganisationUnitGroup group : organisationUnitGroups )
             {
-                if ( entry.getValue() != null && entry.getKey().aggregationLevelIsValid( unitLevel, dataValueLevel ) )
+                final Collection<Integer> orgUnitChildren = hierarchy.getChildren( organisationUnit, group );
+
+                double value = 0d;
+                
+                for ( Integer orgUnitChild : orgUnitChildren )
                 {
-                    double value = 0.0;
+                    final int dataValueLevel = operand.isHasAggregationLevels() ? aggregationCache.getLevelOfOrganisationUnit( orgUnitChild ) : 0;
                     
-                    try
-                    {
-                        value = Double.parseDouble( entry.getValue() );
-                    }
-                    catch ( NumberFormatException ex )
-                    {
-                        log.warn( "Value skipped, not numeric: '" + entry.getValue() );
+                    if ( operand.isHasAggregationLevels() && !operand.aggregationLevelIsValid( unitLevel, dataValueLevel ) )
+                    {
                         continue;
                     }
                     
-                    final Double current = values.get( entry.getKey() );
-                    value += current != null ? current : 0.0;        
-                    values.put( entry.getKey(), value );
+                    for ( CrossTabDataValue crossTabValue : crossTabValues )
+                    {
+                        final String val = crossTabValue.getValueMap().get( orgUnitChild );
+    
+                        if ( val == null )
+                        {
+                            continue;
+                        }
+                        
+                        try
+                        {
+                            value += Double.parseDouble( val );
+                        }
+                        catch ( NumberFormatException ex )
+                        {
+                            log.warn( "Value skipped, not numeric: '" + val );
+                            continue;
+                        }
+                    }
+                }
+                
+                if ( !MathUtils.isZero( value ) )
+                {
+                    values.add( new OrgUnitOperand( organisationUnit, group != null ? group.getId() : 0, value ) );
                 }
             }
         }
@@ -119,19 +140,9 @@
         return values;
     }
     
-    public Collection<DataElementOperand> filterOperands( final Collection<DataElementOperand> operands, final PeriodType periodType )
+    public boolean isApplicable( DataElementOperand operand, PeriodType periodType )
     {
-        final Collection<DataElementOperand> filteredOperands = new HashSet<DataElementOperand>();
-        
-        for ( final DataElementOperand operand : operands )
-        {
-            if ( operand.getValueType().equals( VALUE_TYPE_INT ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_SUM ) &&
-                operand.getFrequencyOrder() <= periodType.getFrequencyOrder() ) // Ignore disaggregation
-            {
-                filteredOperands.add( operand );
-            }
-        }
-        
-        return filteredOperands;
+        return ( operand.getValueType().equals( VALUE_TYPE_INT ) && operand.getAggregationOperator().equals( AGGREGATION_OPERATOR_SUM ) &&
+            operand.getFrequencyOrder() <= periodType.getFrequencyOrder() ); // Ignore disaggregation
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/CrossTabService.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/CrossTabService.java	2012-08-22 16:13:29 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/CrossTabService.java	2012-08-25 11:47:17 +0000
@@ -45,19 +45,20 @@
 {
     String ID = CrossTabService.class.getName();
 
-    String createCrossTabTable( List<DataElementOperand> operands );
+    String createCrossTabTable( List<Integer> organisationUnitIds );
     
     /**
      * Creates and populates the crosstab table. Operands without data will be
      * removed from the operands argument collection.
      * 
-     * @param operands the list of DataElementOperands.
+     * @param operands the collection of DataElementOperands.
      * @param periodIds the collection of Period identifiers.
      * @param organisationUnitIds the collection of OrganisationUnit identifiers.
+     *        The order of the list must be equal to the order of the table columns.
      * @return a List of random keys for each generated crosstab table. 
      */
-    Future<?> populateCrossTabTable( List<DataElementOperand> operands, 
-        Collection<Integer> periodIds, Collection<Integer> organisationUnitIds, String key );
+    Future<?> populateCrossTabTable( Collection<DataElementOperand> operands, 
+        Collection<Integer> periodIds, List<Integer> organisationUnitIds, String key );
 
     /**
      * Drops the crosstab table.
@@ -103,29 +104,19 @@
      * @param key the key used in the table name.
      */
     void dropAggregatedOrgUnitDataCache( String key );
-    
-    /**
-     * Gets all CrossTabDataValues for the given collection of period ids and source ids.
-     * 
-     * @param dataElementIds the data element identifiers.
-     * @param periodIds the period identifiers.
-     * @param sourceIds the source identifiers.
-     * @return collection of CrossTabDataValues.
-     */
-    Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands, Collection<Integer> periodIds, 
-        Collection<Integer> sourceIds, String key );
-
-    /**
-     * Gets all CrossTabDataValues for the given collection of period ids and the source id.
-     * 
-     * @param dataElementIds the data element identifiers.
-     * @param periodIds the period identifiers.
-     * @param sourceId the source identifier.
-     * @return collection of CrossTabDataValues.
-     */
-    Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands, Collection<Integer> periodIds, 
-        int sourceId, String key );
-    
+
+    /**
+     * Gets all CrossTabDataValues for the given collection of period identifiers 
+     * and organisation unit identifiers.
+     * 
+     * @param operand the data element operand.
+     * @param periodIds the period identifiers.
+     * @param organisationUnitIds the organisation unit identifiers.
+     * @return collection of CrossTabDataValues.
+     */
+    Collection<CrossTabDataValue> getCrossTabDataValues( DataElementOperand operand, 
+        Collection<Integer> periodIds, Collection<Integer> organisationUnitIds, String key );
+
     /**
      * Gets a map of DataElementOperands and corresponding Double aggregated data
      * element value from the cache table. If the group argument is not null it

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/DefaultCrossTabService.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/DefaultCrossTabService.java	2012-08-22 16:13:29 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/DefaultCrossTabService.java	2012-08-25 11:47:17 +0000
@@ -98,19 +98,19 @@
     // CrossTabService implementation
     // -------------------------------------------------------------------------
     
-    public String createCrossTabTable( List<DataElementOperand> operands )
+    public String createCrossTabTable( List<Integer> organisationUnitIds )
     {
         final String key = RandomStringUtils.randomAlphanumeric( 8 );
         
         crossTabStore.dropCrossTabTable( key );    
-        crossTabStore.createCrossTabTable( operands, key );
+        crossTabStore.createCrossTabTable( organisationUnitIds, key );
 
         return key;
     }
 
     @Async
-    public Future<?> populateCrossTabTable( List<DataElementOperand> operands,
-        Collection<Integer> periodIds, Collection<Integer> organisationUnitIds, String key )
+    public Future<?> populateCrossTabTable( Collection<DataElementOperand> operands,
+        Collection<Integer> periodIds, List<Integer> organisationUnitIds, String key )
     {
         statementManager.initialise();
         
@@ -119,22 +119,23 @@
 
         int rows = 0;
         
-        for ( final Integer periodId : periodIds )
+        for ( DataElementOperand operand : operands )
         {
-            for ( final Integer sourceId : organisationUnitIds )
+            for ( int periodId : periodIds )
             {
-                final Map<DataElementOperand, String> map = dataMartManager.getDataValueMap( periodId, sourceId );
-
-                final List<String> valueList = new ArrayList<String>( operands.size() + 2 );
-
+                final Map<Integer, String> map = dataMartManager.getDataValueMap( operand, periodId );
+
+                final List<String> valueList = new ArrayList<String>( operands.size() + 3 );
+
+                valueList.add( String.valueOf( operand.getDataElementId() ) );
+                valueList.add( String.valueOf( operand.getOptionComboId() ) );
                 valueList.add( String.valueOf( periodId ) );
-                valueList.add( String.valueOf( sourceId ) );
 
                 boolean hasValues = false;
 
-                for ( DataElementOperand operand : operands )
+                for ( int organisationUnitId : organisationUnitIds )
                 {
-                    String value = map.get( operand );
+                    String value = map.get( organisationUnitId );
 
                     if ( value != null && value.length() > MAX_LENGTH )
                     {
@@ -306,18 +307,12 @@
         crossTabStore.dropAggregatedOrgUnitDataCache( key );
     }
     
-    public Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands,
-        Collection<Integer> periodIds, Collection<Integer> sourceIds, String key )
+    public Collection<CrossTabDataValue> getCrossTabDataValues( DataElementOperand operand, 
+        Collection<Integer> periodIds, Collection<Integer> organisationUnitIds, String key )
     {
-        return crossTabStore.getCrossTabDataValues( operands, periodIds, sourceIds, key );
+        return crossTabStore.getCrossTabDataValues( operand, periodIds, organisationUnitIds, key );
     }
 
-    public Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands,
-        Collection<Integer> periodIds, int sourceId, String key )
-    {
-        return crossTabStore.getCrossTabDataValues( operands, periodIds, sourceId, key );
-    }
-    
     public Map<DataElementOperand, Double> getAggregatedDataCacheValue( Collection<DataElementOperand> operands, 
         Period period, OrganisationUnit unit, OrganisationUnitGroup group, String key )
     {

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/CrossTabStore.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/CrossTabStore.java	2012-07-25 16:22:03 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/CrossTabStore.java	2012-08-25 11:47:17 +0000
@@ -42,6 +42,7 @@
 {
     final String ID = CrossTabStore.class.getName();
     final String CROSSTAB_TABLE_PREFIX = "crosstab_table_";
+    final String COLUMN_PREFIX = "col";
     final String AGGREGATEDDATA_CACHE_PREFIX = "aggregateddata_cache_";
     final String AGGREGATEDORGUNITDATA_CACHE_PREFIX = "aggregatedorgunitdata_cache_";
     
@@ -52,7 +53,7 @@
      * 
      * @param operands the DataElementOperands.
      */
-    void createCrossTabTable( List<DataElementOperand> operands, String key );
+    void createCrossTabTable( List<Integer> organisationUnitIds, String key );
 
     /**
      * Drops the crosstab table.
@@ -95,26 +96,16 @@
     void dropAggregatedOrgUnitDataCache( String key );
     
     /**
-     * Gets all CrossTabDataValues for the given collection of period ids and source ids.
-     * 
-     * @param dataElementIds the data element identifiers.
-     * @param periodIds the period identifiers.
-     * @param sourceIds the source identifiers.
-     * @return collection of CrossTabDataValues.
-     */
-    Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands, Collection<Integer> periodIds, 
-        Collection<Integer> sourceIds, String key );
-
-    /**
-     * Gets all CrossTabDataValues for the given collection of period ids and the source id.
-     * 
-     * @param dataElementIds the data element identifiers.
-     * @param periodIds the period identifiers.
-     * @param sourceId the source identifier.
-     * @return collection of CrossTabDataValues.
-     */
-    Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands, Collection<Integer> periodIds, 
-        int sourceId, String key );
+     * Gets all CrossTabDataValues for the given collection of period identifiers 
+     * and organisation unit identifiers.
+     * 
+     * @param operand the data element operand.
+     * @param periodIds the period identifiers.
+     * @param organisationUnitIds the organisation unit identifiers.
+     * @return collection of CrossTabDataValues.
+     */
+    Collection<CrossTabDataValue> getCrossTabDataValues( DataElementOperand operand, 
+        Collection<Integer> periodIds, Collection<Integer> organisationUnitIds, String key );
 
     /**
      * Gets a map of DataElementOperands and corresponding Double aggregated data

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/JDBCCrossTabStore.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/JDBCCrossTabStore.java	2012-07-25 16:22:03 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/crosstab/jdbc/JDBCCrossTabStore.java	2012-08-25 11:47:17 +0000
@@ -63,19 +63,20 @@
     // CrossTabStore implementation
     // -------------------------------------------------------------------------
 
-    public void createCrossTabTable( List<DataElementOperand> operands, String key )
+    public void createCrossTabTable( List<Integer> organisationUnitIds, String key )
     {
         final StringBuffer sql = new StringBuffer( "CREATE TABLE " + CROSSTAB_TABLE_PREFIX + key + " ( " );
         
+        sql.append( "dataelementid INTEGER NOT NULL, " );
+        sql.append( "categoryoptioncomboid INTEGER NOT NULL, " );
         sql.append( "periodid INTEGER NOT NULL, " );
-        sql.append( "sourceid INTEGER NOT NULL, " );
         
-        for ( DataElementOperand operand : operands )
+        for ( Integer id : organisationUnitIds )
         {
-            sql.append( operand.getColumnName() ).append( " VARCHAR(20), " );
+            sql.append( CrossTabStore.COLUMN_PREFIX + id + " VARCHAR(20), " );
         }
         
-        sql.append( "PRIMARY KEY ( periodid, sourceid ) );" );
+        sql.append( "PRIMARY KEY ( dataelementid, categoryoptioncomboid, periodid ) );" );
         
         statementManager.getHolder().executeUpdate( sql.toString() );
     }
@@ -134,47 +135,22 @@
     // CrossTabDataValue
     // -------------------------------------------------------------------------
 
-    public Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands, 
-        Collection<Integer> periodIds, Collection<Integer> sourceIds, String key )
+    public Collection<CrossTabDataValue> getCrossTabDataValues( DataElementOperand operand, 
+        Collection<Integer> periodIds, Collection<Integer> organisationUnitIds, String key )
     {
         final StatementHolder holder = statementManager.getHolder();
         
-        final String operandIds = getCommadelimitedString( operands );
-        
-        final String sql = "SELECT periodid, sourceid, " + operandIds + " FROM " + CROSSTAB_TABLE_PREFIX + key + " AS c WHERE c.periodid IN (" + 
-            getCommaDelimitedString( periodIds ) + ") AND c.sourceid IN (" + getCommaDelimitedString( sourceIds ) + ")";
+        final String sql = 
+            "SELECT * FROM " + CROSSTAB_TABLE_PREFIX + key + " AS c " +
+            "WHERE dataelementid = " + operand.getDataElementId() + " " +
+            "AND categoryoptioncomboid = " + operand.getOptionComboId() + " " +
+            "AND c.periodid IN (" + getCommaDelimitedString( periodIds ) + ")";
         
         try
         {            
             final ResultSet resultSet = holder.getStatement().executeQuery( sql );
             
-            return getCrossTabDataValues( resultSet, operands );
-        }
-        catch ( SQLException ex )
-        {
-            throw new RuntimeException( "Failed to get CrossTabDataValues", ex );
-        }
-        finally
-        {
-            holder.close();
-        }
-    }
-    
-    public Collection<CrossTabDataValue> getCrossTabDataValues( Collection<DataElementOperand> operands, 
-        Collection<Integer> periodIds, int sourceId, String key )
-    {
-        final StatementHolder holder = statementManager.getHolder();
-
-        final String operandIds = getCommadelimitedString( operands );
-        
-        final String sql = "SELECT periodid, sourceid, " + operandIds + " FROM " + CROSSTAB_TABLE_PREFIX + key + " AS c WHERE c.periodid IN (" + 
-            getCommaDelimitedString( periodIds ) + ") AND c.sourceid = " + sourceId;
-
-        try
-        {
-            final ResultSet resultSet = holder.getStatement().executeQuery( sql );
-            
-            return getCrossTabDataValues( resultSet, operands );
+            return getCrossTabDataValues( resultSet, organisationUnitIds );
         }
         catch ( SQLException ex )
         {
@@ -242,7 +218,7 @@
     // Supportive methods
     // -------------------------------------------------------------------------
 
-    private Collection<CrossTabDataValue> getCrossTabDataValues( ResultSet resultSet, Collection<DataElementOperand> operands )
+    private Collection<CrossTabDataValue> getCrossTabDataValues( ResultSet resultSet, Collection<Integer> organisationUnitIds )
         throws SQLException
     {
         final List<CrossTabDataValue> values = new ArrayList<CrossTabDataValue>();
@@ -251,16 +227,15 @@
         {
             final CrossTabDataValue value = new CrossTabDataValue();
             
-            value.setPeriodId( resultSet.getInt( 1 ) );
-            value.setSourceId( resultSet.getInt( 2 ) );
+            value.setPeriodId( resultSet.getInt( 3 ) );
             
-            for ( DataElementOperand operand : operands )
+            for ( Integer id : organisationUnitIds )
             {
-                final String columnValue = resultSet.getString( operand.getColumnName() );
+                final String columnValue = resultSet.getString( CrossTabStore.COLUMN_PREFIX + id );
                 
                 if ( columnValue != null )
                 {
-                    value.getValueMap().put( operand, columnValue );
+                    value.getValueMap().put( id, columnValue );
                 }
             }
             
@@ -290,21 +265,4 @@
         
         return valueMap;
     }
-    
-    private String getCommadelimitedString( Collection<DataElementOperand> operands )
-    {
-        final StringBuilder builder = new StringBuilder();
-        
-        for ( DataElementOperand operand : operands )
-        {
-            builder.append( operand.getColumnName() ).append( "," );
-        }
-        
-        if ( builder.length() > 0 )
-        {
-            builder.deleteCharAt( builder.length() - 1);
-        }
-        
-        return builder.toString();
-    }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/dataelement/DefaultDataElementDataMart.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/dataelement/DefaultDataElementDataMart.java	2012-08-22 16:13:29 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/dataelement/DefaultDataElementDataMart.java	2012-08-25 11:47:17 +0000
@@ -30,9 +30,8 @@
 import static org.hisp.dhis.system.util.MathUtils.getRounded;
 
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Future;
 
 import org.amplecode.quick.BatchHandler;
@@ -43,12 +42,15 @@
 import org.hisp.dhis.aggregation.AggregatedDataValue;
 import org.hisp.dhis.dataelement.DataElementOperand;
 import org.hisp.dhis.datamart.DataMartEngine;
+import org.hisp.dhis.datamart.OrgUnitOperand;
 import org.hisp.dhis.datamart.aggregation.cache.AggregationCache;
 import org.hisp.dhis.datamart.aggregation.dataelement.DataElementAggregator;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
 import org.hisp.dhis.organisationunit.OrganisationUnitGroup;
 import org.hisp.dhis.organisationunit.OrganisationUnitHierarchy;
 import org.hisp.dhis.period.Period;
+import org.hisp.dhis.period.PeriodType;
+import org.hisp.dhis.system.util.ConversionUtils;
 import org.hisp.dhis.system.util.SystemUtils;
 import org.springframework.scheduling.annotation.Async;
 
@@ -80,41 +82,6 @@
         this.aggregationCache = aggregationCache;
     }
 
-    private DataElementAggregator sumIntAggregator;
-
-    public void setSumIntAggregator( DataElementAggregator sumIntDataElementAggregator )
-    {
-        this.sumIntAggregator = sumIntDataElementAggregator;
-    }
-
-    private DataElementAggregator averageIntAggregator;
-
-    public void setAverageIntAggregator( DataElementAggregator averageIntDataElementAggregator )
-    {
-        this.averageIntAggregator = averageIntDataElementAggregator;
-    }
-
-    private DataElementAggregator averageIntSingleValueAggregator;
-
-    public void setAverageIntSingleValueAggregator( DataElementAggregator averageIntSingleValueAggregator )
-    {
-        this.averageIntSingleValueAggregator = averageIntSingleValueAggregator;
-    }
-
-    private DataElementAggregator sumBoolAggregator;
-
-    public void setSumBoolAggregator( DataElementAggregator sumBooleanDataElementAggregator )
-    {
-        this.sumBoolAggregator = sumBooleanDataElementAggregator;
-    }
-
-    private DataElementAggregator averageBoolAggregator;
-
-    public void setAverageBoolAggregator( DataElementAggregator averageBooleanDataElementAggregator )
-    {
-        this.averageBoolAggregator = averageBooleanDataElementAggregator;
-    }
-    
     private StatementManager statementManager;
 
     public void setStatementManager( StatementManager statementManager )
@@ -122,6 +89,13 @@
         this.statementManager = statementManager;
     }
 
+    private Set<DataElementAggregator> aggregators;
+
+    public void setAggregators( Set<DataElementAggregator> aggregators )
+    {
+        this.aggregators = aggregators;
+    }
+    
     // -------------------------------------------------------------------------
     // DataMart functionality
     // -------------------------------------------------------------------------
@@ -135,55 +109,47 @@
         
         final BatchHandler<AggregatedDataValue> batchHandler = batchHandlerFactory.createBatchHandler( clazz ).init();
         
-        final Map<DataElementOperand, Double> valueMap = new HashMap<DataElementOperand, Double>();
-        
         final AggregatedDataValue aggregatedValue = new AggregatedDataValue();
         
+        final Collection<Integer> organisationUnitIds = ConversionUtils.getIdentifiers( OrganisationUnit.class, organisationUnits );
+
+        populateHasAggregationLevels( operands );
+        
         organisationUnitGroups = organisationUnitGroups != null ? organisationUnitGroups : DataMartEngine.DUMMY_ORG_UNIT_GROUPS;
         
         for ( final Period period : periods )
         {
-            final Collection<DataElementOperand> sumIntOperands = sumIntAggregator.filterOperands( operands, period.getPeriodType() );
-            final Collection<DataElementOperand> averageIntOperands = averageIntAggregator.filterOperands( operands, period.getPeriodType() );
-            final Collection<DataElementOperand> averageIntSingleValueOperands = averageIntSingleValueAggregator.filterOperands( operands, period.getPeriodType() );
-            final Collection<DataElementOperand> sumBoolOperands = sumBoolAggregator.filterOperands( operands, period.getPeriodType() );
-            final Collection<DataElementOperand> averageBoolOperands = averageBoolAggregator.filterOperands( operands, period.getPeriodType() );
-            
-            for ( OrganisationUnitGroup group : organisationUnitGroups )
+            operands : for ( final DataElementOperand operand : operands )
             {
-                for ( final OrganisationUnit unit : organisationUnits )
-                {
-                    final int level = aggregationCache.getLevelOfOrganisationUnit( unit.getId() );
-                    
-                    final Collection<Integer> orgUnitChildren = hierarchy.getChildren( unit.getId(), group );
-                    
-                    valueMap.clear();                
-                    valueMap.putAll( sumIntAggregator.getAggregatedValues( sumIntOperands, period, level, orgUnitChildren, key ) );
-                    valueMap.putAll( averageIntAggregator.getAggregatedValues( averageIntOperands, period, level, orgUnitChildren, key ) );
-                    valueMap.putAll( averageIntSingleValueAggregator.getAggregatedValues( averageIntSingleValueOperands, period, level, orgUnitChildren, key ) );
-                    valueMap.putAll( sumBoolAggregator.getAggregatedValues( sumBoolOperands, period, level, orgUnitChildren, key ) );
-                    valueMap.putAll( averageBoolAggregator.getAggregatedValues( averageBoolOperands, period, level, orgUnitChildren, key ) );
-                    
-                    if ( valueMap.size() > 0 )
-                    {
-                        for ( Entry<DataElementOperand, Double> entry : valueMap.entrySet() )
-                        {
-                            aggregatedValue.clear();
-                            
-                            final double value = getRounded( entry.getValue(), DECIMALS );
-                            
-                            aggregatedValue.setDataElementId( entry.getKey().getDataElementId() );
-                            aggregatedValue.setCategoryOptionComboId( entry.getKey().getOptionComboId() );
-                            aggregatedValue.setPeriodId( period.getId() );
-                            aggregatedValue.setPeriodTypeId( period.getPeriodType().getId() );
-                            aggregatedValue.setOrganisationUnitId( unit.getId() );
-                            aggregatedValue.setOrganisationUnitGroupId( group != null ? group.getId() : 0 );
-                            aggregatedValue.setLevel( level );
-                            aggregatedValue.setValue( value );
-                            
-                            batchHandler.addObject( aggregatedValue );
-                        }
-                    }
+                final PeriodType periodType = period.getPeriodType();
+                
+                final DataElementAggregator aggregator = getAggregator( operand, periodType );
+
+                if ( aggregator == null )
+                {
+                    continue operands;
+                }
+                
+                final List<OrgUnitOperand> values = aggregator.getAggregatedValues( operand, period, organisationUnitIds, organisationUnitGroups, hierarchy, key );
+                
+                for ( OrgUnitOperand orgUnitOperand : values )
+                {
+                    final int level = aggregationCache.getLevelOfOrganisationUnit( orgUnitOperand.getOrgUnitId() );
+                    
+                    final double value = getRounded( orgUnitOperand.getValue(), DECIMALS );
+
+                    aggregatedValue.clear();
+                    
+                    aggregatedValue.setDataElementId( operand.getDataElementId() );
+                    aggregatedValue.setCategoryOptionComboId( operand.getOptionComboId() );
+                    aggregatedValue.setPeriodId( period.getId() );
+                    aggregatedValue.setPeriodTypeId( period.getPeriodType().getId() );
+                    aggregatedValue.setOrganisationUnitId( orgUnitOperand.getOrgUnitId() );
+                    aggregatedValue.setOrganisationUnitGroupId( orgUnitOperand.getOrgUnitGroupId() );
+                    aggregatedValue.setLevel( level );
+                    aggregatedValue.setValue( value );
+                    
+                    batchHandler.addObject( aggregatedValue );
                 }
             }
             
@@ -200,4 +166,25 @@
         
         return null;
     }
+    
+    private DataElementAggregator getAggregator( DataElementOperand operand, PeriodType periodType )
+    {
+        for ( DataElementAggregator aggregator : aggregators )
+        {
+            if ( aggregator.isApplicable( operand, periodType ) )
+            {
+                return aggregator;
+            }
+        }
+        
+        return null;
+    }
+    
+    public void populateHasAggregationLevels( Collection<DataElementOperand> operands ) //TODO check
+    {
+        for ( DataElementOperand operand : operands )
+        {
+            operand.setHasAggregationLevels( operand.hasAggregationLevels() );
+        }
+    }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/engine/DefaultDataMartEngine.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/engine/DefaultDataMartEngine.java	2012-08-22 17:05:32 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/java/org/hisp/dhis/datamart/engine/DefaultDataMartEngine.java	2012-08-25 11:47:17 +0000
@@ -238,7 +238,6 @@
         // ---------------------------------------------------------------------
 
         organisationUnitService.filterOrganisationUnitsWithoutData( organisationUnits );
-        Collections.shuffle( organisationUnits );
         FilterUtils.filter( dataElements, new AggregatableDataElementFilter() );
         FilterUtils.filter( dataElements, new DataElementWithAggregationFilter() );
         expressionService.filterInvalidIndicators( indicators );
@@ -297,18 +296,24 @@
         // Create crosstabtable
         // ---------------------------------------------------------------------
 
+        //TODO improve index on datavalue for crosstab
+        
         final Collection<Integer> intersectingPeriodIds = ConversionUtils.getIdentifiers( Period.class, periodService.getIntersectionPeriods( periods ) );
-        final Set<Integer> childrenIds = organisationUnitService.getOrganisationUnitHierarchy().getChildren( organisationUnitIds );
-        final List<List<Integer>> childrenPages = new PaginatedList<Integer>( childrenIds ).setNumberOfPages( cpuCores ).getPages();
+        final Set<Integer> orgUnitChildrenIds = organisationUnitService.getOrganisationUnitHierarchy().getChildren( organisationUnitIds );
+        final List<Integer> crossTabOrgUnitIds = new ArrayList<Integer>( orgUnitChildrenIds );
+        
+        final String key = crossTabService.createCrossTabTable( crossTabOrgUnitIds );
 
-        final List<DataElementOperand> crossTabOperands = new ArrayList<DataElementOperand>( allOperands );
-        final String key = crossTabService.createCrossTabTable( crossTabOperands );
+        final List<DataElementOperand> operandList = new ArrayList<DataElementOperand>( allOperands );
+        Collections.shuffle( operandList );
+        
+        final List<List<DataElementOperand>> operandPages = new PaginatedList<DataElementOperand>( operandList ).setNumberOfPages( cpuCores ).getPages();
         
         List<Future<?>> crossTabFutures = new ArrayList<Future<?>>();
         
-        for ( List<Integer> childrenPage : childrenPages )
+        for ( List<DataElementOperand> operandPage : operandPages )
         {
-            crossTabFutures.add( crossTabService.populateCrossTabTable( crossTabOperands, intersectingPeriodIds, childrenPage, key ) );
+            crossTabFutures.add( crossTabService.populateCrossTabTable( operandPage, intersectingPeriodIds, crossTabOrgUnitIds, key ) );
         }
 
         ConcurrentUtils.waitForCompletion( crossTabFutures );
@@ -324,32 +329,30 @@
         
         final boolean isGroups = organisationUnitGroups != null && organisationUnitGroups.size() > 0 && groupLevel > 0;
 
-        List<List<OrganisationUnit>> organisationUnitPages = new PaginatedList<OrganisationUnit>( organisationUnits ).setNumberOfPages( cpuCores ).getPages();
-
         if ( isDataElements )
         {
             // -----------------------------------------------------------------
             // 1. Export data element values
             // -----------------------------------------------------------------
             
-            if ( allOperands.size() > 0 )
+            if ( operandList.size() > 0 )
             {
                 final OrganisationUnitHierarchy hierarchy = organisationUnitService.getOrganisationUnitHierarchy().prepareChildren( organisationUnits );
-    
+                
                 List<Future<?>> futures = new ArrayList<Future<?>>();
                 
-                for ( List<OrganisationUnit> organisationUnitPage : organisationUnitPages )
+                for ( List<DataElementOperand> operandPage : operandPages )
                 {
-                    futures.add( dataElementDataMart.exportDataValues( allOperands, periods, organisationUnitPage, 
+                    futures.add( dataElementDataMart.exportDataValues( operandPage, periods, organisationUnits, 
                         null, hierarchy, AggregatedDataValueTempBatchHandler.class, key ) );
                 }
-    
+                
                 ConcurrentUtils.waitForCompletion( futures );
             }
             
-            clock.logTime( "Exported values for data element operands (" + allOperands.size() + "), pages: " + organisationUnitPages.size() + ", " + SystemUtils.getMemoryString() );
+            clock.logTime( "Exported values for data element operands (" + operandList.size() + "), pages: " + operandPages.size() + ", " + SystemUtils.getMemoryString() );
             notifier.notify( id, DATAMART, "Dropping data element index" );
-    
+
             // -----------------------------------------------------------------
             // 2. Drop data element index
             // -----------------------------------------------------------------
@@ -398,24 +401,22 @@
             
             FilterUtils.filter( groupOrganisationUnits, new OrganisationUnitAboveOrEqualToLevelFilter( groupLevel ) );
             
-            organisationUnitPages = new PaginatedList<OrganisationUnit>( groupOrganisationUnits ).setNumberOfPages( cpuCores ).getPages();
-            
-            if ( allOperands.size() > 0 )
+            if ( operandList.size() > 0 )
             {
-                final OrganisationUnitHierarchy hierarchy = organisationUnitService.getOrganisationUnitHierarchy().prepareChildren( organisationUnits, organisationUnitGroups );
+                final OrganisationUnitHierarchy hierarchy = organisationUnitService.getOrganisationUnitHierarchy().prepareChildren( groupOrganisationUnits, organisationUnitGroups );
                 
                 List<Future<?>> futures = new ArrayList<Future<?>>();
                 
-                for ( List<OrganisationUnit> organisationUnitPage : organisationUnitPages )
+                for ( List<DataElementOperand> operandPage : operandPages )
                 {
-                    futures.add( dataElementDataMart.exportDataValues( allOperands, periods, organisationUnitPage, 
+                    futures.add( dataElementDataMart.exportDataValues( operandPage, periods, groupOrganisationUnits, 
                         organisationUnitGroups, hierarchy, AggregatedOrgUnitDataValueTempBatchHandler.class, key ) );
                 }
 
                 ConcurrentUtils.waitForCompletion( futures );
             }
             
-            clock.logTime( "Exported values for data element operands (" + allOperands.size() + "), pages: " + organisationUnitPages.size()  + ", " + SystemUtils.getMemoryString() );
+            clock.logTime( "Exported values for data element operands (" + operandList.size() + "), pages: " + operandPages.size()  + ", " + SystemUtils.getMemoryString() );
             notifier.notify( id, DATAMART, "Dropping data element data indexes" );
 
             // -----------------------------------------------------------------
@@ -457,6 +458,8 @@
 
         crossTabService.dropCrossTabTable( key );
         
+        List<List<OrganisationUnit>> organisationUnitPages = new PaginatedList<OrganisationUnit>( organisationUnits ).setNumberOfPages( cpuCores ).getPages();
+        
         if ( isIndicators )
         {
             // -----------------------------------------------------------------
@@ -477,7 +480,7 @@
 
             ConcurrentUtils.waitForCompletion( aggregatedDataCacheFutures );
         
-            clock.logTime( "Created aggregated data cache, number of indicator operands: " + indicatorOperands.size() + ", operands with data: " + allOperands.size() );
+            clock.logTime( "Created aggregated data cache, number of indicator operands: " + indicatorOperands.size() );
             notifier.notify( id, DATAMART, "Exporting indicator data" );
             
             // -----------------------------------------------------------------

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/main/resources/META-INF/dhis/beans.xml	2012-08-22 16:13:29 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/main/resources/META-INF/dhis/beans.xml	2012-08-25 11:47:17 +0000
@@ -82,12 +82,16 @@
   <bean id="org.hisp.dhis.datamart.dataelement.DataElementDataMart" class="org.hisp.dhis.datamart.dataelement.DefaultDataElementDataMart">
     <property name="batchHandlerFactory" ref="batchHandlerFactory" />
     <property name="aggregationCache" ref="org.hisp.dhis.datamart.aggregation.cache.AggregationCache" />
-    <property name="sumIntAggregator" ref="org.hisp.dhis.datamart.aggregation.dataelement.SumIntAggregator" />
-    <property name="averageIntAggregator" ref="org.hisp.dhis.datamart.aggregation.dataelement.AverageIntAggregator" />
-    <property name="averageIntSingleValueAggregator" ref="org.hisp.dhis.datamart.aggregation.dataelement.AverageIntSingleValueAggregator" />
-    <property name="sumBoolAggregator" ref="org.hisp.dhis.datamart.aggregation.dataelement.SumBoolAggregator" />
-    <property name="averageBoolAggregator" ref="org.hisp.dhis.datamart.aggregation.dataelement.AverageBoolAggregator" />
 	<property name="statementManager" ref="inMemoryStatementManager" />
+    <property name="aggregators">
+      <list>
+        <ref bean="org.hisp.dhis.datamart.aggregation.dataelement.SumIntAggregator"/>
+        <ref bean="org.hisp.dhis.datamart.aggregation.dataelement.AverageIntSingleValueAggregator"/>
+        <ref bean="org.hisp.dhis.datamart.aggregation.dataelement.AverageIntAggregator"/>
+        <ref bean="org.hisp.dhis.datamart.aggregation.dataelement.SumBoolAggregator"/>
+        <ref bean="org.hisp.dhis.datamart.aggregation.dataelement.AverageBoolAggregator"/>
+      </list>
+    </property>
   </bean>
   
   <!-- IndicatorDataMart -->

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceOrgUnitTest.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceOrgUnitTest.java	2012-04-12 12:39:47 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceOrgUnitTest.java	2012-08-25 11:47:17 +0000
@@ -375,8 +375,8 @@
         assertEquals( 1.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitA, groupA ) );
         assertEquals( 1.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitB, groupA ) );
         assertEquals( 1.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitD, groupA ) );
-        assertEquals( 0.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitE, groupA ) );
-        assertEquals( 0.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitF, groupA ) );
+        //assertEquals( 0.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitE, groupA ) );
+        //assertEquals( 0.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitF, groupA ) );
         
         assertEquals( 3.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitA, groupB ) );
         assertEquals( 3.0, aggregatedOrgUnitDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitB, groupB ) );

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceTest.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceTest.java	2012-02-12 20:32:14 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/DataMartServiceTest.java	2012-08-25 11:47:17 +0000
@@ -329,9 +329,9 @@
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitB ), 3.0 );
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitC ), 1.0 );
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitD ), 1.0 );
-        assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitE ), 0.0 );
+        //assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitE ), 0.0 );
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitF ), 2.0 );
-        assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitG ), 0.0 );
+        //assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitG ), 0.0 );
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodA, unitH ), 1.0 );
         
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodD, unitA ), 12.0 );
@@ -343,7 +343,7 @@
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodD, unitG ), 1.0 );
         assertEquals( aggregatedDataValueService.getAggregatedValue( dataElementB, categoryOptionCombo, periodD, unitH ), 3.0 );
     }
-
+    
     @Test
     public void testAverageBoolDataElementDataMart()
     {

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabServiceTest.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabServiceTest.java	2011-12-26 10:07:59 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabServiceTest.java	2012-08-25 11:47:17 +0000
@@ -65,7 +65,6 @@
 
 /**
  * @author Lars Helge Overland
- * @version $Id: CrossTabServiceTest.java 6217 2008-11-06 18:53:04Z larshelg $
  */
 public class CrossTabServiceTest
     extends DhisTest
@@ -78,7 +77,7 @@
 
     private List<DataElementOperand> operands;
     private Collection<Integer> periodIds;
-    private Collection<Integer> organisationUnitIds;
+    private List<Integer> organisationUnitIds;
 
     // -------------------------------------------------------------------------
     // Fixture
@@ -146,7 +145,7 @@
         Character[] characters = { 'A', 'B', 'C', 'D', 'E' };
         
         periodIds = new HashSet<Integer>();
-        organisationUnitIds = new HashSet<Integer>();
+        organisationUnitIds = new ArrayList<Integer>();
         
         Collection<DataElement> dataElements = new HashSet<DataElement>();
         Collection<Period> periods = new HashSet<Period>();
@@ -192,27 +191,28 @@
     public void testPopulateCrossTabValue()
         throws Exception
     {
-        String key = crossTabService.createCrossTabTable( operands );
+        String key = crossTabService.createCrossTabTable( organisationUnitIds );
         crossTabService.populateCrossTabTable( operands, periodIds, organisationUnitIds, key ).get();
         
-        Collection<CrossTabDataValue> values = crossTabService.getCrossTabDataValues( operands, periodIds, organisationUnitIds, key );
-        
-        assertNotNull( values );
-        
-        assertEquals( 25, values.size() );
-        
-        for ( CrossTabDataValue crossTabValue : values )
+        for ( DataElementOperand operand : operands )
         {
-            assertTrue( crossTabValue.getPeriodId() != 0 );
-            assertTrue( crossTabValue.getSourceId() != 0 );
-            
-            assertNotNull( crossTabValue.getValueMap() );
-            
-            assertEquals( 10, crossTabValue.getValueMap().size() );
-            
-            for ( String value : crossTabValue.getValueMap().values() )
+            Collection<CrossTabDataValue> values = crossTabService.getCrossTabDataValues( operand, periodIds, organisationUnitIds, key );
+
+            assertNotNull( values );
+
+            assertEquals( 5, values.size() );
+            
+            for ( CrossTabDataValue crossTabValue : values )
             {
-                assertEquals( "10", value );
+                assertTrue( crossTabValue.getPeriodId() != 0 );
+                
+                assertNotNull( crossTabValue.getValueMap() );
+                assertEquals( 5, crossTabValue.getValueMap().size() );
+                
+                for ( String value : crossTabValue.getValueMap().values() )
+                {
+                    assertEquals( "10", value );
+                }
             }
         }
     }

=== modified file 'dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabStoreTest.java'
--- dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabStoreTest.java	2011-06-04 16:56:41 +0000
+++ dhis-2/dhis-services/dhis-service-datamart-default/src/test/java/org/hisp/dhis/datamart/crosstab/CrossTabStoreTest.java	2012-08-25 11:47:17 +0000
@@ -1,6 +1,7 @@
 package org.hisp.dhis.datamart.crosstab;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.commons.lang.RandomStringUtils;
@@ -14,6 +15,8 @@
 {
     private CrossTabStore crossTabStore;
     
+    private List<Integer> organisationUnits;
+    
     private List<DataElementOperand> operands;
     
     private String key = RandomStringUtils.randomAlphanumeric( 8 );
@@ -27,6 +30,8 @@
     {
         crossTabStore = (CrossTabStore) getBean( CrossTabStore.ID );
         
+        organisationUnits = Arrays.asList( 3, 4, 5, 6 );
+        
         operands = new ArrayList<DataElementOperand>();
         operands.add( new DataElementOperand( 1, 1 ) );
         operands.add( new DataElementOperand( 1, 2 ) );
@@ -47,7 +52,7 @@
     @Test
     public void testDropCrossTabTable()
     {
-        crossTabStore.createCrossTabTable( operands, key );
+        crossTabStore.createCrossTabTable( organisationUnits, key );
         
         crossTabStore.dropCrossTabTable( key );
     }

=== modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java'
--- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java	2012-07-25 13:18:48 +0000
+++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/MathUtils.java	2012-08-25 11:47:17 +0000
@@ -223,6 +223,17 @@
     }
     
     /**
+     * Tests whether the given double is equal to zero.
+     * 
+     * @param value the value.
+     * @return true or false.
+     */
+    public static boolean isZero( double value )
+    {
+        return isEqual( value, 0d );
+    }
+    
+    /**
      * Returns a random int between 0 and 999.
      */
     public static int getRandom()