← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 11673: Report table and chart, supporting multiple number of org unit levels and groups

 

------------------------------------------------------------
revno: 11673
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2013-08-15 13:30:31 +0200
message:
  Report table and chart, supporting multiple number of org unit levels and groups
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/Chart.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/BaseAnalyticalObject.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitLevelStore.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultDimensionService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/common/DimensionServiceTest.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml
  dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml
  dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/reporttable/ReportTableTest.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/chart/Chart.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/Chart.java	2013-07-04 15:41:57 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/chart/Chart.java	2013-08-15 11:30:31 +0000
@@ -117,6 +117,8 @@
     
     private transient List<OrganisationUnit> organisationUnitsAtLevel = new ArrayList<OrganisationUnit>();
 
+    private transient List<OrganisationUnit> organisationUnitsInGroups = new ArrayList<OrganisationUnit>();
+
     // -------------------------------------------------------------------------
     // Constructors
     // -------------------------------------------------------------------------
@@ -135,12 +137,14 @@
     // -------------------------------------------------------------------------
 
     @Override
-    public void init( User user, Date date, OrganisationUnit organisationUnit, List<OrganisationUnit> organisationUnitsAtLevel, I18nFormat format )
+    public void init( User user, Date date, OrganisationUnit organisationUnit, 
+        List<OrganisationUnit> organisationUnitsAtLevel, List<OrganisationUnit> organisationUnitsInGroups, I18nFormat format )
     {
         this.user = user;
         this.relativePeriodDate = date;
         this.relativeOrganisationUnit = organisationUnit;
         this.organisationUnitsAtLevel = organisationUnitsAtLevel;
+        this.organisationUnitsInGroups = organisationUnitsInGroups;
         this.format = format;        
     }
     
@@ -150,14 +154,14 @@
 
     public List<NameableObject> series()
     {
-        DimensionalObject object = getDimensionalObject( series, relativePeriodDate, user, true, organisationUnitsAtLevel, format );
+        DimensionalObject object = getDimensionalObject( series, relativePeriodDate, user, true, organisationUnitsAtLevel, organisationUnitsInGroups, format );
         
         return object != null ? object.getItems() : null;
     }
 
     public List<NameableObject> category()
     {
-        DimensionalObject object = getDimensionalObject( category, relativePeriodDate, user, true, organisationUnitsAtLevel, format );
+        DimensionalObject object = getDimensionalObject( category, relativePeriodDate, user, true, organisationUnitsAtLevel, organisationUnitsInGroups, format );
         
         return object != null ? object.getItems() : null;
     }
