← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19846: Event analytics. Impl support for variables value_count and zero_pos_value_count

 

------------------------------------------------------------
revno: 19846
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2015-08-25 16:09:25 +0200
message:
  Event analytics. Impl support for variables value_count and zero_pos_value_count
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RegexUtils.java
  dhis-2/dhis-api/src/test/java/org/hisp/dhis/program/
  dhis-2/dhis-api/src/test/java/org/hisp/dhis/program/ProgramIndicatorTest.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicatorService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java
  dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/TextUtils.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/programIndicatorForm.vm


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

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RegexUtils.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RegexUtils.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RegexUtils.java	2015-08-25 14:09:25 +0000
@@ -0,0 +1,64 @@
+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.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Lars Helge Overland
+ */
+public class RegexUtils
+{
+    /**
+     * Return the matches in the given input based on the given pattern.
+     * 
+     * @param pattern the pattern.
+     * @param input the input.
+     * @param group the group, can be null.
+     * @return a set of matches.
+     */
+    public static Set<String> getMatches( Pattern pattern, String input, Integer group )
+    {
+        group = group != null ? group : 0;
+        
+        Set<String> set = new HashSet<>();
+        
+        Matcher matcher = pattern.matcher( input );
+        
+        while ( matcher.find() )
+        {
+            set.add( matcher.group( group ) );
+        }
+        
+        return set;
+    }
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java	2015-08-25 09:48:55 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicator.java	2015-08-25 14:09:25 +0000
@@ -28,6 +28,7 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.util.Set;
 import java.util.regex.Pattern;
 
 import org.hisp.dhis.analytics.AggregationType;
@@ -36,6 +37,7 @@
 import org.hisp.dhis.common.DxfNamespaces;
 import org.hisp.dhis.common.IdentifiableObject;
 import org.hisp.dhis.common.MergeStrategy;
+import org.hisp.dhis.common.RegexUtils;
 import org.hisp.dhis.common.view.DetailedView;
 import org.hisp.dhis.common.view.ExportView;
 
@@ -44,6 +46,7 @@
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import com.google.common.collect.Sets;
 
 /**
  * @author Chau Thu Tran
@@ -132,7 +135,21 @@
     {
         return aggregationType != null ? aggregationType : AggregationType.AVERAGE;
     }
-
+    
+    /**
+     * Returns a set of data element and attribute identifiers part of the given
+     * input expression.
+     * 
+     * @param input the expression.
+     * @return a set of UIDs.
+     */
+    public static Set<String> getDataElementAndAttributeIdentifiers( String input )
+    {
+        return Sets.union( 
+            RegexUtils.getMatches( DATAELEMENT_PATTERN, input, 2 ),
+            RegexUtils.getMatches( ATTRIBUTE_PATTERN, input, 1 ) );
+    }
+    
     // -------------------------------------------------------------------------
     // Getters && Setters
     // -------------------------------------------------------------------------

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicatorService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicatorService.java	2015-08-21 03:14:08 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramIndicatorService.java	2015-08-25 14:09:25 +0000
@@ -33,6 +33,7 @@
 import java.util.Set;
 
 import org.hisp.dhis.constant.Constant;
+import org.hisp.dhis.dataelement.DataElement;
 import org.hisp.dhis.trackedentity.TrackedEntityAttribute;
 
 /**
@@ -156,7 +157,7 @@
      *         {@link ProgramIndicator.INVALID_VARIABLES_IN_EXPRESSION}.
      */
     String filterIsValid( String filter );
-        
+    
     /**
      * Get all {@link ProgramStageDataElement} part of the expression.
      * 

=== added directory 'dhis-2/dhis-api/src/test/java/org/hisp/dhis/program'
=== added file 'dhis-2/dhis-api/src/test/java/org/hisp/dhis/program/ProgramIndicatorTest.java'
--- dhis-2/dhis-api/src/test/java/org/hisp/dhis/program/ProgramIndicatorTest.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/test/java/org/hisp/dhis/program/ProgramIndicatorTest.java	2015-08-25 14:09:25 +0000
@@ -0,0 +1,51 @@
+package org.hisp.dhis.program;
+
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.util.Set;
+import org.junit.Test;
+import com.google.common.collect.Sets;
+
+/**
+* @author Lars Helge Overland
+*/
+public class ProgramIndicatorTest
+{
+    @Test
+    public void testGetIdentifiers()
+    {
+        String expression = "#{chG8sINMf11.yD5mUKAm3aK} + #{chG8sINMf11.UaGD9u0kaur} - A{y1Bhi6xHtVk}";
+        
+        Set<String> expected = Sets.newHashSet( "yD5mUKAm3aK", "UaGD9u0kaur", "y1Bhi6xHtVk" );
+        
+        assertEquals( expected, ProgramIndicator.getDataElementAndAttributeIdentifiers( expression ) );
+    }
+}

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java	2015-08-25 09:48:55 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/program/DefaultProgramIndicatorService.java	2015-08-25 14:09:25 +0000
@@ -353,7 +353,7 @@
         while ( matcher.find() )
         {
             String key = matcher.group( 1 );
-            String uid = statementBuilder.columnQuote( matcher.group( 2 ) );
+            String val = matcher.group( 2 );
             
             if ( ProgramIndicator.KEY_DATAELEMENT.equals( key ) )
             {
@@ -363,17 +363,26 @@
             }
             else if ( ProgramIndicator.KEY_ATTRIBUTE.equals( key ) )
             {
-                matcher.appendReplacement( buffer, uid );
+                matcher.appendReplacement( buffer, statementBuilder.columnQuote( val ) );
             }
             else if ( ProgramIndicator.KEY_CONSTANT.equals( key ) )
             {
-                Constant constant = constantService.getConstant( uid );
+                Constant constant = constantService.getConstant( val );
                 
                 if ( constant != null )
                 {
                     matcher.appendReplacement( buffer, String.valueOf( constant.getValue() ) );
                 }
             }
+            else if ( ProgramIndicator.KEY_PROGRAM_VARIABLE.equals( key ) )
+            {
+                String sql = getVariableAsSql( val, expression );
+                
+                if ( sql != null )
+                {
+                    matcher.appendReplacement( buffer, sql );
+                }
+            }
         }
         
         expression = TextUtils.appendTail( matcher, buffer );
@@ -758,6 +767,47 @@
         
         return MathUtils.calculateExpression( expression );
     }
