← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 20133: Added support for displaying V{value_count} and V{zero_pos_value_count} in program indicators. Ad...

 

------------------------------------------------------------
revno: 20133
committer: Markus Bekken <markus.bekken@xxxxxxxxx>
branch nick: dhis2
timestamp: Mon 2015-09-14 16:10:12 +0200
message:
  Added support for displaying V{value_count} and V{zero_pos_value_count} in program indicators. Added support in program rules for functions count(), countifzeropos() and countifvalue().
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/programrule/ProgramRuleVariableSourceType.java
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableServiceTest.java
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableStoreTest.java
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/event-capture.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.filters.js
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js


--
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/programrule/ProgramRuleVariableSourceType.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/programrule/ProgramRuleVariableSourceType.java	2015-05-15 11:08:43 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/programrule/ProgramRuleVariableSourceType.java	2015-09-14 14:10:12 +0000
@@ -39,11 +39,9 @@
     DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE("dataelement_newest_event_program_stage"), 
     DATAELEMENT_NEWEST_EVENT_PROGRAM("dataelement_newest_event_program"), 
     DATAELEMENT_CURRENT_EVENT("dataelement_current_event"), 
-    DATAELEMENT_PREVIOUS_EVENT("dataelement_previous_event"), 
-    CALCULATED_VALUE("calculated_value"), 
-    TEI_ATTRIBUTE("tei_attribute"),
-    CONTEXT_VARIABLE("context_variable"),
-    NUMBEROFEVENTS_PROGRAMSTAGE("numberofevents_programstage");
+    DATAELEMENT_PREVIOUS_EVENT("dataelement_previous_event"),
+    CALCULATED_VALUE("calculated_value"),
+    TEI_ATTRIBUTE("tei_attribute");
     
     private final String value;
 

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableServiceTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableServiceTest.java	2015-06-23 15:59:19 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableServiceTest.java	2015-09-14 14:10:12 +0000
@@ -90,12 +90,11 @@
     {
         ProgramRuleVariable variableA = new ProgramRuleVariable( "RuleVariableA", programA, ProgramRuleVariableSourceType.DATAELEMENT_CURRENT_EVENT, null, dataElementA, null );
         ProgramRuleVariable variableB = new ProgramRuleVariable( "RuleVariableB", programA, ProgramRuleVariableSourceType.TEI_ATTRIBUTE, attributeA, null, null );
-        ProgramRuleVariable variableC = new ProgramRuleVariable( "RuleVariableC", programA, ProgramRuleVariableSourceType.CALCULATED_VALUE, null, null, null );
+        ProgramRuleVariable variableC = new ProgramRuleVariable( "RuleVariableC", programA, ProgramRuleVariableSourceType.DATAELEMENT_NEWEST_EVENT_PROGRAM, null, dataElementA, null );
         
         int idA = variableService.addProgramRuleVariable( variableA );
         int idB = variableService.addProgramRuleVariable( variableB );
         int idC = variableService.addProgramRuleVariable( variableC );
-        
         assertEquals( variableA, variableService.getProgramRuleVariable( idA ) );
         assertEquals( variableB, variableService.getProgramRuleVariable( idB ) );
         assertEquals( variableC, variableService.getProgramRuleVariable( idC ) );
@@ -106,9 +105,9 @@
     {
         ProgramRuleVariable variableD = new ProgramRuleVariable( "RuleVariableD", programB, ProgramRuleVariableSourceType.DATAELEMENT_CURRENT_EVENT, null, dataElementA, null );
         ProgramRuleVariable variableE = new ProgramRuleVariable( "RuleVariableE", programB, ProgramRuleVariableSourceType.TEI_ATTRIBUTE, attributeA, null, null );
-        ProgramRuleVariable variableF = new ProgramRuleVariable( "RuleVariableF", programB, ProgramRuleVariableSourceType.CALCULATED_VALUE, null, null, null );
+        ProgramRuleVariable variableF = new ProgramRuleVariable( "RuleVariableF", programB, ProgramRuleVariableSourceType.DATAELEMENT_NEWEST_EVENT_PROGRAM, null, dataElementA, null );
          //Add a var that is not part of programB....
-        ProgramRuleVariable variableG = new ProgramRuleVariable( "RuleVariableG", programA, ProgramRuleVariableSourceType.CALCULATED_VALUE, null, null, null );
+        ProgramRuleVariable variableG = new ProgramRuleVariable( "RuleVariableG", programA, ProgramRuleVariableSourceType.DATAELEMENT_NEWEST_EVENT_PROGRAM, null, dataElementA, null );
         
         variableService.addProgramRuleVariable( variableD );
         variableService.addProgramRuleVariable( variableE );
@@ -129,7 +128,7 @@
     @Test
     public void testUpdate()
     {
-        ProgramRuleVariable variableH = new ProgramRuleVariable( "RuleVariableH", programA, ProgramRuleVariableSourceType.CALCULATED_VALUE, null, null, null );
+        ProgramRuleVariable variableH = new ProgramRuleVariable( "RuleVariableH", programA, ProgramRuleVariableSourceType.DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE, null, dataElementA, null );
         
         int idH = variableService.addProgramRuleVariable( variableH );
         

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableStoreTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableStoreTest.java	2015-09-13 21:12:28 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/programrule/ProgramRuleVariableStoreTest.java	2015-09-14 14:10:12 +0000
@@ -72,7 +72,7 @@
     {
         ProgramRuleVariable varA = new ProgramRuleVariable( "VarA", programA, ProgramRuleVariableSourceType.DATAELEMENT_CURRENT_EVENT, null, dataelementA, null);
         ProgramRuleVariable varB = new ProgramRuleVariable( "VarB", programA, ProgramRuleVariableSourceType.DATAELEMENT_NEWEST_EVENT_PROGRAM, null, dataelementA, null);
-        ProgramRuleVariable varC = new ProgramRuleVariable( "VarC", programA, ProgramRuleVariableSourceType.CALCULATED_VALUE, null, null, null);
+        ProgramRuleVariable varC = new ProgramRuleVariable( "VarC", programA, ProgramRuleVariableSourceType.TEI_ATTRIBUTE, null, dataelementA, null);
         
         variableStore.save( varA );
         variableStore.save( varB );

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/event-capture.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/event-capture.js	2015-09-11 12:20:43 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/event-capture.js	2015-09-14 14:10:12 +0000
@@ -392,6 +392,10 @@
     if(catigories.length > 0 ){
         return checkAndGetD2Objects( {programs: programs, self: catigories}, 'categories', '../api/categories', 'fields=id,name,categoryOptions[id,name]');
     }
+    else
+    {
+        return programs;
+    }
 }
 
 function getOptionSets( programs )

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js	2015-09-09 08:21:34 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js	2015-09-14 14:10:12 +0000
@@ -342,7 +342,7 @@
 })
 
 /* Returns a function for getting rules for a specific program */
-.factory('TrackerRulesFactory', function($q,MetaDataFactory){
+.factory('TrackerRulesFactory', function($q,MetaDataFactory,$filter){
     return{        
         getRules : function(programUid){            
             var def = $q.defer();            
@@ -372,46 +372,102 @@
 
                             programRules.push(newRule);
 
-                            var variablesInCondition = newRule.condition.match(/#{\w+.?\w*}/g);
-                            var variablesInData = newAction.data.match(/#{\w+.?\w*}/g);
-
+                            var variablesInCondition = newRule.condition.match(/[A#]{\w+.?\w*}/g);
+                            var variablesInData = newAction.data.match(/[A#]{\w+.?\w*}/g);
+                            var valueCountPresent = newRule.condition.indexOf("V{value_count}") >= 0 
+                                                            || newAction.data.indexOf("V{value_count}") >= 0;
+                            var positiveValueCountPresent = newRule.condition.indexOf("V{zero_pos_value_count}") >= 0
+                                                            || newAction.data.indexOf("V{zero_pos_value_count}") >= 0;
+                            var variableObjectsCurrentExpression = [];
+                            
                             var pushDirectAddressedVariable = function(variableWithCurls) {
-                                var variableName = variableWithCurls.replace("#{","").replace("}","");
+                                var variableName = $filter('trimvariablequalifiers')(variableWithCurls);
+;
                                 var variableNameParts = variableName.split('.');
 
+                                var newVariableObject;
 
                                 if(variableNameParts.length === 2) {
                                     //this is a programstage and dataelement specification. translate to program variable:
-                                    variables.push({
+                                    newVariableObject = {
                                         name:variableName,
                                         programRuleVariableSourceType:'DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE',
                                         dataElement:variableNameParts[1],
                                         programStage:variableNameParts[0],
                                         program:programUid
-                                    });
+                                    };
                                 }
                                 else if(variableNameParts.length === 1)
                                 {
                                     //This is an attribute - let us translate to program variable:
-                                    variables.push({
+                                    newVariableObject = {
                                         name:variableName,
                                         programRuleVariableSourceType:'TEI_ATTRIBUTE',
                                         trackedEntityAttribute:variableNameParts[0],
                                         program:programUid
-                                    });
+                                    };
                                 }
-
+                                variables.push(newVariableObject);
+                                
+                                return newVariableObject;
+                                
                             };
-
+                            
                             angular.forEach(variablesInCondition, function(variableInCondition) {
-                                pushDirectAddressedVariable(variableInCondition);
+                                var pushed = pushDirectAddressedVariable(variableInCondition);
                             });
 
                             angular.forEach(variablesInData, function(variableInData) {
-                                pushDirectAddressedVariable(variableInData);
+                                var pushed = pushDirectAddressedVariable(variableInData);
+                                
+                                //We only count the number of values in the data part of the rule
+                                //(Called expression in program indicators)
+                                variableObjectsCurrentExpression.push(pushed);
                             });
+                            
+                            //Change expression or data part of the rule to match the program rules execution model
+                            
+                            if(valueCountPresent) {
+                                var valueCountText;
+                                angular.forEach(variableObjectsCurrentExpression, function(variableCurrentRule) {
+                                   if(valueCountText) {
+                                       //This is not the first value in the value count part of the expression. 
+                                       valueCountText +=  ' + d2:count(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                   else
+                                   {
+                                       //This is the first part value in the value count expression:
+                                       valueCountText = '(d2:count(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                });
+                                //To finish the value count expression we need to close the paranthesis:
+                                valueCountText += ')';
+
+                                //Replace all occurrences of value counts in both the data and expression:
+                                newRule.condition = newRule.condition.replace(new RegExp("V{value_count}", 'g'),valueCountText);
+                                newAction.data = newAction.data.replace(new RegExp("V{value_count}", 'g'),valueCountText);
+                            }
+                            if(positiveValueCountPresent) {
+                                var zeroPosValueCountText;
+                                angular.forEach(variableObjectsCurrentExpression, function(variableCurrentRule) {
+                                   if(zeroPosValueCountText) {
+                                       //This is not the first value in the value count part of the expression. 
+                                       zeroPosValueCountText +=  '+ d2:countifzeropos(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                   else
+                                   {
+                                       //This is the first part value in the value count expression:
+                                       zeroPosValueCountText = '(d2:countifzeropos(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                });
+                                //To finish the value count expression we need to close the paranthesis:
+                                zeroPosValueCountText += ')';
+
+                                //Replace all occurrences of value counts in both the data and expression:
+                                newRule.condition = newRule.condition.replace(new RegExp("V{zero_pos_value_count}", 'g'),zeroPosValueCountText);
+                                newAction.data = newAction.data.replace(new RegExp("V{zero_pos_value_count}", 'g'),zeroPosValueCountText);
+                            }
                         }
-                        
                     });
 
                     var programIndicators = {rules:programRules, variables:variables};

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js	2015-09-11 15:16:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js	2015-09-14 14:10:12 +0000
@@ -146,8 +146,17 @@
         }
     };
 
-    $scope.executeRules = function () {        
-        var evs = {all: $scope.allEventsSorted, byStage: $scope.eventsByStageAsc};
+    $scope.executeRules = function () {
+        //$scope.allEventsSorted cannot be used, as it is not reflecting updates that happened within the current session
+        var allSorted = [];
+        for(var ps = 0; ps < $scope.programStages.length; ps++ ) {
+            for(var e = 0; e < $scope.eventsByStageAsc[$scope.programStages[ps].id].length; e++) {
+                allSorted.push($scope.eventsByStageAsc[$scope.programStages[ps].id][e]);
+            }
+        }
+        allSorted = orderByFilter(allSorted, '-sortingDate').reverse();
+        
+        var evs = {all: allSorted, byStage: $scope.eventsByStageAsc};
         var flag = {debug: true, verbose: false};
         //If the events is displayed in a table, it is necessary to run the rules for all visible events.
         if ($scope.currentStage.displayEventsInTable) {

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js	2015-09-11 15:16:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js	2015-09-14 14:10:12 +0000
@@ -1150,7 +1150,7 @@
 })
 
 /* Returns a function for getting rules for a specific program */
-.factory('TrackerRulesFactory', function($q,MetaDataFactory){
+.factory('TrackerRulesFactory', function($q,MetaDataFactory,$filter){
     return{                
         getRules : function(programUid){            
             var def = $q.defer();            
@@ -1180,46 +1180,102 @@
 
                             programRules.push(newRule);
 
-                            var variablesInCondition = newRule.condition.match(/#{\w+.?\w*}/g);
-                            var variablesInData = newAction.data.match(/#{\w+.?\w*}/g);
-
+                            var variablesInCondition = newRule.condition.match(/[A#]{\w+.?\w*}/g);
+                            var variablesInData = newAction.data.match(/[A#]{\w+.?\w*}/g);
+                            var valueCountPresent = newRule.condition.indexOf("V{value_count}") >= 0 
+                                                            || newAction.data.indexOf("V{value_count}") >= 0;
+                            var positiveValueCountPresent = newRule.condition.indexOf("V{zero_pos_value_count}") >= 0
+                                                            || newAction.data.indexOf("V{zero_pos_value_count}") >= 0;
+                            var variableObjectsCurrentExpression = [];
+                            
                             var pushDirectAddressedVariable = function(variableWithCurls) {
-                                var variableName = variableWithCurls.replace("#{","").replace("}","");
+                                var variableName = $filter('trimvariablequalifiers')(variableWithCurls);
+;
                                 var variableNameParts = variableName.split('.');
 
+                                var newVariableObject;
 
                                 if(variableNameParts.length === 2) {
                                     //this is a programstage and dataelement specification. translate to program variable:
-                                    variables.push({
+                                    newVariableObject = {
                                         name:variableName,
                                         programRuleVariableSourceType:'DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE',
                                         dataElement:variableNameParts[1],
                                         programStage:variableNameParts[0],
                                         program:programUid
-                                    });
+                                    };
                                 }
                                 else if(variableNameParts.length === 1)
                                 {
                                     //This is an attribute - let us translate to program variable:
-                                    variables.push({
+                                    newVariableObject = {
                                         name:variableName,
                                         programRuleVariableSourceType:'TEI_ATTRIBUTE',
                                         trackedEntityAttribute:variableNameParts[0],
                                         program:programUid
-                                    });
+                                    };
                                 }
-
+                                variables.push(newVariableObject);
+                                
+                                return newVariableObject;
+                                
                             };
-
+                            
                             angular.forEach(variablesInCondition, function(variableInCondition) {
-                                pushDirectAddressedVariable(variableInCondition);
+                                var pushed = pushDirectAddressedVariable(variableInCondition);
                             });
 
                             angular.forEach(variablesInData, function(variableInData) {
-                                pushDirectAddressedVariable(variableInData);
+                                var pushed = pushDirectAddressedVariable(variableInData);
+                                
+                                //We only count the number of values in the data part of the rule
+                                //(Called expression in program indicators)
+                                variableObjectsCurrentExpression.push(pushed);
                             });
+                            
+                            //Change expression or data part of the rule to match the program rules execution model
+                            
+                            if(valueCountPresent) {
+                                var valueCountText;
+                                angular.forEach(variableObjectsCurrentExpression, function(variableCurrentRule) {
+                                   if(valueCountText) {
+                                       //This is not the first value in the value count part of the expression. 
+                                       valueCountText +=  ' + d2:count(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                   else
+                                   {
+                                       //This is the first part value in the value count expression:
+                                       valueCountText = '(d2:count(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                });
+                                //To finish the value count expression we need to close the paranthesis:
+                                valueCountText += ')';
+
+                                //Replace all occurrences of value counts in both the data and expression:
+                                newRule.condition = newRule.condition.replace(new RegExp("V{value_count}", 'g'),valueCountText);
+                                newAction.data = newAction.data.replace(new RegExp("V{value_count}", 'g'),valueCountText);
+                            }
+                            if(positiveValueCountPresent) {
+                                var zeroPosValueCountText;
+                                angular.forEach(variableObjectsCurrentExpression, function(variableCurrentRule) {
+                                   if(zeroPosValueCountText) {
+                                       //This is not the first value in the value count part of the expression. 
+                                       zeroPosValueCountText +=  '+ d2:countifzeropos(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                   else
+                                   {
+                                       //This is the first part value in the value count expression:
+                                       zeroPosValueCountText = '(d2:countifzeropos(\'' + variableCurrentRule.name + '\')';
+                                   }
+                                });
+                                //To finish the value count expression we need to close the paranthesis:
+                                zeroPosValueCountText += ')';
+
+                                //Replace all occurrences of value counts in both the data and expression:
+                                newRule.condition = newRule.condition.replace(new RegExp("V{zero_pos_value_count}", 'g'),zeroPosValueCountText);
+                                newAction.data = newAction.data.replace(new RegExp("V{zero_pos_value_count}", 'g'),zeroPosValueCountText);
+                            }
                         }
-                        
                     });
 
                     var programIndicators = {rules:programRules, variables:variables};

=== modified file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.filters.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.filters.js	2015-09-03 14:55:07 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.filters.js	2015-09-14 14:10:12 +0000
@@ -115,6 +115,19 @@
     };
 })
 
+/* trim away the qualifiers before and after a variable name */
+.filter('trimvariablequalifiers', function() {
+    return function(input) {
+        if (!input || (typeof input !== 'string' && !(input instanceof String))) {
+            return input;
+        }
+        
+        var trimmed = input.replace(/^[#VCAvca]{/,"").replace(/}$/,"");
+        
+        return trimmed;
+    };
+})
+
 .filter('forLoop', function() {
     return function(input, start, end) {
         input = new Array(end - start);

=== modified file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js	2015-09-07 07:55:07 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js	2015-09-14 14:10:12 +0000
@@ -796,49 +796,64 @@
 
 /* service for building variables based on the data in users fields */
 .service('VariableService', function(DateUtils,$filter,$log){
-    
-    var pushVariable = function(variables, variablename, variableValue, variableType, variablefound, variablePrefix) {
+    var processSingleValue = function(processedValue,valueType){
         //First clean away single or double quotation marks at the start and end of the variable name.
-        variableValue = $filter('trimquotes')(variableValue);
+        processedValue = $filter('trimquotes')(processedValue);
 
         //Append single quotation marks in case the variable is of text or date type:
-        if(variableType === 'LONG_TEXT' || variableType === 'TEXT' || variableType === 'DATE' || variableType === 'OPTION_SET') {
-            if(variableValue) {
-                variableValue = "'" + variableValue + "'";
+        if(valueType === 'LONG_TEXT' || valueType === 'TEXT' || valueType === 'DATE' || valueType === 'OPTION_SET') {
+            if(processedValue) {
+                processedValue = "'" + processedValue + "'";
             } else {
-                variableValue = "''";
+                processedValue = "''";
             }
-                
         }
-        else if(variableType === 'BOOLEAN' || variableType === 'TRUE_ONLY') {
-            if(variableValue && eval(variableValue)) {
-                variableValue = true;
+        else if(valueType === 'BOOLEAN' || valueType === 'TRUE_ONLY') {
+            if(processedValue && eval(processedValue)) {
+                processedValue = true;
             }
             else {
-                variableValue = false;
+                processedValue = false;
             }
         }
-        else if(variableType === "INTEGER" || variableType === "NUMBER" || variableType === "INTEGER_POSITIVE" || variableType === "INTEGER_NEGATIVE" || variableType === "INTEGER_ZERO_OR_POSITIVE" || variableType === "PERCENTAGE") {
-            if(variableValue) {
-                variableValue = Number(variableValue);
+        else if(valueType === "INTEGER" || valueType === "NUMBER" || valueType === "INTEGER_POSITIVE" || valueType === "INTEGER_NEGATIVE" || valueType === "INTEGER_ZERO_OR_POSITIVE" || valueType === "PERCENTAGE") {
+            if(processedValue) {
+                processedValue = Number(processedValue);
             } else {
-                variableValue = 0;
+                processedValue = 0;
             }  
         }
         else{
-            $log.warn("unknown datatype:" + variableType);
+            $log.warn("unknown datatype:" + valueType);
         }
-
+        
+        return processedValue;
+    };
+
+    var pushVariable = function(variables, variablename, varValue, allValues, varType, variablefound, variablePrefix) {
+        
+
+        
+        var processedValues = [];
+        
+        angular.forEach(allValues, function(alternateValue) {
+            processedValues.push(processSingleValue(alternateValue,varType));
+        });
+        
         variables[variablename] = {
-                        variableValue:variableValue,
-                        variableType:variableType,
+                        variableValue:processSingleValue(varValue, varType),
+                        variableType:varType,
                         hasValue:variablefound,
-                        variablePrefix:variablePrefix
+                        variablePrefix:variablePrefix,
+                        allValues:processedValues
                     };
         return variables;            
     };
     
-    return {        
+    return {
+        processValue: function(value, type) {
+            return processSingleValue(value,type);
+        },
         processVariables: function(variables, variablename, variableValue, variableType, variablefound, variablePrefix) {            
             return pushVariable(variables, variablename, variableValue, variableType, variablefound, variablePrefix);
         },
@@ -856,6 +871,11 @@
                 if(programVariable.dataElement && programVariable.dataElement.id) {
                     dataElementId = programVariable.dataElement.id;
                 }
+                
+                var trackedEntityAttributeId = programVariable.trackedEntityAttribute;
+                if(programVariable.trackedEntityAttribute && programVariable.trackedEntityAttribute.id) {
+                    trackedEntityAttributeId = programVariable.trackedEntityAttribute.id;
+                }
 
                 var programStageId = programVariable.programStage;
                 if(programVariable.programStage && programVariable.programStage.id) {
@@ -866,11 +886,14 @@
                 //If variable evs is not defined, it means the rules is run before any events is registered, skip the types that require an event
                 if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE" && evs){
                     if(programStageId) {
+                        var allValues = [];
                         angular.forEach(evs.byStage[programStageId], function(event) {
-                            if(angular.isDefined(event[dataElementId])
-                                    && event[dataElementId] !== null ){
-                                valueFound = true;
-                                variables = pushVariable(variables, programVariable.name, event[dataElementId], allDes[dataElementId].dataElement.valueType, valueFound, '#');
+                            if(event[dataElementId] !== null) {
+                                if(angular.isDefined(event[dataElementId])){
+                                    allValues.push(event[dataElementId]);
+                                    valueFound = true;
+                                    variables = pushVariable(variables, programVariable.name, event[dataElementId],allValues, allDes[dataElementId].dataElement.valueType, valueFound, '#');
+                                }
                             }
                         });
                     } else {
@@ -878,14 +901,15 @@
                                 + "' does not have a programstage defined,"
                                 + " despite that the variable has sourcetype DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE" );
                     }
-
                 }
                 else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM" && evs){
+                    var allValues = [];
                     angular.forEach(evs.all, function(event) {
                         if(angular.isDefined(event[dataElementId])
                                 && event[dataElementId] !== null ){
+                            allValues.push(event[dataElementId]);
                             valueFound = true;
-                            variables = pushVariable(variables, programVariable.name, event[dataElementId], allDes[dataElementId].dataElement.valueType, valueFound, '#' );
+                            variables = pushVariable(variables, programVariable.name, event[dataElementId], allValues, allDes[dataElementId].dataElement.valueType, valueFound, '#' );
                          }
                     });
                 }
@@ -893,12 +917,13 @@
                     if(angular.isDefined(executingEvent[dataElementId])
                             && executingEvent[dataElementId] !== null ){
                         valueFound = true;
-                        variables = pushVariable(variables, programVariable.name, executingEvent[dataElementId], allDes[dataElementId].dataElement.valueType, valueFound, '#' );
-                    }      
+                        variables = pushVariable(variables, programVariable.name, executingEvent[dataElementId], null, allDes[dataElementId].dataElement.valueType, valueFound, '#' );
+                    }
                 }
                 else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_PREVIOUS_EVENT" && evs){
                     //Only continue checking for a value if there is more than one event.
                     if(evs.all && evs.all.length > 1) {
+                        var allValues = [];
                         var previousvalue = null;
                         var currentEventPassed = false;
                         for(var i = 0; i < evs.all.length; i++) {
@@ -907,12 +932,13 @@
                             if(!currentEventPassed && evs.all[i] !== executingEvent && 
                                     angular.isDefined(evs.all[i][dataElementId])) {
                                 previousvalue = evs.all[i][dataElementId];
+                                allValues.push(previousvalue);
                                 valueFound = true;
                             }
                             else if(evs.all[i] === executingEvent) {
                                 //We have iterated to the newest event - store the last collected variable value - if any is found:
                                 if(valueFound) {
-                                    variables = pushVariable(variables, programVariable.name, previousvalue, allDes[dataElementId].dataElement.valueType, valueFound, '#' );
+                                    variables = pushVariable(variables, programVariable.name, previousvalue, allValues, allDes[dataElementId].dataElement.valueType, valueFound, '#' );
                                 }
                                 //Set currentEventPassed, ending the iteration:
                                 currentEventPassed = true;
@@ -923,11 +949,11 @@
                 else if(programVariable.programRuleVariableSourceType === "TEI_ATTRIBUTE"){
                     angular.forEach(selectedEntity.attributes , function(attribute) {
                         if(!valueFound) {
-                            if(attribute.attribute === programVariable.trackedEntityAttribute.id) {
+                            if(attribute.attribute === trackedEntityAttributeId) {
                                 valueFound = true;
                                 //In registration, the attribute type is found in .type, while in data entry the same data is found in .valueType. 
                                 //Handling here, but planning refactor in registration so it will always be .valueType
-                                variables = pushVariable(variables, programVariable.name, attribute.value, attribute.type ? attribute.type : attribute.valueType, valueFound, 'A' );
+                                variables = pushVariable(variables, programVariable.name, attribute.value, null, attribute.type ? attribute.type : attribute.valueType, valueFound, 'A' );
                             }
                         }
                     });
@@ -941,7 +967,7 @@
                         numberOfEvents = evs.byStage[programStageId].length;
                     }
                     valueFound = true;
-                    variables = pushVariable(variables, programVariable.name, numberOfEvents, 'int', valueFound, '#' );
+                    variables = pushVariable(variables, programVariable.name, numberOfEvents, null, 'int', valueFound, 'V' );
                 }
                 else {
                     //If the rules was executed without events, we ended up in this else clause as expected, as most of the variables require an event to be mapped
@@ -958,38 +984,37 @@
                     if(dataElementId && allDes) {
                         var dataElement = allDes[dataElementId];
                         if( dataElement ) {
-                            variables = pushVariable(variables, programVariable.name, "", dataElement.dataElement.valueType, false, '#' );
+                            variables = pushVariable(variables, programVariable.name, "", null, dataElement.dataElement.valueType, false, '#' );
                         } 
                         else {
                             $log.warn("Variable #{" + programVariable.name + "} is linked to a dataelement that is not part of the program");
-                            variables = pushVariable(variables, programVariable.name, "", "TEXT",false, '#' );
+                            variables = pushVariable(variables, programVariable.name, "", null, "TEXT",false, '#' );
                         }
                     }
                     else if (programVariable.trackedEntityAttribute) {
                         //The variable is an attribute, set correct prefix and a blank value
-                        variables = pushVariable(variables, programVariable.name, "", "TEXT",false, 'A' );
+                        variables = pushVariable(variables, programVariable.name, "", null, "TEXT",false, 'A' );
                     }
                     else {
                         //Fallback for calculated(assigned) values:
-                        variables = pushVariable(variables, programVariable.name, "", "TEXT",false, '#' );
+                        variables = pushVariable(variables, programVariable.name, "", null, "TEXT",false, '#' );
                     }
                 }
             });
 
             //add context variables:
             //last parameter "valuefound" is always true for event date
-            variables = pushVariable(variables, 'incident_date', executingEvent.eventDate, 'DATE', true, 'V' );
-            variables = pushVariable(variables, 'current_date', DateUtils.getToday(), 'DATE', true, 'V' );
+            variables = pushVariable(variables, 'incident_date', executingEvent.eventDate, null, 'DATE', true, 'V' );
+            variables = pushVariable(variables, 'current_date', DateUtils.getToday(), null, 'DATE', true, 'V' );
             if(selectedEnrollment){
-                variables = pushVariable(variables, 'enrollment_date', selectedEnrollment.dateOfEnrollment, 'DATE', true, 'V' );
+                variables = pushVariable(variables, 'enrollment_date', selectedEnrollment.dateOfEnrollment, null, 'DATE', true, 'V' );
+                variables = pushVariable(variables, 'enrollment_id', selectedEnrollment.enrollment, null, 'TEXT', true, 'V');
             }
 
-            //variables = pushVariable(variables, 'value_count', executingEvent.eventDate, 'DATE', true, 'V' );
-            //variables = pushVariable(variables, 'zero_pos_value_count', executingEvent.eventDate, 'DATE', true, 'V' );
-
+                        
             //Push all constant values:
             angular.forEach(allProgramRules.constants, function(constant){
-                variables = pushVariable(variables, constant.id, constant.value, 'INTEGER', true, 'C' );
+                variables = pushVariable(variables, constant.id, constant.value, null, 'INTEGER', true, 'C' );
             });
 
             return variables;
@@ -1104,11 +1129,14 @@
                                 {name:"d2:concatenate"},
                                 {name:"d2:adddays",parameters:2},
                                 {name:"d2:zing",parameters:1},
-                                {name:"d2:oizp",parameters:1}];
+                                {name:"d2:oizp",parameters:1},
+                                {name:"d2:count",parameters:1},
+                                {name:"d2:countifzeropos",parameters:1},
+                                {name:"d2:countifvalue",parameters:2}];
 
             angular.forEach(dhisFunctions, function(dhisFunction){
-                //Replace each * with a regex that matches each parameter, allowing commas only inside single quotation marks.
-                var regularExFunctionCall = new RegExp(dhisFunction.name.replace(".","\\.") + "\\([^\\)]*\\)",'g');
+                //Select the function call, with any number of parameters inside single quotations, or number parameters witout quotations
+                var regularExFunctionCall = new RegExp(dhisFunction.name + "\\( *((\d+)|( *'[^']*'))*( *, *((\d+)|'[^']*'))* *\\)",'g');
                 var callsToThisFunction = expression.match(regularExFunctionCall);
                 angular.forEach(callsToThisFunction, function(callToThisFunction){
                     //Remove the function name and paranthesis:
@@ -1134,57 +1162,51 @@
                     }
 
                     //Special block for d2:weeksBetween(*,*) - add such a block for all other dhis functions.
-                    if(dhisFunction.name === "d2:daysbetween")
-                    {
+                    if(dhisFunction.name === "d2:daysbetween") {
                         var firstdate = $filter('trimquotes')(parameters[0]);
                         var seconddate = $filter('trimquotes')(parameters[1]);
                         firstdate = moment(firstdate);
                         seconddate = moment(seconddate);
                         //Replace the end evaluation of the dhis function:
                         expression = expression.replace(callToThisFunction, seconddate.diff(firstdate,'days'));
-                    }
-                    else if(dhisFunction.name === "d2:yearsbetween")
-                    {
+                    } 
+                    else if(dhisFunction.name === "d2:yearsbetween") {
                         var firstdate = $filter('trimquotes')(parameters[0]);
                         var seconddate = $filter('trimquotes')(parameters[1]);
                         firstdate = moment(firstdate);
                         seconddate = moment(seconddate);
                         //Replace the end evaluation of the dhis function:
                         expression = expression.replace(callToThisFunction, seconddate.diff(firstdate,'years'));
-                    }
-                    else if(dhisFunction.name === "d2:floor")
-                    {
+                    } 
+                    else if(dhisFunction.name === "d2:floor") {
                         var floored = Math.floor(parameters[0]);
                         //Replace the end evaluation of the dhis function:
                         expression = expression.replace(callToThisFunction, floored);
-                    }
-                    else if(dhisFunction.name === "d2:modulus")
-                    {
+                    } 
+                    else if(dhisFunction.name === "d2:modulus") {
                         var dividend = Number(parameters[0]);
                         var divisor = Number(parameters[1]);
                         var rest = dividend % divisor;
                         //Replace the end evaluation of the dhis function:
                         expression = expression.replace(callToThisFunction, rest);
-                    }
-                    else if(dhisFunction.name === "d2:concatenate")
-                    {
+                    } 
+                    else if(dhisFunction.name === "d2:concatenate") {
                         var returnString = "'";
                         for (var i = 0; i < parameters.length; i++) {
                             returnString += parameters[i];
                         }
                         returnString += "'";
                         expression = expression.replace(callToThisFunction, returnString);
-                    }
-                    else if(dhisFunction.name === "d2:adddays")
-                    {
+                    } 
+                    else if(dhisFunction.name === "d2:adddays") {
                         var date = $filter('trimquotes')(parameters[0]);
                         var daystoadd = $filter('trimquotes')(parameters[1]);
                         var newdate = DateUtils.format( moment(date, CalendarService.getSetting().momentFormat).add(daystoadd, 'days') );
                         var newdatestring = "'" + newdate + "'";
                         //Replace the end evaluation of the dhis function:
                         expression = expression.replace(callToThisFunction, newdatestring);
-                    }else if(dhisFunction.name === "d2:zing")
-                    {
+                    } 
+                    else if(dhisFunction.name === "d2:zing") {
                         var number = parameters[0];
                         if( number < 0 ) {
                             number = 0;
@@ -1192,8 +1214,8 @@
                         
                         //Replace the end evaluation of the dhis function:
                         expression = expression.replace(callToThisFunction, number);
-                    }else if(dhisFunction.name === "d2:oizp")
-                    {
+                    } 
+                    else if(dhisFunction.name === "d2:oizp") {
                         var number = parameters[0];
                         var output = 1;
                         if( number < 0 ) {
@@ -1203,6 +1225,100 @@
                         //Replace the end evaluation of the dhis function:
                         expression = expression.replace(callToThisFunction, output);
                     }
+                    else if(dhisFunction.name === "d2:count") {
+                        var variableName = parameters[0];
+                        var variableObject = variablesHash[variableName];
+                        var count = 0;
+                        if(variableObject)
+                        {
+                            if(variableObject.hasValue){
+                                if(variableObject.allValues)
+                                {
+                                    count = variableObject.allValues.length;
+                                } else {
+                                    //If there is a value found for the variable, the count is 1 even if there is no list of alternate values
+                                    //This happens for variables of "DATAELEMENT_CURRENT_STAGE" and "TEI_ATTRIBUTE"
+                                    count = 1;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            $log.warn("could not find variable to count: " + variableName);
+                        }
+                        
+                        //Replace the end evaluation of the dhis function:
+                        expression = expression.replace(callToThisFunction, count);
+                    }
+                    else if(dhisFunction.name === "d2:countifzeropos") {
+                        var variableName = $filter('trimvariablequalifiers') (parameters[0]);
+                        var variableObject = variablesHash[variableName];
+                        
+                        var count = 0;
+                        if(variableObject)
+                        {
+                            if( variableObject.hasValue ) {
+                                if(variableObject.allValues && variableObject.allValues.length > 0)
+                                {
+                                    for(var i = 0; i < variableObject.allValues.length; i++)
+                                    {
+                                        if(variableObject.allValues[i] >= 0) {
+                                            count++;
+                                        }
+                                    }
+                                }
+                                else {
+                                    //The variable has a value, but no list of alternates. This means we only compare the elements real value
+                                    if(variableObject.variableValue >= 0) {
+                                        count = 1;
+                                    }
+                                }
+                            }
+                        }
+                        else
+                        {
+                            $log.warn("could not find variable to countifzeropos: " + variableName);
+                        }
+                        
+                        //Replace the end evaluation of the dhis function:
+                        expression = expression.replace(callToThisFunction, count);
+                    }
+                    else if(dhisFunction.name === "d2:countifvalue") {
+                        var variableName = parameters[0];
+                        var variableObject = variablesHash[variableName];
+                        
+                        var valueToCompare = VariableService.processValue(parameters[1],variableObject.variableType);
+                                
+                        var count = 0;
+                        if(variableObject)
+                        {
+                            if( variableObject.hasValue )
+                            {
+                                if( variableObject.allValues )
+                                {
+                                    for(var i = 0; i < variableObject.allValues.length; i++)
+                                    {
+                                        if(valueToCompare === variableObject.allValues[i]) {
+                                            count++;
+                                        }
+                                    }
+                                } else {
+                                    //The variable has a value, but no list of alternates. This means we compare the standard variablevalue
+                                    if(valueToCompare === variableObject.variableValue) {
+                                        count = 1;
+                                    }
+                                }
+                                
+                            }
+                        }
+                        else
+                        {
+                            $log.warn("could not find variable to countifvalue: " + variableName);
+                        }
+                        
+                        //Replace the end evaluation of the dhis function:
+                        expression = expression.replace(callToThisFunction, count);
+                    }
                 });
             });
         }
@@ -1332,9 +1448,10 @@
                                     $rootScope.ruleeffects[ruleEffectKey][action.id].data =
                                         variablesHash[nameWithoutBrackets].variableValue;
                                 }
-                                else if(action.data.indexOf('{') !== -1)
+                                else if(action.data.indexOf('{') !== -1 || action.data.indexOf('d2:') !== -1)
                                 {
-                                    //Since the value couldnt be looked up directly, and contains a dollar sign, the expression was more complex
+                                    //Since the value couldnt be looked up directly, and contains a curly brace or a dhis function call,
+                                    //the expression was more complex than replacing a single variable value.
                                     //Now we will have to make a thorough replacement and separate evaluation to find the correct value:
                                     $rootScope.ruleeffects[ruleEffectKey][action.id].data = replaceVariables(action.data, variablesHash);
                                     //In a scenario where the data contains a complex expression, evaluate the expression to compile(calculate) the result: