← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 20322: Updated std dev and min-max validation to use the path property of organisation unit instead of l...

 

------------------------------------------------------------
revno: 20322
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Wed 2015-09-23 18:56:18 +0200
message:
  Updated std dev and min-max validation to use the path property of organisation unit instead of large in-queries. Improves performance.
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataanalysis/DataAnalysisStore.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataanalysis/MinMaxDataAnalysisService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryOptionCombo.java
  dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/sqlview/SqlViewServiceTest.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/MinMaxOutlierAnalysisService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/StdDevOutlierAnalysisService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/jdbc/JdbcDataAnalysisStore.java
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataanalysis/DataAnalysisStoreTest.java
  dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/objectmapper/DeflatedDataValueNameMinMaxRowMapper.java
  dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetAnalysisAction.java
  dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetFollowupAction.java
  dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/META-INF/dhis/beans.xml


--
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/dataanalysis/DataAnalysisStore.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataanalysis/DataAnalysisStore.java	2015-09-16 14:49:50 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataanalysis/DataAnalysisStore.java	2015-09-23 16:56:18 +0000
@@ -32,7 +32,6 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.hisp.dhis.dataelement.DataElement;
 import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
@@ -53,11 +52,11 @@
      * 
      * @param dataElement the DataElement.
      * @param categoryOptionCombo the DataElementCategoryOptionCombo.
-     * @param organisationUnits the set of OrganisationUnit identifiers.
+     * @param parent the parent OrganisationUnits.
      * @param from the from date for which to include data values.
-     * @return a mapping between organisation unit identifier and its standard deviation.
+     * @return a mapping between OrganisationUnit identifier and its standard deviation.
      */
-    Map<Integer, Double> getStandardDeviation( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, Set<Integer> organisationUnits, Date from );
+    Map<Integer, Double> getStandardDeviation( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, Collection<OrganisationUnit> parents, Date from );
     
     /**
      * Calculates the average of the DataValues registered for the given
@@ -65,11 +64,11 @@
      * 
      * @param dataElement the DataElement.
      * @param categoryOptionCombo the DataElementCategoryOptionCombo.
-     * @param organisationUnits the set of OrganisationUnit identifiers.
+     * @param parent the parent OrganisationUnits.
      * @param from the from date for which to include data values.
-     * @return a mapping between organisation unit identifier and its average data value.
+     * @return a mapping between OrganisationUnit unit identifier and its average data value.
      */
-    Map<Integer, Double> getAverage( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, Set<Integer> organisationUnits, Date from );
+    Map<Integer, Double> getAverage( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, Collection<OrganisationUnit> parents, Date from );
     
     /**
      * Generates a collection of data value violations of min-max predefined values.
@@ -77,12 +76,12 @@
      * @param dataElements the data elements.
      * @param categoryOptionCombos the category option combos.
      * @param periods the periods.
-     * @param organisationUnits the organisation units.
+     * @param parents the parent OrganisationUnit units.
      * @param limit the max limit of violations to return.
      * @return a list of data value violations.
      */
     List<DeflatedDataValue> getMinMaxViolations( Collection<DataElement> dataElements, Collection<DataElementCategoryOptionCombo> categoryOptionCombos,
-        Collection<Period> periods, Collection<OrganisationUnit> organisationUnits, int limit );
+        Collection<Period> periods, Collection<OrganisationUnit> parents, int limit );
     
     /**
      * Returns a collection of DeflatedDataValues for the given input.

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataanalysis/MinMaxDataAnalysisService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataanalysis/MinMaxDataAnalysisService.java	2015-05-06 13:36:35 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataanalysis/MinMaxDataAnalysisService.java	2015-09-23 16:56:18 +0000
@@ -35,7 +35,14 @@
 
 public interface MinMaxDataAnalysisService
     extends DataAnalysisService
-{    
-    void generateMinMaxValues( Collection<OrganisationUnit> organisationUnits,
+{
+    /**
+     * Generate min-max values.
+     * 
+     * @param parents the parent organisation units.
+     * @param dataElements the data elements.
+     * @param stdDevFactor the std dev factor.
+     */
+    void generateMinMaxValues( Collection<OrganisationUnit> parents,
         Collection<DataElement> dataElements, Double stdDevFactor );
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryOptionCombo.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryOptionCombo.java	2015-09-23 12:27:33 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryOptionCombo.java	2015-09-23 16:56:18 +0000
@@ -262,6 +262,7 @@
      * Creates a mapping between the category option combo identifier and name
      * for the given collection of elements.
      */