@@ -168,7 +172,7 @@
         
         for ( String filter : filterDimensions )
         {
-            DimensionalObject object = getDimensionalObject( filter, relativePeriodDate, user, true, organisationUnitsAtLevel, format );
+            DimensionalObject object = getDimensionalObject( filter, relativePeriodDate, user, true, organisationUnitsAtLevel, organisationUnitsInGroups, format );
             
             if ( object != null )
             {

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/BaseAnalyticalObject.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/BaseAnalyticalObject.java	2013-08-15 09:32:41 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/BaseAnalyticalObject.java	2013-08-15 11:30:31 +0000
@@ -27,19 +27,19 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import static org.hisp.dhis.common.DimensionalObject.CATEGORYOPTIONCOMBO_DIM_ID;
 import static org.hisp.dhis.common.DimensionalObject.DATAELEMENT_DIM_ID;
 import static org.hisp.dhis.common.DimensionalObject.DATAELEMENT_OPERAND_ID;
 import static org.hisp.dhis.common.DimensionalObject.DATASET_DIM_ID;
 import static org.hisp.dhis.common.DimensionalObject.DATA_X_DIM_ID;
+import static org.hisp.dhis.common.DimensionalObject.DIMENSION_SEP;
 import static org.hisp.dhis.common.DimensionalObject.INDICATOR_DIM_ID;
 import static org.hisp.dhis.common.DimensionalObject.ORGUNIT_DIM_ID;
 import static org.hisp.dhis.common.DimensionalObject.PERIOD_DIM_ID;
-import static org.hisp.dhis.common.DimensionalObject.CATEGORYOPTIONCOMBO_DIM_ID;
-import static org.hisp.dhis.common.DimensionalObject.DIMENSION_SEP;
+import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_LEVEL;
+import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_ORGUNIT_GROUP;
 import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_USER_ORGUNIT;
 import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_USER_ORGUNIT_CHILDREN;
-import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_LEVEL;
-import static org.hisp.dhis.organisationunit.OrganisationUnit.KEY_ORGUNIT_GROUP;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -47,10 +47,8 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 import org.hisp.dhis.common.adapter.JacksonPeriodDeserializer;
@@ -134,14 +132,10 @@
 
     protected boolean userOrganisationUnitChildren;
 
-    /**
-     * Level of dimension item organisation units.
-     */
-    protected Integer organisationUnitLevel;
-    
-    /**
-     * Groups containing dimension item organisation units.
-     */
+    @Scanned
+    protected List<Integer> organisationUnitLevels = new ArrayList<Integer>();
+
+    @Scanned
     protected List<OrganisationUnitGroup> itemOrganisationUnitGroups = new ArrayList<OrganisationUnitGroup>();
     
     protected boolean rewindRelativePeriods;
@@ -174,7 +168,8 @@
     // Logic
     // -------------------------------------------------------------------------
 
-    public abstract void init( User user, Date date, OrganisationUnit organisationUnit, List<OrganisationUnit> organisationUnitsAtLevel, I18nFormat format );
+    public abstract void init( User user, Date date, OrganisationUnit organisationUnit, 
+        List<OrganisationUnit> organisationUnitsAtLevel, List<OrganisationUnit> organisationUnitsInGroups, I18nFormat format );
     
     public abstract void populateAnalyticalProperties();
     
@@ -188,6 +183,16 @@
         return relatives != null && !relatives.isEmpty();
     }
     
+    public boolean hasOrganisationUnitLevels()
+    {
+        return organisationUnitLevels != null && !organisationUnitLevels.isEmpty();
+    }
+    
+    public boolean hasItemOrganisationUnitGroups()
+    {
+        return itemOrganisationUnitGroups != null && !itemOrganisationUnitGroups.isEmpty();
+    }
+    
     protected void addTransientOrganisationUnits( Collection<OrganisationUnit> organisationUnits )
     {
         if ( organisationUnits != null )
@@ -203,19 +208,7 @@
             this.transientOrganisationUnits.add( organisationUnit );
         }
     }
-    
-    protected Set<OrganisationUnit> getOrganisationUnitsInItemGroups()
-    {
-        Set<OrganisationUnit> units = new HashSet<OrganisationUnit>();
-        
-        for ( OrganisationUnitGroup group : itemOrganisationUnitGroups )
-        {
-            units.addAll( group.getMembers() );
-        }
-        
-        return units;
-    }
-    
+        
     /**
      * Assembles a DimensionalObject. Collapses indicators, data elements, data
      * element operands and data sets into the dx dimension.
@@ -230,7 +223,8 @@
      * @param format the I18nFormat.
      * @return a DimensionalObject.
      */
-    protected DimensionalObject getDimensionalObject( String dimension, Date date, User user, boolean dynamicNames, List<OrganisationUnit> organisationUnitsAtLevel, I18nFormat format )
+    protected DimensionalObject getDimensionalObject( String dimension, Date date, User user, boolean dynamicNames, 
+        List<OrganisationUnit> organisationUnitsAtLevel, List<OrganisationUnit> organisationUnitsInGroups, I18nFormat format )
     {       
         List<NameableObject> items = new ArrayList<NameableObject>();
         
@@ -282,14 +276,14 @@
                 items.addAll( user.getOrganisationUnit().getSortedChildren() );
             }
             
