dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #39723
[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: