← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19136: Event analytics, data item prefix for option when data items are collapsed. Introduced enum Value...

 

Merge authors:
  Lars Helge Øverland (larshelge)
------------------------------------------------------------
revno: 19136 [merge]
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Mon 2015-05-11 22:15:52 +0200
message:
  Event analytics, data item prefix for option when data items are collapsed. Introduced enum ValueType. Using integer as well as double as event analytics column types depending on data element/attribute value type.
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/ValueType.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/QueryItem.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/legend/LegendSet.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/option/OptionSet.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/trackedentity/TrackedEntityAttribute.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.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/common/QueryItem.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/QueryItem.java	2015-03-03 16:54:51 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/QueryItem.java	2015-05-11 11:58:34 +0000
@@ -120,6 +120,11 @@
         return legendSet != null;
     }
     
+    public boolean hasOptionSet()
+    {
+        return optionSet != null;
+    }
+    
     public String getLegendSetUid()
     {
         return legendSet != null ? legendSet.getUid() : null;

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/ValueType.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/ValueType.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/ValueType.java	2015-05-11 14:01:03 +0000
@@ -0,0 +1,160 @@
+package org.hisp.dhis.common;
+
+/*
+ * Copyright (c) 2004-2015, 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.Date;
+
+import org.hisp.dhis.dataelement.DataElement;
+import org.hisp.dhis.trackedentity.TrackedEntityAttribute;
+
+/**
+ * @author Lars Helge Overland
+ */
+public enum ValueType
+{   
+    TEXT( String.class ),
+    LONG_TEXT( String.class ),
+    LETTER( String.class ),
+    TYPE_PHONE_NUMBER( String.class ),
+    EMAIL( String.class ),
+    BOOLEAN( Boolean.class ),
+    TRUE_ONLY( Boolean.class ),
+    DATE( Date.class ),
+    NUMBER( Double.class ),
+    UNIT_INTERVAL( Double.class ),
+    PERCENTAGE( Double.class ),
+    INTEGER( Integer.class ),
+    INTEGER_POSITIVE( Integer.class ),
+    INTEGER_NEGATIVE( Integer.class ),
+    INTEGER_ZERO_OR_POSITIVE( Integer.class ),
+    NEGATIVE_INTEGER( Integer.class );
+    
+    private final Class<?> javaClass;
+    
+    private ValueType()
+    {
+        this.javaClass = null;
+    }
+    
+    private ValueType( Class<?> javaClass )
+    {
+        this.javaClass = javaClass;
+    }
+    
+    public Class<?> getJavaClass()
+    {
+        return javaClass;
+    }
+
+    /**
+     * TODO replace string value type on data element with ValueType and remove
+     * this method.
+     */
+    public static ValueType getFromDataElement( DataElement dataElement )
+    {        
+        if ( DataElement.VALUE_TYPE_STRING.equals( dataElement.getType() ) )
+        {
+            if ( DataElement.VALUE_TYPE_LONG_TEXT.equals( dataElement.getTextType() ) )
+            {
+                return ValueType.LONG_TEXT;
+            }
+            else
+            {
+                return ValueType.TEXT;
+            }
+        }
+        else if ( DataElement.VALUE_TYPE_INT.equals( dataElement.getType() ) )
+        {
+            if ( DataElement.VALUE_TYPE_UNIT_INTERVAL.equals( dataElement.getNumberType() ) )
+            {
+                return ValueType.UNIT_INTERVAL;
+            }
+            else if ( DataElement.VALUE_TYPE_PERCENTAGE.equals( dataElement.getNumberType() ) )
+            {
+                return ValueType.PERCENTAGE;
+            }
+            else if ( DataElement.VALUE_TYPE_INT.equals( dataElement.getNumberType() ) )
+            {
+                return ValueType.INTEGER;
+            }
+            else if ( DataElement.VALUE_TYPE_POSITIVE_INT.equals( dataElement.getNumberType() ) )
+            {
+                return ValueType.INTEGER_POSITIVE;
+            }
+            else if ( DataElement.VALUE_TYPE_ZERO_OR_POSITIVE_INT.equals( dataElement.getNumberType() ) )
+            {
+                return ValueType.INTEGER_ZERO_OR_POSITIVE;
+            }
+            else if ( DataElement.VALUE_TYPE_NEGATIVE_INT.equals( dataElement.getNumberType() ) )
+            {
+                return ValueType.INTEGER_NEGATIVE;
+            }
+            else
+            {
+                return ValueType.NUMBER;
+            }
+        }
+        else if ( DataElement.VALUE_TYPE_BOOL.equals( dataElement.getType() ) )
+        {
+            return ValueType.BOOLEAN;
+        }
+        else if ( DataElement.VALUE_TYPE_TRUE_ONLY.equals( dataElement.getType() ) )
+        {
+            return ValueType.TRUE_ONLY;
+        }
+        else if ( DataElement.VALUE_TYPE_DATE.equals( dataElement.getType() ) )
+        {
+            return ValueType.DATE;
+        }
+
+        return ValueType.TEXT; // Fall back
+    }
+
+    /**
+     * TODO replace string value type on attribute with ValueType and remove
+     * this method.
+     */
+    public static ValueType getFromAttribute( TrackedEntityAttribute attribute )
+    {
+        if ( TrackedEntityAttribute.TYPE_NUMBER.equals( attribute.getValueType() ) )
+        {
+            return ValueType.NUMBER;
+        }
+        else if ( TrackedEntityAttribute.TYPE_BOOL.equals( attribute.getValueType() ) || TrackedEntityAttribute.TYPE_TRUE_ONLY.equals( attribute.getValueType() ) )
+        {
+            return ValueType.BOOLEAN;
+        }
+        else if ( TrackedEntityAttribute.TYPE_DATE.equals( attribute.getValueType() ) )
+        {
+            return ValueType.DATE;
+        }
+        
+        return ValueType.TEXT; // Fall back
+    }    
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/legend/LegendSet.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/legend/LegendSet.java	2015-02-26 15:21:29 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/legend/LegendSet.java	2015-05-11 14:01:03 +0000
@@ -78,6 +78,19 @@
     {
         legends.clear();
     }
+    
+    public Legend getLegendByUid( String uid )
+    {
+        for ( Legend legend : legends )
+        {
+            if ( legend != null && legend.getUid().equals( uid ) )
+            {
+                return legend;
+            }
+        }
+        
+        return null;
+    }
 
     // -------------------------------------------------------------------------
     // Getters and setters

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/option/OptionSet.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/option/OptionSet.java	2015-04-20 16:36:41 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/option/OptionSet.java	2015-05-11 14:01:03 +0000
@@ -111,6 +111,19 @@
         return codes;
     }
     
+    public Option getOptionByCode( String code )
+    {
+        for ( Option option : options )
+        {
+            if ( option != null && option.getCode().equals( code ) )
+            {
+                return option;
+            }
+        }
+        
+        return null;
+    }
+    
     // -------------------------------------------------------------------------
     // Getters and setters
     // -------------------------------------------------------------------------

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/trackedentity/TrackedEntityAttribute.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/trackedentity/TrackedEntityAttribute.java	2015-04-17 12:41:18 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/trackedentity/TrackedEntityAttribute.java	2015-05-11 11:58:34 +0000
@@ -56,17 +56,17 @@
 public class TrackedEntityAttribute
     extends BaseDimensionalObject
 {
-    public static final String TYPE_DATE = "date";
     public static final String TYPE_STRING = "string";
+    public static final String TYPE_PHONE_NUMBER = "phoneNumber";
+    public static final String TYPE_EMAIL = "email";
     public static final String TYPE_NUMBER = "number";
     public static final String TYPE_LETTER = "letter";
     public static final String TYPE_BOOL = "bool";
     public static final String TYPE_TRUE_ONLY = "trueOnly";
+    public static final String TYPE_DATE = "date";
     public static final String TYPE_OPTION_SET = "optionSet";
-    public static final String TYPE_PHONE_NUMBER = "phoneNumber";
     public static final String TYPE_TRACKER_ASSOCIATE = "trackerAssociate";
     public static final String TYPE_USERS = "users";
-    public static final String TYPE_EMAIL = "email";
 
     private String description;
 

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java	2015-03-24 11:45:27 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java	2015-05-11 11:58:34 +0000
@@ -46,6 +46,7 @@
 import org.hisp.dhis.common.NameableObjectUtils;
 import org.hisp.dhis.common.QueryItem;
 import org.hisp.dhis.legend.Legend;
+import org.hisp.dhis.option.OptionSet;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
 import org.hisp.dhis.period.Period;
 import org.hisp.dhis.program.Program;
@@ -229,6 +230,24 @@
         return legends;
     }
     
+    /**
+     * Get option sets part of items.
+     */
+    public Set<OptionSet> getItemOptionSets()
+    {
+        Set<OptionSet> optionSets = new HashSet<>();
+        
+        for ( QueryItem item : items )
+        {
+            if ( item.hasOptionSet() )
+            {
+                optionSets.add( item.getOptionSet() );
+            }
+        }
+        
+        return optionSets;
+    }
+    
     public boolean isOrganisationUnitMode( String mode )
     {
         return organisationUnitMode != null && organisationUnitMode.equalsIgnoreCase( mode );

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java	2015-04-09 09:02:42 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java	2015-05-11 11:58:34 +0000
@@ -191,8 +191,10 @@
         {
             for ( QueryItem item : params.getItems() )
             {
+                //TODO use ValueType for type
+
                 String legendSet = item.hasLegendSet() ? item.getLegendSet().getUid() : null;
-                
+
                 grid.addHeader( new GridHeader( item.getItem().getUid(), item.getItem().getName(), item.getTypeAsString(), false, true, item.getOptionSetUid(), legendSet ) );
             }
         }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java	2015-02-25 15:59:31 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java	2015-05-11 14:01:03 +0000
@@ -59,6 +59,8 @@
 import org.hisp.dhis.common.QueryFilter;
 import org.hisp.dhis.common.QueryItem;
 import org.hisp.dhis.jdbc.StatementBuilder;
+import org.hisp.dhis.legend.Legend;
+import org.hisp.dhis.option.Option;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
 import org.hisp.dhis.system.util.MathUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -77,6 +79,8 @@
     private static final Log log = LogFactory.getLog( JdbcEventAnalyticsManager.class );
     
     private static final String QUERY_ERR_MSG = "Query failed, likely because the requested analytics table does not exist";
+    private static final String ITEM_NAME_SEP = ": ";
+    private static final String NA = "[N/A]";
     
     @Autowired
     private JdbcTemplate jdbcTemplate;
@@ -170,8 +174,9 @@
             
             for ( QueryItem queryItem : params.getItems() )
             {
-                String itemValue = rowSet.getString( queryItem.getItemName() );                
-                grid.addValue( itemValue );
+                String itemValue = rowSet.getString( queryItem.getItemName() );
+                String gridValue = params.isCollapseDataDimensions() ? getCollapsedDataItemValue( params, queryItem, itemValue ) : itemValue;
+                grid.addValue( gridValue );
             }
             
             if ( params.hasValueDimension() )
@@ -552,4 +557,36 @@
         
         return StringUtils.defaultIfEmpty( fixedCols + ", ", fixedCols );
     }
+
+    /**
+     * Returns an item value for the given query, query item and value. Assumes that
+     * data dimensions are collapsed for the given query. Returns the short name
+     * of the given query item followed by the item value. If the given query item
+     * has a legend set, the item value is treated as an id and substituted with
+     * the matching legend name. If the given query item has an option set, the 
+     * item value is treated as a code and substituted with the matching option 
+     * name.
+     */
+    private String getCollapsedDataItemValue( EventQueryParams params, QueryItem item, String itemValue )
+    {
+        String value = item.getItem().getDisplayShortName() + ITEM_NAME_SEP;
+        
+        Legend legend = null;
+        Option option = null;
+        
+        if ( item.hasLegendSet() && ( legend = item.getLegendSet().getLegendByUid( itemValue ) ) != null )
+        {
+            return value + legend.getDisplayName();
+        }        
+        else if ( item.hasOptionSet() && ( option = item.getOptionSet().getOptionByCode( itemValue ) ) != null )
+        {
+            return value + option.getDisplayName();
+        }
+        else
+        {
+            itemValue = StringUtils.defaultString( itemValue, NA );
+            
+            return value + itemValue;
+        }
+    }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.java	2015-03-13 15:48:26 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.java	2015-05-11 11:58:34 +0000
@@ -40,6 +40,7 @@
 import java.util.concurrent.Future;
 
 import org.hisp.dhis.analytics.AnalyticsTable;
+import org.hisp.dhis.common.ValueType;
 import org.hisp.dhis.dataelement.DataElement;
 import org.hisp.dhis.organisationunit.OrganisationUnitGroupSet;
 import org.hisp.dhis.organisationunit.OrganisationUnitLevel;
@@ -224,10 +225,8 @@
     public List<String[]> getDimensionColumns( AnalyticsTable table )
     {
         final String dbl = statementBuilder.getDoubleColumnType();
-        final String text = "character varying(255)";
         final String numericClause = " and value " + statementBuilder.getRegexpMatch() + " '"
             + MathUtils.NUMERIC_LENIENT_REGEXP + "'";
-        final String doubleSelect = "cast(value as " + dbl + ")";
 
         List<String[]> columns = new ArrayList<>();
 
@@ -261,9 +260,10 @@
 
         for ( DataElement dataElement : table.getProgram().getAllDataElements() )
         {
-            String dataType = dataElement.isNumericType() ? dbl : text;
+            ValueType valueType = ValueType.getFromDataElement( dataElement );
+            String dataType = getColumnType( valueType );
             String dataClause = dataElement.isNumericType() ? numericClause : "";
-            String select = dataElement.isNumericType() ? doubleSelect : "value";
+            String select = getSelectClause( valueType );
 
             String sql = "(select " + select + " from trackedentitydatavalue where programstageinstanceid=psi.programstageinstanceid " + 
                 "and dataelementid=" + dataElement.getId() + dataClause + ") as " + quote( dataElement.getUid() );
@@ -275,9 +275,10 @@
         for ( DataElement dataElement : table.getProgram().getDataElementsWithLegendSet() )
         {
             String column = quote( dataElement.getUid() + PartitionUtils.SEP + dataElement.getLegendSet().getUid() );
+            String select = getSelectClause( ValueType.getFromDataElement( dataElement ) );
             
             String sql = "(select l.uid from maplegend l inner join maplegendsetmaplegend lsl on l.maplegendid=lsl.maplegendid " +
-                "inner join trackedentitydatavalue dv on l.startvalue <= " + doubleSelect + " and l.endvalue > " + doubleSelect + " " +
+                "inner join trackedentitydatavalue dv on l.startvalue <= " + select + " and l.endvalue > " + select + " " +
                 "and lsl.legendsetid=" + dataElement.getLegendSet().getId() + " and dv.programstageinstanceid=psi.programstageinstanceid " + 
                 "and dv.dataelementid=" + dataElement.getId() + numericClause + ") as " + column;
                 
@@ -287,9 +288,10 @@
 
         for ( TrackedEntityAttribute attribute : table.getProgram().getTrackedEntityAttributes() )
         {
-            String dataType = attribute.isNumericType() ? dbl : text;
+            ValueType valueType = ValueType.getFromAttribute( attribute );
+            String dataType = getColumnType( valueType );
             String dataClause = attribute.isNumericType() ? numericClause : "";
-            String select = attribute.isNumericType() ? doubleSelect : "value";
+            String select = getSelectClause( valueType );
 
             String sql = "(select " + select + " from trackedentityattributevalue where trackedentityinstanceid=pi.trackedentityinstanceid " + 
                 "and trackedentityattributeid=" + attribute.getId() + dataClause + ") as " + quote( attribute.getUid() );
@@ -301,9 +303,10 @@
         for ( TrackedEntityAttribute attribute : table.getProgram().getTrackedEntityAttributesWithLegendSet() )
         {
             String column = quote( attribute.getUid() + PartitionUtils.SEP + attribute.getLegendSet().getUid() );
+            String select = getSelectClause( ValueType.getFromAttribute( attribute ) );
             
             String sql = "(select l.uid from maplegend l inner join maplegendsetmaplegend lsl on l.maplegendid=lsl.maplegendid " +
-                "inner join trackedentityattributevalue av on l.startvalue <= " + doubleSelect + " and l.endvalue > " + doubleSelect + " " +
+                "inner join trackedentityattributevalue av on l.startvalue <= " + select + " and l.endvalue > " + select + " " +
                 "and lsl.legendsetid=" + attribute.getLegendSet().getId() + " and av.trackedentityinstanceid=pi.trackedentityinstanceid " +
                 "and av.trackedentityattributeid=" + attribute.getId() + numericClause + ") as " + column;
             
@@ -379,4 +382,43 @@
     {
         return null; // Not needed
     }
+    
+    /**
+     * Returns the database column type based on the given value type.
+     */
+    private String getColumnType( ValueType valueType )
+    {
+        if ( Double.class.equals( valueType.getJavaClass() ) )
+        {
+            return statementBuilder.getDoubleColumnType();
+        }
+        else if ( Integer.class.equals( valueType.getJavaClass() ) )
+        {
+            return "integer";
+        }
+        else
+        {
+            return "character varying(255)";
+        }
+    }
+    
+    /**
+     * Returns the select clause, potentially with a cast statement, based on the
+     * given value type.
+     */
+    private String getSelectClause( ValueType valueType )
+    {
+        if ( Double.class.equals( valueType.getJavaClass() ) )
+        {
+            return "cast(value as " + statementBuilder.getDoubleColumnType() + ")";
+        }
+        else if ( Integer.class.equals( valueType.getJavaClass() ) )
+        {
+            return "cast(value as integer)";
+        }
+        else
+        {
+            return "value";
+        }
+    }
 }