-            if ( organisationUnitLevel != null && organisationUnitsAtLevel != null )
+            if ( organisationUnitLevels != null && !organisationUnitLevels.isEmpty() && organisationUnitsAtLevel != null )
             {
-                items.addAll( organisationUnitsAtLevel );
+                items.addAll( organisationUnitsAtLevel ); // Must be set externally
             }
             
             if ( itemOrganisationUnitGroups != null && !itemOrganisationUnitGroups.isEmpty() )
             {
-                items.addAll( getOrganisationUnitsInItemGroups() );
+                items.addAll( organisationUnitGroups ); // Must be set externally
             }
             
             type = DimensionType.ORGANISATIONUNIT;
@@ -425,11 +419,14 @@
                 ouList.add( new BaseNameableObject( KEY_USER_ORGUNIT_CHILDREN, KEY_USER_ORGUNIT_CHILDREN, KEY_USER_ORGUNIT_CHILDREN ) );
             }
             
-            if ( organisationUnitLevel != null )
+            if ( organisationUnitLevels != null && !organisationUnitLevels.isEmpty() )
             {
-                String id = KEY_LEVEL + organisationUnitLevel;
+                for ( Integer level : organisationUnitLevels )
+                {
+                    String id = KEY_LEVEL + level;
                 
-                ouList.add( new BaseNameableObject( id, id, id ) );
+                    ouList.add( new BaseNameableObject( id, id, id ) );
+                }
             }
             
             if ( itemOrganisationUnitGroups != null && !itemOrganisationUnitGroups.isEmpty() )
@@ -606,7 +603,6 @@
             
             userOrganisationUnit = object.isUserOrganisationUnit();
             userOrganisationUnitChildren = object.isUserOrganisationUnitChildren();
-            organisationUnitLevel = object.getOrganisationUnitLevel();
         }
     }
 
@@ -784,15 +780,16 @@
 
     @JsonProperty
     @JsonView( {DetailedView.class, ExportView.class} )
-    @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0)
-    public Integer getOrganisationUnitLevel()
+    @JacksonXmlElementWrapper( localName = "organisationUnitLevels", namespace = DxfNamespaces.DXF_2_0)
+    @JacksonXmlProperty( localName = "organisationUnitLevel", namespace = DxfNamespaces.DXF_2_0)
+    public List<Integer> getOrganisationUnitLevels()
     {
-        return organisationUnitLevel;
+        return organisationUnitLevels;
     }
 
-    public void setOrganisationUnitLevel( Integer organisationUnitLevel )
+    public void setOrganisationUnitLevels( List<Integer> organisationUnitLevels )
     {
-        this.organisationUnitLevel = organisationUnitLevel;
+        this.organisationUnitLevels = organisationUnitLevels;
     }
 
     @JsonProperty

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitLevelStore.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitLevelStore.java	2013-08-12 09:30:23 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitLevelStore.java	2013-08-15 11:30:31 +0000
@@ -29,8 +29,6 @@
 
 import org.hisp.dhis.common.GenericIdentifiableObjectStore;
 
-import java.util.Collection;
-
 /**
  * Defines methods for persisting OrganisationUnitLevels.
  *

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java	2013-08-15 09:32:41 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java	2013-08-15 11:30:31 +0000
@@ -227,16 +227,16 @@
     Collection<OrganisationUnit> getLeafOrganisationUnits( int id );
 
     /**
-     * Returns the intersection of the members of the given OrganisationUnitGroup
+     * Returns the intersection of the members of the given OrganisationUnitGroups
      * and the OrganisationUnits which are children of the given collection of
-     * parents in the hierarchy. If the given parents are null or empty, the
-     * members of the group are returned.
+     * parents in the hierarchy. If the given parent collection is null or empty, 
+     * the members of the group are returned.
      * 
-     * @param group the OrganisationUnitGroup.
+     * @param groups the collection of OrganisationUnitGroups.
      * @param parents the collection of OrganisationUnit parents in the hierarchy.
      * @return collection of OrganisationUnits.
      */