+    @Deprecated
     public static Map<Integer, String> getCategoryOptionComboMap( Collection<DataElementCategoryOptionCombo> categoryOptionCombos )
     {
         Map<Integer, String> map = new HashMap<>();

=== modified file 'dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/sqlview/SqlViewServiceTest.java'
--- dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/sqlview/SqlViewServiceTest.java	2015-09-14 09:21:13 +0000
+++ dhis-2/dhis-services/dhis-service-administration/src/test/java/org/hisp/dhis/sqlview/SqlViewServiceTest.java	2015-09-23 16:56:18 +0000
@@ -58,7 +58,7 @@
     private String sqlB = "SELECT COUNT(_ous.*) AS so_dem FROM _orgunitstructure AS _ous";
 
     private String sqlC = "SELECT COUNT(_cocn.*) AS so_dem, _icgss.indicatorid AS in_id"
-        + "FROM _indicatorgroupsetstructure AS _icgss, _categoryoptioncomboname AS _cocn "
+        + "FROM _indicatorgroupsetstructure AS _icgss, categoryoptioncombo AS _cocn "
         + "GROUP BY _icgss.indicatorid;";
 
     private String sqlD = "SELECT de.name, dv.sourceid, dv.value, p.startdate "

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/MinMaxOutlierAnalysisService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/MinMaxOutlierAnalysisService.java	2015-09-04 09:29:23 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/MinMaxOutlierAnalysisService.java	2015-09-23 16:56:18 +0000
@@ -28,11 +28,17 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.amplecode.quick.BatchHandler;
 import org.amplecode.quick.BatchHandlerFactory;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.hisp.dhis.common.IdentifiableObjectUtils;
 import org.hisp.dhis.common.ValueType;
 import org.hisp.dhis.commons.filter.Filter;
 import org.hisp.dhis.commons.filter.FilterUtils;
@@ -48,13 +54,6 @@
 import org.hisp.dhis.system.util.MathUtils;
 import org.joda.time.DateTime;
 
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * @author Lars Helge Overland
  */
@@ -95,7 +94,7 @@
     // -------------------------------------------------------------------------
 
     @Override
-    public List<DeflatedDataValue> analyse( Collection<OrganisationUnit> organisationUnits,
+    public List<DeflatedDataValue> analyse( Collection<OrganisationUnit> parents,
         Collection<DataElement> dataElements, Collection<Period> periods, Double stdDevFactor, Date from )
     {
         Set<DataElement> elements = new HashSet<>( dataElements );
@@ -109,22 +108,22 @@
             categoryOptionCombos.addAll( dataElement.getCategoryCombo().getOptionCombos() );
         }
 
-        log.debug( "Starting min-max analysis, no of data elements: " + elements.size() + ", no of org units: " + organisationUnits.size() );
+        log.debug( "Starting min-max analysis, no of data elements: " + elements.size() + ", no of parent org units: " + parents.size() );
 
-        return dataAnalysisStore.getMinMaxViolations( elements, categoryOptionCombos, periods, organisationUnits, MAX_OUTLIERS );
+        return dataAnalysisStore.getMinMaxViolations( elements, categoryOptionCombos, periods, parents, MAX_OUTLIERS );
     }
 
     @Override
-    public void generateMinMaxValues( Collection<OrganisationUnit> organisationUnits,
+    public void generateMinMaxValues( Collection<OrganisationUnit> parents,
         Collection<DataElement> dataElements, Double stdDevFactor )
     {
-        log.info( "Starting min-max value generation, no of data elements: " + dataElements.size() + ", no of org units: " + organisationUnits.size() );
+        log.info( "Starting min-max value generation, no of data elements: " + dataElements.size() + ", no of org units: " + parents.size() );
 
-        Set<Integer> orgUnitIds = new HashSet<>( IdentifiableObjectUtils.getIdentifiers( organisationUnits ) );
+        //Set<Integer> orgUnitIds = new HashSet<>( IdentifiableObjectUtils.getIdentifiers( organisationUnits ) );
 
         Date from = new DateTime( 1, 1, 1, 1, 1 ).toDate();
 
-        minMaxDataElementService.removeMinMaxDataElements( dataElements, organisationUnits );
+        minMaxDataElementService.removeMinMaxDataElements( dataElements, parents );
 
         log.debug( "Deleted existing min-max values" );
 
@@ -140,9 +139,9 @@
 
                 for ( DataElementCategoryOptionCombo categoryOptionCombo : categoryOptionCombos )
                 {
-                    Map<Integer, Double> standardDeviations = dataAnalysisStore.getStandardDeviation( dataElement, categoryOptionCombo, orgUnitIds, from );
+                    Map<Integer, Double> standardDeviations = dataAnalysisStore.getStandardDeviation( dataElement, categoryOptionCombo, parents, from );
 
-                    Map<Integer, Double> averages = dataAnalysisStore.getAverage( dataElement, categoryOptionCombo, standardDeviations.keySet(), from );
+                    Map<Integer, Double> averages = dataAnalysisStore.getAverage( dataElement, categoryOptionCombo, parents, from );
 
                     for ( Integer unit : averages.keySet() )
                     {

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/StdDevOutlierAnalysisService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/StdDevOutlierAnalysisService.java	2015-09-04 09:29:23 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/StdDevOutlierAnalysisService.java	2015-09-23 16:56:18 +0000
@@ -32,14 +32,12 @@
 import java.util.Collection;
 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.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.hisp.dhis.common.IdentifiableObjectUtils;
 import org.hisp.dhis.dataelement.DataElement;
 import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
 import org.hisp.dhis.datavalue.DeflatedDataValue;
@@ -71,12 +69,10 @@
     // -------------------------------------------------------------------------
 
     @Override
-    public final List<DeflatedDataValue> analyse( Collection<OrganisationUnit> organisationUnits,
+    public final List<DeflatedDataValue> analyse( Collection<OrganisationUnit> parents,
         Collection<DataElement> dataElements, Collection<Period> periods, Double stdDevFactor, Date from )
     {
-        log.info( "Starting std dev analysis, no of org units: " + organisationUnits.size() + ", factor: " + stdDevFactor + ", from: " + from );
-
-        Set<Integer> units = new HashSet<>( IdentifiableObjectUtils.getIdentifiers( organisationUnits ) );
+        log.info( "Starting std dev analysis, no of org units: " + parents.size() + ", factor: " + stdDevFactor + ", from: " + from );
 
         List<DeflatedDataValue> outlierCollection = new ArrayList<>();
 
@@ -91,9 +87,9 @@
 
                 for ( DataElementCategoryOptionCombo categoryOptionCombo : categoryOptionCombos )
                 {
-                    Map<Integer, Double> standardDeviations = dataAnalysisStore.getStandardDeviation( dataElement, categoryOptionCombo, units, from );
+                    Map<Integer, Double> standardDeviations = dataAnalysisStore.getStandardDeviation( dataElement, categoryOptionCombo, parents, from );
 
-                    Map<Integer, Double> averages = dataAnalysisStore.getAverage( dataElement, categoryOptionCombo, standardDeviations.keySet(), from );
+                    Map<Integer, Double> averages = dataAnalysisStore.getAverage( dataElement, categoryOptionCombo, parents, from );
 
                     Map<Integer, Integer> lowBoundMap = new HashMap<>();
                     Map<Integer, Integer> highBoundMap = new HashMap<>();

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/jdbc/JdbcDataAnalysisStore.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/jdbc/JdbcDataAnalysisStore.java	2015-09-23 13:58:30 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataanalysis/jdbc/JdbcDataAnalysisStore.java	2015-09-23 16:56:18 +0000
@@ -39,7 +39,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -89,16 +88,17 @@
     // -------------------------------------------------------------------------
 
     @Override
-    public Map<Integer, Double> getStandardDeviation( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, Set<Integer> organisationUnits, Date from )
+    public Map<Integer, Double> getStandardDeviation( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, 
+        Collection<OrganisationUnit> parents, Date from )
     {
         Map<Integer, Double> map = new HashMap<>();
         
-        if ( organisationUnits.isEmpty() )
+        if ( parents.isEmpty() )
         {
             return map;
         }
         
-        final String sql = 
+        String sql = 
             "select ou.organisationunitid, " +
               "(select stddev_pop( cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) ) " +
               "from datavalue dv " +
@@ -107,8 +107,14 @@
               "and dv.categoryoptioncomboid = " + categoryOptionCombo.getId() + " " +
               "and pe.startdate >= '" + DateUtils.getMediumDateString( from ) + "' " +
               "and dv.sourceid = ou.organisationunitid) as deviation " +
-            "from organisationunit ou " +
-            "where ou.organisationunitid in (" + getCommaDelimitedString( organisationUnits ) + ")";
+            "from organisationunit ou where (";
+        
+        for ( OrganisationUnit parent : parents )
+        {
+            sql += "ou.path like '%" + parent.getUid() + "%' or ";
+        }
+
+        sql = TextUtils.removeLastOr( sql ) + ")";
         
         SqlRowSet rowSet = jdbcTemplate.queryForRowSet( sql );
         
@@ -126,16 +132,17 @@
     }
     
     @Override
-    public Map<Integer, Double> getAverage( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, Set<Integer> organisationUnits, Date from )
+    public Map<Integer, Double> getAverage( DataElement dataElement, DataElementCategoryOptionCombo categoryOptionCombo, 
+        Collection<OrganisationUnit> parents, Date from )
     {
         Map<Integer, Double> map = new HashMap<>();
         
-        if ( organisationUnits.isEmpty() )
+        if ( parents.isEmpty() )
         {
             return map;
         }
-        
-        final String sql = 
+                
+        String sql = 
             "select ou.organisationunitid, " +
                 "(select avg( cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) ) " +
                 "from datavalue dv " +
@@ -144,8 +151,14 @@
                 "and dv.categoryoptioncomboid = " + categoryOptionCombo.getId() + " " +
                 "and pe.startdate >= '" + DateUtils.getMediumDateString( from ) + "' " +
                 "and dv.sourceid = ou.organisationunitid) as average " +
-            "from organisationunit ou " +
-            "where ou.organisationunitid in (" + getCommaDelimitedString( organisationUnits ) + ")";
+            "from organisationunit ou where (";
+        
+        for ( OrganisationUnit parent : parents )
+        {
+            sql += "ou.path like '%" + parent.getUid() + "%' or ";
+        }
+        
+        sql = TextUtils.removeLastOr( sql ) + ")";
         
         SqlRowSet rowSet = jdbcTemplate.queryForRowSet( sql );
         
@@ -164,40 +177,46 @@
     
     @Override
     public List<DeflatedDataValue> getMinMaxViolations( Collection<DataElement> dataElements, Collection<DataElementCategoryOptionCombo> categoryOptionCombos,
-        Collection<Period> periods, Collection<OrganisationUnit> organisationUnits, int limit )
+        Collection<Period> periods, Collection<OrganisationUnit> parents, int limit )
     {
-        if ( dataElements.isEmpty() || categoryOptionCombos.isEmpty() || periods.isEmpty() || organisationUnits.isEmpty() )
+        if ( dataElements.isEmpty() || categoryOptionCombos.isEmpty() || periods.isEmpty() || parents.isEmpty() )
         {
             return new ArrayList<>();
         }
         
         String dataElementIds = getCommaDelimitedString( getIdentifiers( dataElements ) );
-        String organisationUnitIds = getCommaDelimitedString( getIdentifiers( organisationUnits ) );
         String periodIds = getCommaDelimitedString( getIdentifiers( periods ) );
         String categoryOptionComboIds = getCommaDelimitedString( getIdentifiers( categoryOptionCombos ) );
-        
-        Map<Integer, String> optionComboMap = DataElementCategoryOptionCombo.getCategoryOptionComboMap( categoryOptionCombos );
-        
-        //TODO persist name on category option combo and use join to improve performance
-        
+                
         String sql = 
             "select dv.dataelementid, dv.periodid, dv.sourceid, dv.categoryoptioncomboid, dv.value, dv.storedby, dv.lastupdated, " +
-            "dv.created, dv.comment, dv.followup, ou.name as sourcename, de.name as dataelementname, pt.name as periodtypename, pe.startdate, pe.enddate, mm.minimumvalue, mm.maximumvalue " +
+            "dv.created, dv.comment, dv.followup, ou.name as sourcename, de.name as dataelementname, " +
+            "pt.name as periodtypename, pe.startdate, pe.enddate, coc.name as categoryoptioncomboname, mm.minimumvalue, mm.maximumvalue " +
             "from datavalue dv " +
             "join minmaxdataelement mm on ( dv.dataelementid = mm.dataelementid and dv.categoryoptioncomboid = mm.categoryoptioncomboid and dv.sourceid = mm.sourceid ) " +
             "join dataelement de on dv.dataelementid = de.dataelementid " +
             "join period pe on dv.periodid = pe.periodid " +
             "join periodtype pt on pe.periodtypeid = pt.periodtypeid " +
             "join organisationunit ou on dv.sourceid = ou.organisationunitid " +
+            "join categoryoptioncombo coc on dv.categoryoptioncomboid = coc.categoryoptioncomboid " +
             "where dv.dataelementid in (" + dataElementIds + ") " +
             "and dv.categoryoptioncomboid in (" + categoryOptionComboIds + ") " +
             "and dv.periodid in (" + periodIds + ") " + 
-            "and dv.sourceid in (" + organisationUnitIds + ") and ( " +
+            "and ( " +
                 "cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) < mm.minimumvalue " +
-                "or cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) > mm.maximumvalue )" +
-            statementBuilder.limitRecord( 0, limit );
-        
-        return jdbcTemplate.query( sql, new DeflatedDataValueNameMinMaxRowMapper( null, null, optionComboMap ) );
+                "or cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) > mm.maximumvalue ) " +
+            "and (";
+
+        for ( OrganisationUnit parent : parents )
+        {
+            sql += "ou.path like '%" + parent.getUid() + "%' or ";
+        }
+        
+        sql = TextUtils.removeLastOr( sql ) + ")";
+        
+        sql += statementBuilder.limitRecord( 0, limit );
+        
+        return jdbcTemplate.query( sql, new DeflatedDataValueNameMinMaxRowMapper( null, null ) );
     }
     
     @Override
@@ -209,7 +228,7 @@
             return new ArrayList<>();
         }
         
-        //TODO parallel processes
+        //TODO parallel processes?
                 
         List<List<Integer>> organisationUnitPages = new PaginatedList<>( lowerBoundMap.keySet() ).setPageSize( 100 ).getPages();
         
@@ -243,24 +262,22 @@
             "and dv.categoryoptioncomboid = " + categoryOptionCombo.getId() + " " +
             "and dv.periodid in (" + periodIds + ") and ( ";
         
-        for ( Integer organisationUnit : organisationUnits )
+        for ( Integer orgUnitUid : organisationUnits )
         {
-            sql += "( dv.sourceid = " + organisationUnit + " " +
-                "and ( cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) < " + lowerBoundMap.get( organisationUnit ) + " " +
-                "or cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) > " + upperBoundMap.get( organisationUnit ) + " ) ) or ";
+            sql += "( dv.sourceid = " + orgUnitUid + " " +
+                "and ( cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) < " + lowerBoundMap.get( orgUnitUid ) + " " +
+                "or cast( dv.value as " + statementBuilder.getDoubleColumnType() + " ) > " + upperBoundMap.get( orgUnitUid ) + " ) ) or ";
         }
         
         sql = sql.substring( 0, ( sql.length() - 3 ) ) + " )";
         
-        return jdbcTemplate.query( sql, new DeflatedDataValueNameMinMaxRowMapper( lowerBoundMap, upperBoundMap, null ) );
+        return jdbcTemplate.query( sql, new DeflatedDataValueNameMinMaxRowMapper( lowerBoundMap, upperBoundMap ) );
     }
 
     @Override
     public List<DeflatedDataValue> getFollowupDataValues( OrganisationUnit organisationUnit, int limit )
     {
-        final String idLevelColumn = "idlevel" + organisationUnit.getLevel();
-
-        String sql =
+        final String sql =
             "select dv.dataelementid, dv.periodid, dv.sourceid, dv.categoryoptioncomboid, dv.value, " +
             "dv.storedby, dv.lastupdated, dv.created, dv.comment, dv.followup, mm.minimumvalue, mm.maximumvalue, de.name AS dataelementname, " +
             "pe.startdate, pe.enddate, pt.name AS periodtypename, ou.name AS sourcename, cc.name AS categoryoptioncomboname " +
@@ -269,10 +286,9 @@
             "join dataelement de on dv.dataelementid = de.dataelementid " +
             "join period pe on dv.periodid = pe.periodid " +
             "join periodtype pt on pe.periodtypeid = pt.periodtypeid " +
-            "left join organisationunit ou on ou.organisationunitid = dv.sourceid " +
+            "join organisationunit ou on ou.organisationunitid = dv.sourceid " +
             "join categoryoptioncombo cc on dv.categoryoptioncomboid = cc.categoryoptioncomboid " +
-            "inner join _orgunitstructure ous on ous.organisationunitid = dv.sourceid " +
-            "where ous." + idLevelColumn + " = " + organisationUnit.getId() + " " +
+            "where ou.path like '%" + organisationUnit.getUid() + "%' " +
             "and dv.followup = true " +
             statementBuilder.limitRecord( 0, limit );
         

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataanalysis/DataAnalysisStoreTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataanalysis/DataAnalysisStoreTest.java	2015-05-03 12:08:13 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataanalysis/DataAnalysisStoreTest.java	2015-09-23 16:56:18 +0000
@@ -101,7 +101,7 @@
     private OrganisationUnit organisationUnitA;
     private OrganisationUnit organisationUnitB;
     
-    private Set<Integer> organisationUnits;
+    private Set<OrganisationUnit> organisationUnits;
     
     // ----------------------------------------------------------------------
     // Fixture
@@ -138,8 +138,8 @@
         organisationUnitService.addOrganisationUnit( organisationUnitB );
         
         organisationUnits = new HashSet<>();
-        organisationUnits.add( organisationUnitA.getId() );
-        organisationUnits.add( organisationUnitB.getId() );
+        organisationUnits.add( organisationUnitA );
+        organisationUnits.add( organisationUnitB );
     }
 
     // ----------------------------------------------------------------------

=== modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/objectmapper/DeflatedDataValueNameMinMaxRowMapper.java'
--- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/objectmapper/DeflatedDataValueNameMinMaxRowMapper.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/objectmapper/DeflatedDataValueNameMinMaxRowMapper.java	2015-09-23 16:56:18 +0000
@@ -65,17 +65,15 @@
 {
     private Map<Integer, Integer> minMap;
     private Map<Integer, Integer> maxMap;
-    private Map<Integer, String> optionComboMap;
     
     public DeflatedDataValueNameMinMaxRowMapper()
     {
     }
     
-    public DeflatedDataValueNameMinMaxRowMapper( Map<Integer, Integer> minMap, Map<Integer, Integer> maxMap, Map<Integer, String> optionComboMap )
+    public DeflatedDataValueNameMinMaxRowMapper( Map<Integer, Integer> minMap, Map<Integer, Integer> maxMap )
     {
         this.minMap = minMap;
         this.maxMap = maxMap;
-        this.optionComboMap = optionComboMap;
     }
     
     @Override
@@ -102,7 +100,7 @@
             resultSet.getString( "startdate" ),
             resultSet.getString( "enddate" ) );
         value.setSourceName( resultSet.getString( "sourcename" ) );
-        value.setCategoryOptionComboName( optionComboMap != null ? optionComboMap.get( value.getCategoryOptionComboId() ) : resultSet.getString( "categoryoptioncomboname" ) );
+        value.setCategoryOptionComboName( resultSet.getString( "categoryoptioncomboname" ) );
         
         return value;
     }

=== modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetAnalysisAction.java'
--- dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetAnalysisAction.java	2015-05-03 12:08:13 +0000
+++ dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetAnalysisAction.java	2015-09-23 16:56:18 +0000
@@ -28,7 +28,12 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import com.opensymphony.xwork2.Action;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -39,19 +44,14 @@
 import org.hisp.dhis.datavalue.DeflatedDataValue;
 import org.hisp.dhis.i18n.I18nFormat;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
-import org.hisp.dhis.organisationunit.OrganisationUnitService;
 import org.hisp.dhis.oust.manager.SelectionTreeManager;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodService;
 import org.hisp.dhis.util.SessionUtils;
 import org.joda.time.DateTime;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import com.google.common.collect.Sets;
+import com.opensymphony.xwork2.Action;
 
 /**
  * Finds outliers in given data elements for given sources in a given period and
@@ -99,13 +99,6 @@
         this.dataSetService = dataSetService;
     }
 
-    private OrganisationUnitService organisationUnitService;
-
-    public void setOrganisationUnitService( OrganisationUnitService organisationUnitService )
-    {
-        this.organisationUnitService = organisationUnitService;
-    }
-
     private I18nFormat format;
 
     public void setFormat( I18nFormat format )
@@ -196,8 +189,6 @@
             return ERROR;
         }
         
-        Collection<OrganisationUnit> orgUnits = organisationUnitService.getOrganisationUnitWithChildren( unit.getId() );
-
         Collection<Period> periods = periodService.getPeriodsBetweenDates( format.parseDate( fromDate ), format.parseDate( toDate ) );
 
         Set<DataElement> dataElements = new HashSet<>();
@@ -218,7 +209,7 @@
 
         if ( service != null )
         {
-            dataValues = service.analyse( orgUnits, dataElements, periods, standardDeviation, from );
+            dataValues = service.analyse( Sets.newHashSet( unit ), dataElements, periods, standardDeviation, from );
 
             maxExceeded = dataValues.size() > DataAnalysisService.MAX_OUTLIERS;
         }

=== modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetFollowupAction.java'
--- dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetFollowupAction.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-web/dhis-web-validationrule/src/main/java/org/hisp/dhis/validationrule/action/dataanalysis/GetFollowupAction.java	2015-09-23 16:56:18 +0000
@@ -28,7 +28,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import com.opensymphony.xwork2.Action;
+import java.util.ArrayList;
+import java.util.List;
+
 import org.hisp.dhis.dataanalysis.DataAnalysisService;
 import org.hisp.dhis.dataanalysis.FollowupAnalysisService;
 import org.hisp.dhis.datavalue.DeflatedDataValue;
@@ -36,8 +38,7 @@
 import org.hisp.dhis.oust.manager.SelectionTreeManager;
 import org.hisp.dhis.util.SessionUtils;
 
-import java.util.ArrayList;
-import java.util.Collection;
+import com.opensymphony.xwork2.Action;
 
 /**
  * @author Halvdan Hoem Grelland
@@ -69,14 +70,14 @@
     // Output
     // -------------------------------------------------------------------------
 
-    private Collection<DeflatedDataValue> dataValues = new ArrayList<>();
+    private List<DeflatedDataValue> dataValues = new ArrayList<>();
 
-    public Collection<DeflatedDataValue> getDataValues()
+    public List<DeflatedDataValue> getDataValues()
     {
         return dataValues;
     }
 
-    private boolean maxExceeded;
+    private boolean maxExceeded = false;
 
     public boolean getMaxExceeded()
     {
@@ -92,18 +93,12 @@
     {
         OrganisationUnit orgUnit = selectionTreeManager.getReloadedSelectedOrganisationUnit();
 
-        if( orgUnit != null )
+        if ( orgUnit != null )
         {
             dataValues = followupAnalysisService.getFollowupDataValues( orgUnit, DataAnalysisService.MAX_OUTLIERS + 1 ); // +1 to detect overflow
 
             maxExceeded = dataValues.size() > DataAnalysisService.MAX_OUTLIERS;
         }
-        else
-        {
-            dataValues = new ArrayList<>();
-
-            maxExceeded = false;
-        }
 
         SessionUtils.setSessionVar( KEY_ANALYSIS_DATA_VALUES, dataValues );
 

=== modified file 'dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/META-INF/dhis/beans.xml	2014-12-04 06:39:46 +0000
+++ dhis-2/dhis-web/dhis-web-validationrule/src/main/resources/META-INF/dhis/beans.xml	2015-09-23 16:56:18 +0000
@@ -182,7 +182,6 @@
     <property name="selectionTreeManager" ref="org.hisp.dhis.oust.manager.SelectionTreeManager" />
     <property name="periodService" ref="org.hisp.dhis.period.PeriodService" />
     <property name="dataSetService" ref="org.hisp.dhis.dataset.DataSetService" />
-    <property name="organisationUnitService" ref="org.hisp.dhis.organisationunit.OrganisationUnitService" />
   </bean>
 
   <bean id="org.hisp.dhis.validationrule.action.dataanalysis.MarkForFollowupAction"