------------------------------------------------------------
revno: 19419
committer: Abyot Asalefew Gizaw <aby...@gmail.com>
branch nick: dhis2
timestamp: Tue 2015-06-16 13:22:51 +0200
message:
  moved services related to rules engine to dhis-web-commons-resources
modified:
  
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.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-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-06-14 13:07:10 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js	2015-06-16 11:22:51 +0000
@@ -1773,469 +1773,4 @@
             return event;
         }
     }; 
-})
-
-/* service for building variables based on the data in users fields */
-.service('VariableService', function($rootScope,$q,TrackerRuleVariableFactory,$filter,orderByFilter,$log){
-    return {
-        getVariables: function(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity) {
-            var thePromisedVariables = $q.defer();
-            var variables = {};
-            
-            var pushVariable = function(variablename, variableValue, variableType, variablefound) {
-                //First clean away single or double quotation marks at the start and end of the variable name.
-                variableValue = $filter('trimquotes')(variableValue);
-                
-                //Append single quotation marks in case the variable is of text type:
-                if(variableType === 'string') {
-                    variableValue = "'" + variableValue + "'";
-                }
-                else if(variableType === 'date') {
-                    variableValue = "'" + variableValue + "'";
-                }
-                else if(variableType === 'bool' || variableType === 'trueOnly') {
-                    if(eval(variableValue)) {
-                        variableValue = true;
-                    }
-                    else {    
-                        variableValue = false;
-                    }
-                }
-                else if(variableType === "int" || variableType === "number") {
-                    variableValue = Number(variableValue);
-                }
-                else{
-                    $log.warn("unknown datatype:" + variableType);
-                }
-                 
-                
-                //Make sure that the variableValue does not contain a dollar sign anywhere 
-                //- this would potentially mess up later use of the variable:
-//                if(angular.isDefined(variableValue) 
-//                        && variableValue !== null
-//                        && variableValue.indexOf("$") !== -1 ) {
-//                    variableValue = variableValue.replace(/\\$/,"");
-//                }
-                
-                //TODO:
-                //Also clean away instructions that might be erroneusly evalutated in javascript
-
-                variables[variablename] = {
-                                variableValue:variableValue,
-                                variableType:variableType,
-                                hasValue:variablefound
-                            };
-            };
-            
-            TrackerRuleVariableFactory.getProgramRuleVariables(programid).then(function(programVariables){
-
-                // The following section will need a different implementation for event capture:
-                var allEventsSorted = [];
-                var currentEvent = executingEvent;
-                var eventsSortedPerProgramStage = [];
-                
-                for(var key in allEventsByStage){
-                    if(allEventsByStage.hasOwnProperty(key)){
-                        eventsSortedPerProgramStage[key] = [];
-                        angular.forEach(allEventsByStage[key], function(event){
-                            allEventsSorted.push(event);
-                            eventsSortedPerProgramStage[key].push(event);
-                        });
-                        eventsSortedPerProgramStage[key] = orderByFilter(eventsSortedPerProgramStage[key], '-sortingDate').reverse(); 
-                    }
-                }
-                allEventsSorted = orderByFilter(allEventsSorted, '-sortingDate').reverse(); 
-                
-                var allDes = allDataElements;
-//                angular.forEach($scope.programStages, function(programStage){
-//                    angular.forEach(programStage.programStageDataElements, function(dataElement) {
-//                        allDes[dataElement.dataElement.id] = dataElement;
-//                    });
-//                });
-                //End of region that neeeds specific implementation for event capture
-                
-                angular.forEach(programVariables, function(programVariable) {
-                    var valueFound = false;
-                    if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE"){
-                        if(programVariable.programStage) {
-                            angular.forEach(eventsSortedPerProgramStage[programVariable.programStage.id], function(event) {
-                                if(angular.isDefined(event[programVariable.dataElement.id])
-                                        && event[programVariable.dataElement.id] !== null ){
-                                    valueFound = true;
-                                    pushVariable(programVariable.name, event[programVariable.dataElement.id], allDes[programVariable.dataElement.id].dataElement.type, valueFound );
-                                }
-                            });
-                        } else {
-                            $log.warn("Variable id:'" + programVariable.id + "' name:'" + programVariable.name 
-                                    + "' 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"){
-                        angular.forEach(allEventsSorted, function(event) {
-                            if(angular.isDefined(event[programVariable.dataElement.id])
-                                    && event[programVariable.dataElement.id] !== null ){
-                                valueFound = true;
-                                 pushVariable(programVariable.name, event[programVariable.dataElement.id], allDes[programVariable.dataElement.id].dataElement.type, valueFound );
-                             }
-                        });
-                    }
-                    else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_CURRENT_EVENT"){
-                        if(angular.isDefined(currentEvent[programVariable.dataElement.id])
-                                && currentEvent[programVariable.dataElement.id] !== null ){
-                            valueFound = true;
-                            pushVariable(programVariable.name, currentEvent[programVariable.dataElement.id], allDes[programVariable.dataElement.id].dataElement.type, valueFound );
-                        }      
-                    }
-                    else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_PREVIOUS_EVENT"){
-                        //Only continue checking for a value if there is more than one event.
-                        if(allEventsSorted && allEventsSorted.length > 1) {
-                            var previousvalue = null;
-                            var currentEventPassed = false;
-                            for(var i = 0; i < allEventsSorted.length; i++) {
-                                //Store the values as we iterate through the stages
-                                //If the event[i] is not the current event, it is older(previous). Store the previous value if it exists
-                                if(!currentEventPassed && allEventsSorted[i] !== currentEvent && 
-                                        angular.isDefined(allEventsSorted[i][programVariable.dataElement.id])) {
-                                    previousvalue = allEventsSorted[i][programVariable.dataElement.id];
-                                    valueFound = true;
-                                }
-                                else if(allEventsSorted[i] === currentEvent) {
-                                    //We have iterated to the newest event - store the last collected variable value - if any is found:
-                                    if(valueFound) {
-                                        pushVariable(programVariable.name, previousvalue, allDes[programVariable.dataElement.id].dataElement.type, valueFound );
-                                    }
-                                    //Set currentEventPassed, ending the iteration:
-                                    currentEventPassed = true;
-                                }
-                            }
-                        }
-                    }
-                    else if(programVariable.programRuleVariableSourceType === "TEI_ATTRIBUTE"){
-                        angular.forEach(selectedEntity.attributes , function(attribute) {
-                            if(!valueFound) {
-                                if(attribute.attribute === programVariable.trackedEntityAttribute.id) {
-                                    valueFound = true;
-                                    pushVariable(programVariable.name, attribute.value, attribute.type, valueFound );
-                                }
-                            }
-                        });
-                    }
-                    else if(programVariable.programRuleVariableSourceType === "CALCULATED_VALUE"){
-                        //We won't assign the calculated variables at this step. The rules execution will calculate and assign the variable.
-                    }
-                    else if(programVariable.programRuleVariableSourceType === "NUMBEROFEVENTS_PROGRAMSTAGE"){
-                        var numberOfEvents = 0;
-                        if( programVariable.programStage && eventsSortedPerProgramStage[programVariable.programStage.id] ) {
-                            numberOfEvents = eventsSortedPerProgramStage[programVariable.programStage.id].length;
-                        }
-                        valueFound = true;
-                        pushVariable(programVariable.name, numberOfEvents, 'int', valueFound );
-                    }
-                    else {
-                        //Missing handing of ruletype
-                        $log.warn("Unknown programRuleVariableSourceType:" + programVariable.programRuleVariableSourceType);
-                    }
-
-                   
-                    if(!valueFound){
-                        //If there is still no value found, assign default value:
-                        if(programVariable.dataElement) {
-                            var dataElement = allDes[programVariable.dataElement.id];
-                            if( dataElement ) {
-                                pushVariable(programVariable.name, "", dataElement.dataElement.type );
-                            } 
-                            else {
-                                $log.warn("Variable #{" + programVariable.name + "} is linked to a dataelement that is not part of the program");
-                                pushVariable(programVariable.name, "", "string" );
-                            }
-                        }
-                        else {
-                            pushVariable(programVariable.name, "", "string" );
-                        }
-                    }
-                });
-
-                //add context variables:
-                //last parameter "valuefound" is always true for event date
-                pushVariable('eventdate', executingEvent.eventDate, 'date', true );
-                
-                thePromisedVariables.resolve(variables);
-            });
-            
-            return thePromisedVariables.promise;
-        }
-    };
-})
-       
-
-
-/* service for executing tracker rules and broadcasting results */
-.service('TrackerRulesExecutionService', function(TrackerRulesFactory,VariableService, $rootScope, $log, $filter, orderByFilter){
-    return {
-        executeRules: function(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity) {
-            //When debugging rules, the caller should provide a variable for wether or not the rules is being debugged.
-            //hard coding this for now:
-            var debug = true;
-            var verbose = true;
-            
-            var variablesHash = {};
-                    
-            var replaceVariables = function(expression) {
-                //replaces the variables in an expression with actual variable values.
-                //First check if the expression contains variables at all(any dollar signs):
-                if(expression.indexOf('#') !== -1) {
-                    //Find every variable name in the expression;
-                    var variablespresent = expression.match(/#{\w+}/g);
-                    //Replace each matched variable:
-                    angular.forEach(variablespresent, function(variablepresent) {
-                        //First strip away any dollar signs from the variable name:
-                        variablepresent = variablepresent.replace("#{","").replace("}","");
-                        
-                        if(angular.isDefined(variablesHash[variablepresent])) {
-                            //Replace all occurrences of the variable name(hence using regex replacement):
-                            expression = expression.replace(new RegExp("#{" + variablepresent + "}", 'g'),
-                                variablesHash[variablepresent].variableValue);
-                        }
-                        else {
-                            $log.warn("Expression " + expression + " conains variable " + variablepresent 
-                                    + " - but this variable is not defined." );
-                        }
-                            
-                    });
-                }
-                return expression;
-            };
-            
-            var runDhisFunctions = function(expression) {
-                //Called from "runExpression". Only proceed with this logic in case there seems to be dhis function calls: "dhis." is present.
-                if(angular.isDefined(expression) && expression.indexOf("dhis.") !== -1){   
-                    var dhisFunctions = [{name:"dhis.daysbetween",parameters:2},
-                                        {name:"dhis.floor",parameters:1},
-                                        {name:"dhis.modulus",parameters:2},
-                                        {name:"dhis.hasValue",parameters:1},
-                                        {name:"dhis.concatenate"}];
-                    
-                    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');
-                        var callsToThisFunction = expression.match(regularExFunctionCall);
-                        angular.forEach(callsToThisFunction, function(callToThisFunction){
-                            //Remove the function name and paranthesis:
-                            var justparameters = callToThisFunction.replace(/(^[^\(]+\()|\)$/g,"");
-                            //Then split into single parameters:
-                            var parameters = justparameters.match(/(('[^']+')|([^,]+))/g);
-                            
-                            //Show error if no parameters is given and the function requires parameters,
-                            //or if the number of parameters is wrong.
-                            if(angular.isDefined(dhisFunction.parameters)){
-                                //But we are only checking parameters where the dhisFunction actually has a defined set of parameters(concatenate, for example, does not have a fixed number);
-                                if((!angular.isDefined(parameters) && dhisFunction.parameters > 0)
-                                        || parameters.length !== dhisFunction.parameters){
-                                    $log.warn(dhisFunction.name + " was called with the incorrect number of parameters");
-                                }
-                            }
-
-                            //In case the function call is nested, the parameter itself contains an expression, run the expression.
-                            if(angular.isDefined(parameters)) {
-                                for (var i = 0; i < parameters.length; i++) {
-                                    parameters[i] = runExpression(parameters[i],dhisFunction.name,"parameter:" + i);
-                                }
-                            }
-
-                            //Special block for dhis.weeksBetween(*,*) - add such a block for all other dhis functions.
-                            if(dhisFunction.name === "dhis.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 === "dhis.floor")
-                            {
-                                var floored = Math.floor(parameters[0]);
-                                //Replace the end evaluation of the dhis function:
-                                expression = expression.replace(callToThisFunction, floored);
-                            }
-                            else if(dhisFunction.name === "dhis.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 === "dhis.hasValue")
-                            {
-                                //"evaluate" hasvalue to true or false:
-                                if(variablesHash[parameters[0]].hasValue){
-                                    expression = expression.replace(callToThisFunction, 'true');
-                                } else {
-                                    expression = expression.replace(callToThisFunction, 'false');
-                                }
-                            }
-                            else if(dhisFunction.name === "dhis.concatenate")
-                            {
-                                var returnString = "'";
-                                for (var i = 0; i < parameters.length; i++) {
-                                    returnString += parameters[i];
-                                }
-                                returnString += "'";
-                                expression = expression.replace(callToThisFunction, returnString);
-                            }
-                        });
-                    });
-                }
-                
-                return expression;
-            };
-            
-            var runExpression = function(expression, beforereplacement, identifier ){
-                //determine if expression is true, and actions should be effectuated
-                //If DEBUG mode, use try catch and report errors. If not, omit the heavy try-catch loop.:
-                var answer = false;
-                if(debug) {
-                    try{
-                        
-                        var dhisfunctionsevaluated = runDhisFunctions(expression);
-                        answer = eval(dhisfunctionsevaluated);
-
-                        if(verbose)
-                        {
-                            $log.info("Expression with id " + identifier + " was successfully run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - Result of evaluation was:" + answer);
-                        }
-                    }
-                    catch(e)
-                    {
-                        $log.warn("Expression with id " + identifier + " could not be run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - error message:" + e);
-                    }
-                }
-                else {
-                    //Just run the expression. This is much faster than the debug route: http://jsperf.com/try-catch-block-loop-performance-comparison
-                    var dhisfunctionsevaluated = runDhisFunctions(expression);
-                    answer = eval(dhisfunctionsevaluated);
-                }
-                return answer;
-            };
-            
-            
-            VariableService.getVariables(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity).then(function(variablesReceived){
-                TrackerRulesFactory.getProgramStageRules(programid, executingEvent.programStage).then(function(rules){
-                    //But run rules in priority - lowest number first(priority null is last)
-                    rules = orderByFilter(rules, 'priority');
-                    
-                    variablesHash = variablesReceived;
-
-                    if(angular.isObject(rules) && angular.isArray(rules)){
-                        //The program has rules, and we want to run them.
-                        //Prepare repository unless it is already prepared:
-                        if(angular.isUndefined( $rootScope.ruleeffects ) ) {
-                            $rootScope.ruleeffects = {};
-                        }
-                            
-                        if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event] )){
-                            $rootScope.ruleeffects[executingEvent.event] = {};
-                        }
-
-                        var updatedEffectsExits = false;
-
-                        angular.forEach(rules, function(rule) {
-                            var ruleEffective = false;
-
-                            var expression = rule.condition;
-                            //Go through and populate variables with actual values, but only if there actually is any replacements to be made(one or more "$" is present)
-                            if(expression) {
-                                if(expression.indexOf('#') !== -1) {
-                                    expression = replaceVariables(expression);
-                                }
-                                //run expression:
-                                ruleEffective = runExpression(expression, rule.condition, "rule:" + rule.id);
-                            } else {
-                                $log.warn("Rule id:'" + rule.id + "'' and name:'" + rule.name + "' had no condition specified. Please check rule configuration.");
-                            }
-                            
-                            angular.forEach(rule.actions, function(action){
-                                //In case the effect-hash is not populated, add entries
-                                if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event][action.id] )){
-                                    $rootScope.ruleeffects[executingEvent.event][action.id] =  {
-                                        id:action.id,
-                                        location:action.location, 
-                                        action:action.programRuleActionType,
-                                        dataElement:action.dataElement,
-                                        content:action.content,
-                                        data:action.data,
-                                        ineffect:false
-                                    };
-                                }
-
-                                //In case the rule is effective and contains specific data, 
-                                //the effect be refreshed from the variables list.
-                                //If the rule is not effective we can skip this step
-                                if(ruleEffective && action.data)
-                                {
-                                    //The key data might be containing a dollar sign denoting that the key data is a variable.
-                                    //To make a lookup in variables hash, we must make a lookup without the dollar sign in the variable name
-                                    //The first strategy is to make a direct lookup. In case the "data" expression is more complex, we have to do more replacement and evaluation.
-
-                                    var nameWithoutBrackets = action.data.replace('#{','').replace('}','');
-                                    if(angular.isDefined(variablesHash[nameWithoutBrackets]))
-                                    {
-                                        //The variable exists, and is replaced with its corresponding value
-                                        $rootScope.ruleeffects[executingEvent.event][action.id].data =
-                                            variablesHash[nameWithoutBrackets].variableValue;
-                                    }
-                                    else if(action.data.indexOf('#') !== -1)
-                                    {
-                                        //Since the value couldnt be looked up directly, and contains a dollar sign, the expression was more complex
-                                        //Now we will have to make a thorough replacement and separate evaluation to find the correct value:
-                                        $rootScope.ruleeffects[executingEvent.event][action.id].data = replaceVariables(action.data);
-                                        //In a scenario where the data contains a complex expression, evaluate the expression to compile(calculate) the result:
-                                        $rootScope.ruleeffects[executingEvent.event][action.id].data = runExpression($rootScope.ruleeffects[executingEvent.event][action.id].data, action.data, "action:" + action.id);
-                                    }
-                                }
-
-                                //Update the rule effectiveness if it changed in this evaluation;
-                                if($rootScope.ruleeffects[executingEvent.event][action.id].ineffect !== ruleEffective)
-                                {
-                                    //There is a change in the rule outcome, we need to update the effect object.
-                                    updatedEffectsExits = true;
-                                    $rootScope.ruleeffects[executingEvent.event][action.id].ineffect = ruleEffective;
-                                }
-
-                                //In case the rule is of type "assign variable" and the rule is effective,
-                                //the variable data result needs to be applied to the correct variable:
-                                if($rootScope.ruleeffects[executingEvent.event][action.id].action === "ASSIGNVARIABLE" && $rootScope.ruleeffects[executingEvent.event][action.id].ineffect){
-                                    //from earlier evaluation, the data portion of the ruleeffect now contains the value of the variable to be assign.
-                                    //the content portion of the ruleeffect defines the name for the variable, when dollar is removed:
-                                    var variabletoassign = $rootScope.ruleeffects[executingEvent.event][action.id].content.replace("#{","").replace("}","");
-
-                                    if(!angular.isDefined(variablesHash[variabletoassign])){
-                                        $log.warn("Variable " + variabletoassign + " was not defined.");
-                                    }
-
-                                    //Even if the variable is not defined: we assign it:
-                                    if(variablesHash[variabletoassign].variableValue !== $rootScope.ruleeffects[executingEvent.event][action.id].data){
-                                        //If the variable was actually updated, we assume that there is an updated ruleeffect somewhere:
-                                        updatedEffectsExits = true;
-                                        //Then we assign the new value:
-                                        variablesHash[variabletoassign].variableValue = $rootScope.ruleeffects[executingEvent.event][action.id].data;
-                                    }
-                                }
-                            });
-                        });
-
-                        //Broadcast rules finished if there was any actual changes to the event.
-                        if(updatedEffectsExits){
-                            $rootScope.$broadcast("ruleeffectsupdated", { event: executingEvent.event });
-                        }
-                    }
-
-                    return true;
-                });
-            });
-        }
-    };
 });