-    Collection<OrganisationUnit> getOrganisationUnits( OrganisationUnitGroup group, Collection<OrganisationUnit> parents );
+    Collection<OrganisationUnit> getOrganisationUnits( Collection<OrganisationUnitGroup> groups, Collection<OrganisationUnit> parents );
     
     /**
      * Returns an OrganisationUnit and all its children.
@@ -296,7 +296,7 @@
      * at the given hierarchical level. The root OrganisationUnits are at level 1.
      * If parent is null, then all OrganisationUnits at the given level are returned.
      *
-     * @param level  the hierarchical level.
+     * @param level the hierarchical level.
      * @param parent the parent unit.
      * @return all OrganisationUnits which are children of the given unit and are
      *         at the given hierarchical level.
@@ -305,6 +305,19 @@
     Collection<OrganisationUnit> getOrganisationUnitsAtLevel( int level, OrganisationUnit parent );
 
     /**
+     * Returns all OrganisationUnits which are children of the given unit and are
+     * at the given hierarchical levels. The root OrganisationUnits are at level 1.
+     * If parent is null, then all OrganisationUnits at the given level are returned.
+     *
+     * @param levels the hierarchical levels.
+     * @param parent the parent unit.
+     * @return all OrganisationUnits which are children of the given unit and are
+     *         at the given hierarchical level.
+     * @throws IllegalArgumentException if the level is illegal.
+     */
+    Collection<OrganisationUnit> getOrganisationUnitsAtLevels( Collection<Integer> levels, Collection<OrganisationUnit> parents );
+    
+    /**
      * Returns all OrganisationUnits which are children of the given units and are
      * at the given hierarchical level. The root OrganisationUnits are at level 1.
      * If parents is null, then all OrganisationUnits at the given level are returned.

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java	2013-07-04 15:41:57 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/reporttable/ReportTable.java	2013-08-15 11:30:31 +0000
@@ -300,7 +300,8 @@
     // -------------------------------------------------------------------------
 
     @Override
-    public void init( User user, Date date, OrganisationUnit organisationUnit, List<OrganisationUnit> organisationUnitsAtLevel, I18nFormat format )
+    public void init( User user, Date date, OrganisationUnit organisationUnit, 
+        List<OrganisationUnit> organisationUnitsAtLevel, List<OrganisationUnit> organisationUnitsInGroups, I18nFormat format )
     {
         verify( ( periods != null && !periods.isEmpty() ) || hasRelativePeriods(), "Must contain periods or relative periods" );
 
@@ -337,14 +338,15 @@
         
         // Populate grid
         
-        this.populateGridColumnsAndRows( date, user, organisationUnitsAtLevel, format );
+        this.populateGridColumnsAndRows( date, user, organisationUnitsAtLevel, organisationUnitsInGroups, format );
     }
     
     // -------------------------------------------------------------------------
     // Public methods
     // -------------------------------------------------------------------------
     
-    public void populateGridColumnsAndRows( Date date, User user, List<OrganisationUnit> organisationUnitsAtLevel, I18nFormat format )
+    public void populateGridColumnsAndRows( Date date, User user, 
+        List<OrganisationUnit> organisationUnitsAtLevel, List<OrganisationUnit> organisationUnitsInGroups, I18nFormat format )
     {
         List<NameableObject[]> tableColumns = new ArrayList<NameableObject[]>();
         List<NameableObject[]> tableRows = new ArrayList<NameableObject[]>();
@@ -352,17 +354,17 @@
         
         for ( String dimension : columnDimensions )
         {
-            tableColumns.add( getDimensionalObject( dimension, date, user, false, organisationUnitsAtLevel, format ).getItems().toArray( IRT ) );
+            tableColumns.add( getDimensionalObject( dimension, date, user, false, organisationUnitsAtLevel, organisationUnitsInGroups, format ).getItems().toArray( IRT ) );
         }
         
         for ( String dimension : rowDimensions )
         {
-            tableRows.add( getDimensionalObject( dimension, date, user, true, organisationUnitsAtLevel, format ).getItems().toArray( IRT ) );
+            tableRows.add( getDimensionalObject( dimension, date, user, true, organisationUnitsAtLevel, organisationUnitsInGroups, format ).getItems().toArray( IRT ) );
         }
         
         for ( String filter : filterDimensions )
         {
-            filterItems.addAll( getDimensionalObject( filter, date, user, true, organisationUnitsAtLevel, format ).getItems() );
+            filterItems.addAll( getDimensionalObject( filter, date, user, true, organisationUnitsAtLevel, organisationUnitsInGroups, format ).getItems() );
         }
 
         gridColumns = new CombinationGenerator<NameableObject>( tableColumns.toArray( IRT2D ) ).getCombinations();

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java	2013-08-15 09:32:41 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java	2013-08-15 11:30:31 +0000
@@ -853,18 +853,12 @@
             
             if ( !levels.isEmpty() )
             {
-                for ( Integer level : levels )
-                {
-                    orgUnits.addAll( organisationUnitService.getOrganisationUnitsAtLevel( level, ousList ) );
-                }
+                orgUnits.addAll( organisationUnitService.getOrganisationUnitsAtLevels( levels, ousList ) );
             }
             
             if ( !groups.isEmpty() )
             {
-                for ( OrganisationUnitGroup group : groups )
-                {                    
-                    orgUnits.addAll( organisationUnitService.getOrganisationUnits( group, ousList ) );
-                }
+                orgUnits.addAll( organisationUnitService.getOrganisationUnits( groups, ousList ) );
             }
             
             if ( levels.isEmpty() && groups.isEmpty() )

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultDimensionService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultDimensionService.java	2013-08-15 09:32:41 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/common/DefaultDimensionService.java	2013-08-15 11:30:31 +0000
@@ -275,8 +275,6 @@
                 }
                 else if ( ORGANISATIONUNIT.equals( type ) )
                 {
-                    List<OrganisationUnit> ous = new ArrayList<OrganisationUnit>();
-                    
                     for ( String ou : uids )
                     {
                         if ( KEY_USER_ORGUNIT.equals( ou ) )
@@ -293,18 +291,18 @@
                             
                             if ( level > 0 )
                             {
-                                object.setOrganisationUnitLevel( level );
+                                object.getOrganisationUnitLevels().add( level );
                             }
                         }
                         else if ( ou != null && ou.startsWith( KEY_ORGUNIT_GROUP ) )
                         {
                             String uid = DimensionalObjectUtils.getUidFromOrgUnitGroupParam( ou );
                             
-                            OrganisationUnitGroup group = null;
+                            OrganisationUnitGroup group = identifiableObjectManager.get( OrganisationUnitGroup.class, uid );
                             
-                            if ( uid != null && ( group = identifiableObjectManager.get( OrganisationUnitGroup.class, uid ) ) != null )
+                            if ( group != null )
                             {
-                                ous.addAll( group.getMembers() );
+                                object.getItemOrganisationUnitGroups().add( group );
                             }
                         }
                         else
@@ -313,12 +311,10 @@
                             
                             if ( unit != null )
                             {
-                                ous.add( unit );
+                                object.getOrganisationUnits().add( unit );
                             }
                         }
                     }
-                    
-                    object.setOrganisationUnits( ous );
                 }
                 else if ( CATEGORY.equals( type ) )
                 {

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java	2013-08-15 09:32:41 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java	2013-08-15 11:30:31 +0000
@@ -271,9 +271,14 @@
         } );
     }
 
-    public Collection<OrganisationUnit> getOrganisationUnits( OrganisationUnitGroup group, Collection<OrganisationUnit> parents )
+    public Collection<OrganisationUnit> getOrganisationUnits( Collection<OrganisationUnitGroup> groups, Collection<OrganisationUnit> parents )
     {
-        Set<OrganisationUnit> members = new HashSet<OrganisationUnit>( group.getMembers() );
+        Set<OrganisationUnit> members = new HashSet<OrganisationUnit>();
+        
+        for ( OrganisationUnitGroup group : groups )
+        {
+            members.addAll( group.getMembers() );
+        }
         
         if ( parents != null && !parents.isEmpty() )
         {
@@ -390,48 +395,59 @@
 
     public Collection<OrganisationUnit> getOrganisationUnitsAtLevel( int level, OrganisationUnit parent )
     {
-        Set<OrganisationUnit> set = new HashSet<OrganisationUnit>();
-        set.add( parent );
+        Set<OrganisationUnit> parents = new HashSet<OrganisationUnit>();
+        parents.add( parent );
         
-        return getOrganisationUnitsAtLevel( level, parent != null ? set : null );
+        return getOrganisationUnitsAtLevel( level, parent != null ? parents : null );
     }
 
     public Collection<OrganisationUnit> getOrganisationUnitsAtLevel( int level, Collection<OrganisationUnit> parents )
     {
-        if ( level < 1 )
-        {
-            throw new IllegalArgumentException( "Level must be greater than zero" );
-        }
+        Set<Integer> levels = new HashSet<Integer>();
+        levels.add( level );
         
+        return getOrganisationUnitsAtLevels( levels, parents );
+    }
+    
+    public Collection<OrganisationUnit> getOrganisationUnitsAtLevels( Collection<Integer> levels, Collection<OrganisationUnit> parents )
+    {
         if ( parents == null || parents.isEmpty() )
         {
             parents = new HashSet<OrganisationUnit>( getRootOrganisationUnits() );
         }
-        
+
         Set<OrganisationUnit> result = new HashSet<OrganisationUnit>();
         
-        for ( OrganisationUnit parent : parents )
+        for ( Integer level : levels )
         {
-            int parentLevel = parent.getOrganisationUnitLevel();
-
-            if ( level < parentLevel )
-            {
-                throw new IllegalArgumentException(
-                    "Level must be greater than or equal to level of parent OrganisationUnit" );
-            }
-
-            if ( level == parentLevel )
-            {
-                result.add( parent );
-            }
-            else
-            {
-                addOrganisationUnitChildrenAtLevel( parent, parentLevel + 1, level, result );
-            }
-
-            for ( OrganisationUnit unit : result )
-            {
-                unit.setLevel( level );
+            if ( level < 1 )
+            {
+                throw new IllegalArgumentException( "Level must be greater than zero" );
+            }
+            
+            for ( OrganisationUnit parent : parents )
+            {
+                int parentLevel = parent.getOrganisationUnitLevel();
+    
+                if ( level < parentLevel )
+                {
+                    throw new IllegalArgumentException(
+                        "Level must be greater than or equal to level of parent OrganisationUnit" );
+                }
+    
+                if ( level == parentLevel )
+                {
+                    result.add( parent );
+                }
+                else
+                {
+                    addOrganisationUnitChildrenAtLevel( parent, parentLevel + 1, level, result );
+                }
+    
+                for ( OrganisationUnit unit : result )
+                {
+                    unit.setLevel( level );
+                }
             }
         }
         

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/common/DimensionServiceTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/common/DimensionServiceTest.java	2013-08-15 09:32:41 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/common/DimensionServiceTest.java	2013-08-15 11:30:31 +0000
@@ -122,7 +122,7 @@
         organisationUnitService.addOrganisationUnit( ouD );
         organisationUnitService.addOrganisationUnit( ouE );
 
-        String level2 = KEY_LEVEL + 2 + DimensionalObject.DIMENSION_SEP + ouA.getUid();
+        String level2 = KEY_LEVEL + 2;
         
         ouUser = new BaseNameableObject( KEY_USER_ORGUNIT, KEY_USER_ORGUNIT, KEY_USER_ORGUNIT );
         ouLevel2 = new BaseNameableObject( level2, level2, level2 );
@@ -197,7 +197,7 @@
         assertEquals( 2, reportTable.getDataElements().size() );
         assertEquals( 1, reportTable.getPeriods().size() );
         assertEquals( 1, reportTable.getOrganisationUnits().size() );
-        assertEquals( Integer.valueOf( 2 ), reportTable.getOrganisationUnitLevel() );
+        assertEquals( Integer.valueOf( 2 ), reportTable.getOrganisationUnitLevels().get( 0 ) );
     }
 
     @Test

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java	2013-08-08 13:54:42 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/chart/impl/DefaultChartService.java	2013-08-15 11:30:31 +0000
@@ -205,14 +205,20 @@
             organisationUnit = user.getOrganisationUnit();
         }
 
-        List<OrganisationUnit> atLevel = new ArrayList<OrganisationUnit>();
-        
-        if ( chart.getOrganisationUnitLevel() != null && chart.getOrganisationUnits() != null )
-        {
-            atLevel.addAll( organisationUnitService.getOrganisationUnitsAtLevel( chart.getOrganisationUnitLevel(), chart.getOrganisationUnits() ) );
-        }
-        
-        chart.init( user, date, organisationUnit, atLevel, format );
+        List<OrganisationUnit> atLevels = new ArrayList<OrganisationUnit>();
+        List<OrganisationUnit> inGroups = new ArrayList<OrganisationUnit>();
+        
+        if ( chart.hasOrganisationUnitLevels() )
+        {
+            atLevels.addAll( organisationUnitService.getOrganisationUnitsAtLevels( chart.getOrganisationUnitLevels(), chart.getOrganisationUnits() ) );
+        }
+        
+        if ( chart.hasItemOrganisationUnitGroups() )
+        {
+            inGroups.addAll( organisationUnitService.getOrganisationUnits( chart.getItemOrganisationUnitGroups(), chart.getOrganisationUnits() ) );
+        }
+        
+        chart.init( user, date, organisationUnit, atLevels, inGroups, format );
 
         return getJFreeChart( chart );
     }

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java	2013-07-23 07:51:07 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/java/org/hisp/dhis/reporttable/impl/DefaultReportTableService.java	2013-08-15 11:30:31 +0000
@@ -111,14 +111,20 @@
                 
         OrganisationUnit organisationUnit = organisationUnitService.getOrganisationUnit( organisationUnitUid );
 
-        List<OrganisationUnit> atLevel = new ArrayList<OrganisationUnit>();
-        
-        if ( reportTable.getOrganisationUnitLevel() != null && reportTable.getOrganisationUnits() != null )
-        {
-            atLevel.addAll( organisationUnitService.getOrganisationUnitsAtLevel( reportTable.getOrganisationUnitLevel(), reportTable.getOrganisationUnits() ) );
-        }
-        
-        reportTable.init( currentUserService.getCurrentUser(), reportingPeriod, organisationUnit, atLevel, format );
+        List<OrganisationUnit> atLevels = new ArrayList<OrganisationUnit>();
+        List<OrganisationUnit> inGroups = new ArrayList<OrganisationUnit>();
+        
+        if ( reportTable.hasOrganisationUnitLevels() )
+        {
+            atLevels.addAll( organisationUnitService.getOrganisationUnitsAtLevels( reportTable.getOrganisationUnitLevels(), reportTable.getOrganisationUnits() ) );
+        }
+        
+        if ( reportTable.hasItemOrganisationUnitGroups() )
+        {
+            inGroups.addAll( organisationUnitService.getOrganisationUnits( reportTable.getItemOrganisationUnitGroups(), reportTable.getOrganisationUnits() ) );
+        }
+        
+        reportTable.init( currentUserService.getCurrentUser(), reportingPeriod, organisationUnit, atLevels, inGroups, format );
 
         Map<String, Double> valueMap = analyticsService.getAggregatedDataValueMapping( reportTable, format );
 

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml	2013-08-13 07:18:45 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/chart/hibernate/Chart.hbm.xml	2013-08-15 11:30:31 +0000
@@ -127,6 +127,13 @@
         foreign-key="fk_chart_orgunitgroups_orgunitgroupid" />
     </list>
     
+    <list name="organisationUnitLevels" table="chart_orgunitlevels">
+      <cache usage="read-write" />
+      <key column="chartid" foreign-key="fk_chart_orgunitlevels_chartid" />
+      <list-index column="sort_order" base="0" />
+      <element column="orgunitlevel" type="integer" />
+    </list>
+    
     <list name="itemOrganisationUnitGroups" table="chart_itemorgunitgroups">
       <cache usage="read-write" />
       <key column="chartid" foreign-key="fk_chart_itemorgunitunitgroups_chartid" />
@@ -139,8 +146,6 @@
 
     <property name="userOrganisationUnitChildren" />
 
-    <property name="organisationUnitLevel" />
-
     <property name="showData" />
 
     <property name="rewindRelativePeriods" />

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml'
--- dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml	2013-08-13 07:18:45 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/main/resources/org/hisp/dhis/reporttable/hibernate/ReportTable.hbm.xml	2013-08-15 11:30:31 +0000
@@ -96,6 +96,13 @@
         foreign-key="fk_reporttable_orgunitgroups_orgunitgroupid" />
     </list>
 
+    <list name="organisationUnitLevels" table="reporttable_orgunitlevels">
+      <cache usage="read-write" />
+      <key column="reporttableid" foreign-key="fk_reporttable_orgunitlevels_reporttableid" />
+      <list-index column="sort_order" base="0" />
+      <element column="orgunitlevel" type="integer" />
+    </list>
+
     <list name="itemOrganisationUnitGroups" table="reporttable_itemorgunitgroups">
       <cache usage="read-write" />
       <key column="reporttableid" foreign-key="fk_reporttable_itemorgunitunitgroups_reporttableid" />
@@ -152,8 +159,6 @@
 
     <property name="userOrganisationUnitChildren" />
 
-    <property name="organisationUnitLevel" />
-
     <many-to-one name="legendSet" class="org.hisp.dhis.mapping.MapLegendSet" column="legendsetid"
       foreign-key="fk_reporttable_legendsetid" />
 

=== modified file 'dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/reporttable/ReportTableTest.java'
--- dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/reporttable/ReportTableTest.java	2013-07-04 15:41:57 +0000
+++ dhis-2/dhis-services/dhis-service-reporting/src/test/java/org/hisp/dhis/reporttable/ReportTableTest.java	2013-08-15 11:30:31 +0000
@@ -261,7 +261,7 @@
             new ArrayList<DataElement>(), indicators, new ArrayList<DataSet>(), periods, units, 
             true, true, false, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null, null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -313,7 +313,7 @@
             new ArrayList<DataElement>(), indicators, new ArrayList<DataSet>(), periods, units, 
             false, false, true, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null, null,  null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -363,7 +363,7 @@
             new ArrayList<DataElement>(), indicators, new ArrayList<DataSet>(), periods, units, 
             true, false, true, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -413,7 +413,7 @@
             new ArrayList<DataElement>(), indicators, new ArrayList<DataSet>(), periods, units, 
             true, true, true, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -443,7 +443,7 @@
             new ArrayList<DataElement>(), indicators, new ArrayList<DataSet>(), periods, units, 
             false, false, false, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -473,7 +473,7 @@
             dataElements, new ArrayList<Indicator>(), new ArrayList<DataSet>(), periods, units, 
             true, true, false, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -513,7 +513,7 @@
             dataElements, new ArrayList<Indicator>(), new ArrayList<DataSet>(), periods, units, 
             false, false, true, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -551,7 +551,7 @@
             dataElements, new ArrayList<Indicator>(), new ArrayList<DataSet>(), periods, units, 
             true, false, true, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -591,7 +591,7 @@
             new ArrayList<DataElement>(), new ArrayList<Indicator>(), dataSets, periods, units, 
             true, true, false, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -631,7 +631,7 @@
             new ArrayList<DataElement>(), new ArrayList<Indicator>(), dataSets, periods, units, 
             false, false, true, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();
@@ -669,7 +669,7 @@
             new ArrayList<DataElement>(), new ArrayList<Indicator>(), dataSets, periods, units, 
             true, false, true, relatives, null, "january_2000" );
 
-        reportTable.init( null, null, null, null, i18nFormat );
+        reportTable.init( null, null, null,  null, null, i18nFormat );
 
         List<String> columnDims = reportTable.getColumnDimensions();
         List<String> rowDims = reportTable.getRowDimensions();