+    
+    /**
+     * Creates a SQL select clause from the given program indicator variable 
+     * based on the given expression. Wraps the count variables with 
+     * <code>nullif</code> to avoid potential division by zero.
+     * 
+     * @param var the program indicator variable.
+     * @param expression the program indicator expression.
+     * @return a SQL select clause.
+     */
+    private String getVariableAsSql( String var, String expression )
+    {
+        if ( ProgramIndicator.VAR_EXECUTION_DATE.equals( var ) )
+        {
+            return "executiondate";
+        }
+        else if ( ProgramIndicator.VAR_VALUE_COUNT.equals( var ) )
+        {
+            String sql = "nullif((";
+            
+            for ( String uid : ProgramIndicator.getDataElementAndAttributeIdentifiers( expression ) )
+            {
+                sql += "case when " + statementBuilder.columnQuote( uid ) + " is not null then 1 else 0 end + ";
+            }
+            
+            return TextUtils.removeLast( sql, "+" ) + "),0)";
+        }
+        else if ( ProgramIndicator.VAR_ZERO_POS_VALUE_COUNT.equals( var ) )
+        {
+            String sql = "nullif((";
+            
+            for ( String uid : ProgramIndicator.getDataElementAndAttributeIdentifiers( expression ) )
+            {
+                sql += "case when " + statementBuilder.columnQuote( uid ) + " > 0 then 1 else 0 end + ";
+            }
+            
+            return TextUtils.removeLast( sql, "+" ) + "),0)";
+        }
+        
+        return null;
+    }
 
     private boolean isZeroOrPositive( String value )
     {

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java	2015-08-25 13:54:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/DefaultUserService.java	2015-08-25 14:09:25 +0000
@@ -267,19 +267,19 @@
 
     public boolean validateUserQueryParams( UserQueryParams params )
     {
-        if ( params.isCanManage() && (params.getUser() == null || !params.getUser().hasManagedGroups()) )
+        if ( params.isCanManage() && ( params.getUser() == null || !params.getUser().hasManagedGroups() ) )
         {
             log.warn( "Cannot get managed users as user does not have any managed groups" );
             return false;
         }
 
-        if ( params.isAuthSubset() && (params.getUser() == null || !params.getUser().getUserCredentials().hasAuthorities()) )
+        if ( params.isAuthSubset() && ( params.getUser() == null || !params.getUser().getUserCredentials().hasAuthorities() ) )
         {
             log.warn( "Cannot get users with authority subset as user does not have any authorities" );
             return false;
         }
 
-        if ( params.isDisjointRoles() && (params.getUser() == null || !params.getUser().getUserCredentials().hasUserAuthorityGroups()) )
+        if ( params.isDisjointRoles() && ( params.getUser() == null || !params.getUser().getUserCredentials().hasUserAuthorityGroups() ) )
         {
             log.warn( "Cannot get users with disjoint roles as user does not have any user roles" );
             return false;

=== modified file 'dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/TextUtils.java'
--- dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/TextUtils.java	2015-08-21 03:35:34 +0000
+++ dhis-2/dhis-support/dhis-support-commons/src/main/java/org/hisp/dhis/commons/util/TextUtils.java	2015-08-25 14:09:25 +0000
@@ -193,8 +193,8 @@
     }
     
     /**
-     * Removes the last occurence of the word "or" from the given string,
-     * including potential trailing spaces, case-insentitive.
+     * Removes the last occurrence of the word "or" from the given string,
+     * including potential trailing spaces, case-insensitive.
      * 
      * @param string the string.
      * @return the chopped string.
@@ -207,8 +207,8 @@
     }
 
     /**
-     * Removes the last occurence of the word "and" from the given string,
-     * including potential trailing spaces, case-insentitive.
+     * Removes the last occurrence of the word "and" from the given string,
+     * including potential trailing spaces, case-insensitive.
      * 
      * @param string the string.
      * @return the chopped string.
@@ -221,7 +221,7 @@
     }
 
     /**
-     * Removes the last occurence of comma (",") from the given string,
+     * Removes the last occurrence of comma (",") from the given string,
      * including potential trailing spaces.
      * 
      * @param string the string.
@@ -233,6 +233,20 @@
         
         return StringUtils.removeEndIgnoreCase( string, "," );
     }
+
+    /**
+     * Removes the last occurrence of the the given string, including potential 
+     * trailing spaces.
+     * 
+     * @param string the string.
+     * @return the chopped string.
+     */
+    public static String removeLast( String string, String remove )
+    {
+        string = StringUtils.stripEnd( string, " " );
+        
+        return StringUtils.removeEndIgnoreCase( string,  remove );
+    }
     
     /**
      * Trims the given string from the end.

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/programIndicatorForm.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/programIndicatorForm.vm	2015-08-25 09:48:55 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-program/src/main/webapp/dhis-web-maintenance-program/programIndicatorForm.vm	2015-08-25 14:09:25 +0000
@@ -15,8 +15,8 @@
 				<li><a href="#expression-tab1">$i18n.getString("data_elements")</a></li>
 				#if($program.programType=='WITH_REGISTRATION')
 					<li><a href="#expression-tab2">$i18n.getString("attributes")</a></li>
-					<li><a href="#expression-tab3">$i18n.getString("variables")</a></li>
 				#end
+				<li><a href="#expression-tab3">$i18n.getString("variables")</a></li>
 				<li><a href="#expression-tab4">$i18n.getString("constants")</a></li>
 			</ul>	
 			
@@ -43,7 +43,7 @@
 		      </table>
 			</div>
 			
-		#if($program.programType=='WITH_REGISTRATION')
+			#if($program.programType=='WITH_REGISTRATION')
 		
 			<div id="expression-tab2">
 				<table style="width:100%">
@@ -65,6 +65,8 @@
 		      </table>
 			</div>
 			
+			#end
+			
 			<div id="expression-tab3">
 				<table style="width:100%">
 				  <tr>
@@ -87,8 +89,6 @@
 				</tr>
 				</table>
 			</div>
-			
-		#end
 		
 			<div id="expression-tab4">
 				<table style="width:100%">
@@ -158,10 +158,10 @@
 		<div id="filter-tabs" style="width:670px">
 			<ul>
 				<li><a href="#filter-tab1">$i18n.getString("data_elements")</a></li>
-				#if($program.type!='3')
+				#if($program.programType=='WITH_REGISTRATION')
 					<li><a href="#filter-tab2">$i18n.getString("attributes")</a></li>
-					<li><a href="#filter-tab3">$i18n.getString("variables")</a></li>
 				#end
+				<li><a href="#filter-tab3">$i18n.getString("variables")</a></li>
 				<li><a href="#filter-tab4">$i18n.getString("constants")</a></li>
 			</ul>	
 			
@@ -187,7 +187,7 @@
 		      </table>
 			</div>
 			
-		#if($program.type!='3')
+			#if($program.programType=='WITH_REGISTRATION')
 		
 			<div id="filter-tab2">
 				<table style="width:100%">
@@ -209,6 +209,8 @@
 		      </table>
 			</div>
 			
+			#end
+			
 			<div id="filter-tab3">
 				<table style="width:100%">
 				  <tr>
@@ -231,8 +233,6 @@
 				</tr>
 				</table>
 			</div>
-						
-		#end
 		
 			<div id="filter-tab4">
 				<table style="width:100%">