=== 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-06-15 15:29:59 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js	2015-06-16 11:22:51 +0000
@@ -707,4 +707,467 @@
             return colExists;
         }
     };
+})
+
+/* service for building variables based on the data in users fields */
+.service('VariableService', function($rootScope,$q,TrackerRuleVariableFactory,$filter,orderByFilter,$log){
+    return {
+        getVariables: function(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity) {
+            var thePromisedVariables = $q.defer();
+            var variables = {};
+            
+            var pushVariable = function(variablename, variableValue, variableType, variablefound) {
+                //First clean away single or double quotation marks at the start and end of the variable name.
+                variableValue = $filter('trimquotes')(variableValue);
+                
+                //Append single quotation marks in case the variable is of text type:
+                if(variableType === 'string') {
+                    variableValue = "'" + variableValue + "'";
+                }
+                else if(variableType === 'date') {
+                    variableValue = "'" + variableValue + "'";
+                }
+                else if(variableType === 'bool' || variableType === 'trueOnly') {
+                    if(eval(variableValue)) {
+                        variableValue = true;
+                    }
+                    else {    
+                        variableValue = false;
+                    }
+                }
+                else if(variableType === "int" || variableType === "number") {
+                    variableValue = Number(variableValue);
+                }
+                else{
+                    $log.warn("unknown datatype:" + variableType);
+                }
+                 
+                
+                //Make sure that the variableValue does not contain a dollar sign anywhere 
+                //- this would potentially mess up later use of the variable:
+//                if(angular.isDefined(variableValue) 
+//                        && variableValue !== null
+//                        && variableValue.indexOf("$") !== -1 ) {
+//                    variableValue = variableValue.replace(/\\$/,"");
+//                }
+                
+                //TODO:
+                //Also clean away instructions that might be erroneusly evalutated in javascript
+
+                variables[variablename] = {
+                                variableValue:variableValue,
+                                variableType:variableType,
+                                hasValue:variablefound
+                            };
+            };
+            
+            TrackerRuleVariableFactory.getProgramRuleVariables(programid).then(function(programVariables){
+
+                // The following section will need a different implementation for event capture:
+                var allEventsSorted = [];
+                var currentEvent = executingEvent;
+                var eventsSortedPerProgramStage = [];
+                
+                for(var key in allEventsByStage){
+                    if(allEventsByStage.hasOwnProperty(key)){
+                        eventsSortedPerProgramStage[key] = [];
+                        angular.forEach(allEventsByStage[key], function(event){
+                            allEventsSorted.push(event);
+                            eventsSortedPerProgramStage[key].push(event);
+                        });
+                        eventsSortedPerProgramStage[key] = orderByFilter(eventsSortedPerProgramStage[key], '-sortingDate').reverse(); 
+                    }
+                }
+                allEventsSorted = orderByFilter(allEventsSorted, '-sortingDate').reverse(); 
+                
+                var allDes = allDataElements;
+//                angular.forEach($scope.programStages, function(programStage){
+//                    angular.forEach(programStage.programStageDataElements, function(dataElement) {
+//                        allDes[dataElement.dataElement.id] = dataElement;
+//                    });
+//                });
+                //End of region that neeeds specific implementation for event capture
+                
+                angular.forEach(programVariables, function(programVariable) {
+                    var valueFound = false;
+                    if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE"){
+                        if(programVariable.programStage) {
+                            angular.forEach(eventsSortedPerProgramStage[programVariable.programStage.id], function(event) {
+                                if(angular.isDefined(event[programVariable.dataElement.id])
+                                        && event[programVariable.dataElement.id] !== null ){
+                                    valueFound = true;
+                                    pushVariable(programVariable.name, event[programVariable.dataElement.id], allDes[programVariable.dataElement.id].dataElement.type, valueFound );
+                                }
+                            });
+                        } else {
+                            $log.warn("Variable id:'" + programVariable.id + "' name:'" + programVariable.name 
+                                    + "' 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"){
+                        angular.forEach(allEventsSorted, function(event) {
+                            if(angular.isDefined(event[programVariable.dataElement.id])
+                                    && event[programVariable.dataElement.id] !== null ){
+                                valueFound = true;
+                                 pushVariable(programVariable.name, event[programVariable.dataElement.id], allDes[programVariable.dataElement.id].dataElement.type, valueFound );
+                             }
+                        });
+                    }
+                    else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_CURRENT_EVENT"){
+                        if(angular.isDefined(currentEvent[programVariable.dataElement.id])
+                                && currentEvent[programVariable.dataElement.id] !== null ){
+                            valueFound = true;
+                            pushVariable(programVariable.name, currentEvent[programVariable.dataElement.id], allDes[programVariable.dataElement.id].dataElement.type, valueFound );
+                        }      
+                    }
+                    else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_PREVIOUS_EVENT"){
+                        //Only continue checking for a value if there is more than one event.
+                        if(allEventsSorted && allEventsSorted.length > 1) {
+                            var previousvalue = null;
+                            var currentEventPassed = false;
+                            for(var i = 0; i < allEventsSorted.length; i++) {
+                                //Store the values as we iterate through the stages
+                                //If the event[i] is not the current event, it is older(previous). Store the previous value if it exists
+                                if(!currentEventPassed && allEventsSorted[i] !== currentEvent && 
+                                        angular.isDefined(allEventsSorted[i][programVariable.dataElement.id])) {
+                                    previousvalue = allEventsSorted[i][programVariable.dataElement.id];
+                                    valueFound = true;
+                                }
+                                else if(allEventsSorted[i] === currentEvent) {
+                                    //We have iterated to the newest event - store the last collected variable value - if any is found:
+                                    if(valueFound) {
+                                        pushVariable(programVariable.name, previousvalue, allDes[programVariable.dataElement.id].dataElement.type, valueFound );
+                                    }
+                                    //Set currentEventPassed, ending the iteration:
+                                    currentEventPassed = true;
+                                }
+                            }
+                        }
+                    }
+                    else if(programVariable.programRuleVariableSourceType === "TEI_ATTRIBUTE"){
+                        angular.forEach(selectedEntity.attributes , function(attribute) {
+                            if(!valueFound) {
+                                if(attribute.attribute === programVariable.trackedEntityAttribute.id) {
+                                    valueFound = true;
+                                    pushVariable(programVariable.name, attribute.value, attribute.type, valueFound );
+                                }
+                            }
+                        });
+                    }
+                    else if(programVariable.programRuleVariableSourceType === "CALCULATED_VALUE"){
+                        //We won't assign the calculated variables at this step. The rules execution will calculate and assign the variable.
+                    }
+                    else if(programVariable.programRuleVariableSourceType === "NUMBEROFEVENTS_PROGRAMSTAGE"){
+                        var numberOfEvents = 0;
+                        if( programVariable.programStage && eventsSortedPerProgramStage[programVariable.programStage.id] ) {
+                            numberOfEvents = eventsSortedPerProgramStage[programVariable.programStage.id].length;
+                        }
+                        valueFound = true;
+                        pushVariable(programVariable.name, numberOfEvents, 'int', valueFound );
+                    }
+                    else {
+                        //Missing handing of ruletype
+                        $log.warn("Unknown programRuleVariableSourceType:" + programVariable.programRuleVariableSourceType);
+                    }
+
+                   
+                    if(!valueFound){
+                        //If there is still no value found, assign default value:
+                        if(programVariable.dataElement) {
+                            var dataElement = allDes[programVariable.dataElement.id];
+                            if( dataElement ) {
+                                pushVariable(programVariable.name, "", dataElement.dataElement.type );
+                            } 
+                            else {
+                                $log.warn("Variable #{" + programVariable.name + "} is linked to a dataelement that is not part of the program");
+                                pushVariable(programVariable.name, "", "string" );
+                            }
+                        }
+                        else {
+                            pushVariable(programVariable.name, "", "string" );
+                        }
+                    }
+                });
+
+                //add context variables:
+                //last parameter "valuefound" is always true for event date
+                pushVariable('eventdate', executingEvent.eventDate, 'date', true );
+                
+                thePromisedVariables.resolve(variables);
+            });
+            
+            return thePromisedVariables.promise;
+        }
+    };
+})
+
+/* service for executing tracker rules and broadcasting results */
+.service('TrackerRulesExecutionService', function(TrackerRulesFactory,VariableService, $rootScope, $log, $filter, orderByFilter){
+    return {
+        executeRules: function(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity) {
+            //When debugging rules, the caller should provide a variable for wether or not the rules is being debugged.
+            //hard coding this for now:
+            var debug = true;
+            var verbose = true;
+            
+            var variablesHash = {};
+                    
+            var replaceVariables = function(expression) {
+                //replaces the variables in an expression with actual variable values.
+                //First check if the expression contains variables at all(any dollar signs):
+                if(expression.indexOf('#') !== -1) {
+                    //Find every variable name in the expression;
+                    var variablespresent = expression.match(/#{\w+}/g);
+                    //Replace each matched variable:
+                    angular.forEach(variablespresent, function(variablepresent) {
+                        //First strip away any dollar signs from the variable name:
+                        variablepresent = variablepresent.replace("#{","").replace("}","");
+                        
+                        if(angular.isDefined(variablesHash[variablepresent])) {
+                            //Replace all occurrences of the variable name(hence using regex replacement):
+                            expression = expression.replace(new RegExp("#{" + variablepresent + "}", 'g'),
+                                variablesHash[variablepresent].variableValue);
+                        }
+                        else {
+                            $log.warn("Expression " + expression + " conains variable " + variablepresent 
+                                    + " - but this variable is not defined." );
+                        }
+                            
+                    });
+                }
+                return expression;
+            };
+            
+            var runDhisFunctions = function(expression) {
+                //Called from "runExpression". Only proceed with this logic in case there seems to be dhis function calls: "dhis." is present.
+                if(angular.isDefined(expression) && expression.indexOf("dhis.") !== -1){   
+                    var dhisFunctions = [{name:"dhis.daysbetween",parameters:2},
+                                        {name:"dhis.floor",parameters:1},
+                                        {name:"dhis.modulus",parameters:2},
+                                        {name:"dhis.hasValue",parameters:1},
+                                        {name:"dhis.concatenate"}];
+                    
+                    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');
+                        var callsToThisFunction = expression.match(regularExFunctionCall);
+                        angular.forEach(callsToThisFunction, function(callToThisFunction){
+                            //Remove the function name and paranthesis:
+                            var justparameters = callToThisFunction.replace(/(^[^\(]+\()|\)$/g,"");
+                            //Then split into single parameters:
+                            var parameters = justparameters.match(/(('[^']+')|([^,]+))/g);
+                            
+                            //Show error if no parameters is given and the function requires parameters,
+                            //or if the number of parameters is wrong.
+                            if(angular.isDefined(dhisFunction.parameters)){
+                                //But we are only checking parameters where the dhisFunction actually has a defined set of parameters(concatenate, for example, does not have a fixed number);
+                                if((!angular.isDefined(parameters) && dhisFunction.parameters > 0)
+                                        || parameters.length !== dhisFunction.parameters){
+                                    $log.warn(dhisFunction.name + " was called with the incorrect number of parameters");
+                                }
+                            }
+
+                            //In case the function call is nested, the parameter itself contains an expression, run the expression.
+                            if(angular.isDefined(parameters)) {
+                                for (var i = 0; i < parameters.length; i++) {
+                                    parameters[i] = runExpression(parameters[i],dhisFunction.name,"parameter:" + i);
+                                }
+                            }
+
+                            //Special block for dhis.weeksBetween(*,*) - add such a block for all other dhis functions.
+                            if(dhisFunction.name === "dhis.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 === "dhis.floor")
+                            {
+                                var floored = Math.floor(parameters[0]);
+                                //Replace the end evaluation of the dhis function:
+                                expression = expression.replace(callToThisFunction, floored);
+                            }
+                            else if(dhisFunction.name === "dhis.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 === "dhis.hasValue")
+                            {
+                                //"evaluate" hasvalue to true or false:
+                                if(variablesHash[parameters[0]].hasValue){
+                                    expression = expression.replace(callToThisFunction, 'true');
+                                } else {
+                                    expression = expression.replace(callToThisFunction, 'false');
+                                }
+                            }
+                            else if(dhisFunction.name === "dhis.concatenate")
+                            {
+                                var returnString = "'";
+                                for (var i = 0; i < parameters.length; i++) {
+                                    returnString += parameters[i];
+                                }
+                                returnString += "'";
+                                expression = expression.replace(callToThisFunction, returnString);
+                            }
+                        });
+                    });
+                }
+                
+                return expression;
+            };
+            
+            var runExpression = function(expression, beforereplacement, identifier ){
+                //determine if expression is true, and actions should be effectuated
+                //If DEBUG mode, use try catch and report errors. If not, omit the heavy try-catch loop.:
+                var answer = false;
+                if(debug) {
+                    try{
+                        
+                        var dhisfunctionsevaluated = runDhisFunctions(expression);
+                        answer = eval(dhisfunctionsevaluated);
+
+                        if(verbose)
+                        {
+                            $log.info("Expression with id " + identifier + " was successfully run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - Result of evaluation was:" + answer);
+                        }
+                    }
+                    catch(e)
+                    {
+                        $log.warn("Expression with id " + identifier + " could not be run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - error message:" + e);
+                    }
+                }
+                else {
+                    //Just run the expression. This is much faster than the debug route: http://jsperf.com/try-catch-block-loop-performance-comparison
+                    var dhisfunctionsevaluated = runDhisFunctions(expression);
+                    answer = eval(dhisfunctionsevaluated);
+                }
+                return answer;
+            };
+            
+            
+            VariableService.getVariables(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity).then(function(variablesReceived){
+                TrackerRulesFactory.getProgramStageRules(programid, executingEvent.programStage).then(function(rules){
+                    //But run rules in priority - lowest number first(priority null is last)
+                    rules = orderByFilter(rules, 'priority');
+                    
+                    variablesHash = variablesReceived;
+
+                    if(angular.isObject(rules) && angular.isArray(rules)){
+                        //The program has rules, and we want to run them.
+                        //Prepare repository unless it is already prepared:
+                        if(angular.isUndefined( $rootScope.ruleeffects ) ) {
+                            $rootScope.ruleeffects = {};
+                        }
+                            
+                        if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event] )){
+                            $rootScope.ruleeffects[executingEvent.event] = {};
+                        }
+
+                        var updatedEffectsExits = false;
+
+                        angular.forEach(rules, function(rule) {
+                            var ruleEffective = false;
+
+                            var expression = rule.condition;
+                            //Go through and populate variables with actual values, but only if there actually is any replacements to be made(one or more "$" is present)
+                            if(expression) {
+                                if(expression.indexOf('#') !== -1) {
+                                    expression = replaceVariables(expression);
+                                }
+                                //run expression:
+                                ruleEffective = runExpression(expression, rule.condition, "rule:" + rule.id);
+                            } else {
+                                $log.warn("Rule id:'" + rule.id + "'' and name:'" + rule.name + "' had no condition specified. Please check rule configuration.");
+                            }
+                            
+                            angular.forEach(rule.actions, function(action){
+                                //In case the effect-hash is not populated, add entries
+                                if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event][action.id] )){
+                                    $rootScope.ruleeffects[executingEvent.event][action.id] =  {
+                                        id:action.id,
+                                        location:action.location, 
+                                        action:action.programRuleActionType,
+                                        dataElement:action.dataElement,
+                                        content:action.content,
+                                        data:action.data,
+                                        ineffect:false
+                                    };
+                                }
+
+                                //In case the rule is effective and contains specific data, 
+                                //the effect be refreshed from the variables list.
+                                //If the rule is not effective we can skip this step
+                                if(ruleEffective && action.data)
+                                {
+                                    //The key data might be containing a dollar sign denoting that the key data is a variable.
+                                    //To make a lookup in variables hash, we must make a lookup without the dollar sign in the variable name
+                                    //The first strategy is to make a direct lookup. In case the "data" expression is more complex, we have to do more replacement and evaluation.
+
+                                    var nameWithoutBrackets = action.data.replace('#{','').replace('}','');
+                                    if(angular.isDefined(variablesHash[nameWithoutBrackets]))
+                                    {
+                                        //The variable exists, and is replaced with its corresponding value
+                                        $rootScope.ruleeffects[executingEvent.event][action.id].data =
+                                            variablesHash[nameWithoutBrackets].variableValue;
+                                    }
+                                    else if(action.data.indexOf('#') !== -1)
+                                    {
+                                        //Since the value couldnt be looked up directly, and contains a dollar sign, the expression was more complex
+                                        //Now we will have to make a thorough replacement and separate evaluation to find the correct value:
+                                        $rootScope.ruleeffects[executingEvent.event][action.id].data = replaceVariables(action.data);
+                                        //In a scenario where the data contains a complex expression, evaluate the expression to compile(calculate) the result:
+                                        $rootScope.ruleeffects[executingEvent.event][action.id].data = runExpression($rootScope.ruleeffects[executingEvent.event][action.id].data, action.data, "action:" + action.id);
+                                    }
+                                }
+
+                                //Update the rule effectiveness if it changed in this evaluation;
+                                if($rootScope.ruleeffects[executingEvent.event][action.id].ineffect !== ruleEffective)
+                                {
+                                    //There is a change in the rule outcome, we need to update the effect object.
+                                    updatedEffectsExits = true;
+                                    $rootScope.ruleeffects[executingEvent.event][action.id].ineffect = ruleEffective;
+                                }
+
+                                //In case the rule is of type "assign variable" and the rule is effective,
+                                //the variable data result needs to be applied to the correct variable:
+                                if($rootScope.ruleeffects[executingEvent.event][action.id].action === "ASSIGNVARIABLE" && $rootScope.ruleeffects[executingEvent.event][action.id].ineffect){
+                                    //from earlier evaluation, the data portion of the ruleeffect now contains the value of the variable to be assign.
+                                    //the content portion of the ruleeffect defines the name for the variable, when dollar is removed:
+                                    var variabletoassign = $rootScope.ruleeffects[executingEvent.event][action.id].content.replace("#{","").replace("}","");
+
+                                    if(!angular.isDefined(variablesHash[variabletoassign])){
+                                        $log.warn("Variable " + variabletoassign + " was not defined.");
+                                    }
+
+                                    //Even if the variable is not defined: we assign it:
+                                    if(variablesHash[variabletoassign].variableValue !== $rootScope.ruleeffects[executingEvent.event][action.id].data){
+                                        //If the variable was actually updated, we assume that there is an updated ruleeffect somewhere:
+                                        updatedEffectsExits = true;
+                                        //Then we assign the new value:
+                                        variablesHash[variabletoassign].variableValue = $rootScope.ruleeffects[executingEvent.event][action.id].data;
+                                    }
+                                }
+                            });
+                        });
+
+                        //Broadcast rules finished if there was any actual changes to the event.
+                        if(updatedEffectsExits){
+                            $rootScope.$broadcast("ruleeffectsupdated", { event: executingEvent.event });
+                        }
+                    }
+
+                    return true;
+                });
+            });
+        }
+    };
 });

_______________________________________________
Mailing list: https://launchpad.net/~dhis2-devs
Post to     : dhis2-devs@lists.launchpad.net
Unsubscribe : https://launchpad.net/~dhis2-devs
More help   : https://help.launchpad.net/ListHelp

Reply via email to