dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #39171
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19862: Registration now makes calls to the program rules engine
------------------------------------------------------------
revno: 19862
committer: Markus Bekken <markus.bekken@xxxxxxxxx>
branch nick: dhis2
timestamp: Wed 2015-08-26 14:29:22 +0200
message:
Registration now makes calls to the program rules engine
modified:
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.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/components/registration/default-registration-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html 2015-08-19 20:13:21 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html 2015-08-26 12:29:22 +0000
@@ -12,7 +12,7 @@
ng-required="attribute.mandatory || attribute.unique"
ng-disabled="editingDisabled"
name="foo"
- on-select="validationAndSkipLogic(selectedTei, attribute.id)"
+ on-select="teiValueUpdated(selectedTei, attribute.id)"
style="width:100%;">
<ui-select-match allow-clear="true" style="width:100%; height:34px; line-height:1.0; padding: 2px 6px; margin-top:5px" placeholder="{{'select_or_search' | translate}}">{{$select.selected.name || $select.selected}}</ui-select-match>
<ui-select-choices repeat="option.name as option in optionSets[attributesById[attribute.id].optionSet.id].options | filter: $select.search | limitTo:30">
@@ -31,7 +31,7 @@
max-date="attribute.allowFutureDate ? '' : 0"
ng-model="selectedTei[attribute.id]"
ng-disabled="editingDisabled"
- blur-or-change="validationAndSkipLogic(selectedTei, attribute.id)"
+ blur-or-change="teiValueUpdated(selectedTei, attribute.id)"
ng-required="attribute.mandatory || attribute.unique"/>
</span>
<span ng-switch-when="trueOnly">
@@ -40,7 +40,7 @@
class="form-control"
ng-model="selectedTei[attribute.id]"
ng-disabled="editingDisabled"
- ng-change="validationAndSkipLogic(selectedTei, attribute.id)"
+ ng-change="teiValueUpdated(selectedTei, attribute.id)"
ng-required="attribute.mandatory || attribute.unique"/>
</span>
<span ng-switch-when="bool">
@@ -48,7 +48,7 @@
ng-model="selectedTei[attribute.id]"
class="form-control"
ng-disabled="editingDisabled"
- ng-change="validationAndSkipLogic(selectedTei, attribute.id)"
+ ng-change="teiValueUpdated(selectedTei, attribute.id)"
ng-required="attribute.mandatory || attribute.unique">
<option value="">{{'please_select'| translate}}</option>
<option value="false">{{'no'| translate}}</option>
@@ -61,7 +61,7 @@
class="form-control"
ng-model="selectedTei[attribute.id]"
ng-disabled="editingDisabled"
- ng-blur="validationAndSkipLogic(selectedTei, attribute.id)"
+ ng-blur="teiValueUpdated(selectedTei, attribute.id)"
ng-required="attribute.mandatory || attribute.unique"/>
</span>
<span ng-switch-when="email">
@@ -70,7 +70,7 @@
class="form-control"
ng-model="selectedTei[attribute.id]"
ng-disabled="editingDisabled"
- ng-blur="validationAndSkipLogic(selectedTei, attribute.id)"
+ ng-blur="teiValueUpdated(selectedTei, attribute.id)"
ng-required="attribute.mandatory || attribute.unique"/>
</span>
<span ng-switch-default>
@@ -79,7 +79,7 @@
class="form-control"
ng-model="selectedTei[attribute.id]"
ng-disabled="editingDisabled"
- ng-blur="validationAndSkipLogic(selectedTei, attribute.id)"
+ ng-blur="teiValueUpdated(selectedTei, attribute.id)"
ng-required="attribute.mandatory || attribute.unique"/>
</span>
</span>
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js 2015-08-19 20:13:21 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js 2015-08-26 12:29:22 +0000
@@ -18,7 +18,9 @@
EventUtils,
RegistrationService,
DateUtils,
- SessionStorageService) {
+ SessionStorageService,
+ TrackerRulesFactory,
+ TrackerRulesExecutionService) {
$scope.today = DateUtils.getToday();
$scope.trackedEntityForm = null;
@@ -60,6 +62,11 @@
$scope.trackedEntities.selected = $scope.trackedEntities.available[0];
});
+ $scope.allProgramRules = [];
+ TrackerRulesFactory.getRules($scope.selectedProgram.id).then(function(rules){
+ $scope.allProgramRules = rules;
+ });
+
//watch for selection of program
$scope.$watch('selectedProgram', function() {
$scope.trackedEntityForm = null;
@@ -107,7 +114,7 @@
//reset form
$scope.selectedTei = {};
$scope.selectedEnrollment = {};
- $scope.outerForm.submitted = false;
+ $scope.outerForm.submitted = false;
if(destination === 'DASHBOARD') {
$location.path('/dashboard').search({tei: teiId,
@@ -262,9 +269,94 @@
}, 100);
};
- /*$scope.validationAndSkipLogic = function(tei, field){
- };*/
-
+ var processRuleEffect = function(){
+
+ angular.forEach($rootScope.ruleeffects['registration'], function (effect) {
+ if (effect.dataElement) {
+ //in the data entry controller we only care about the "hidefield", showerror and showwarning actions
+ if (effect.action === "HIDEFIELD") {
+ if (effect.dataElement) {
+ if (effect.ineffect && affectedEvent[effect.dataElement.id]) {
+ //If a field is going to be hidden, but contains a value, we need to take action;
+ if (effect.content) {
+ //TODO: Alerts is going to be replaced with a proper display mecanism.
+ alert(effect.content);
+ }
+ else {
+ //TODO: Alerts is going to be replaced with a proper display mecanism.
+ alert($scope.prStDes[effect.dataElement.id].dataElement.formName + "Was blanked out and hidden by your last action");
+ }
+
+ //Blank out the value:
+ affectedEvent[effect.dataElement.id] = "";
+ $scope.saveDatavalueForEvent($scope.prStDes[effect.dataElement.id], null, affectedEvent);
+ }
+
+ $scope.hiddenFields[effect.dataElement.id] = effect.ineffect;
+ }
+ else {
+ $log.warn("ProgramRuleAction " + effect.id + " is of type HIDEFIELD, bot does not have a dataelement defined");
+ }
+ } else if (effect.action === "SHOWERROR") {
+ if (effect.dataElement) {
+
+ if(effect.ineffect) {
+ $scope.errorMessages[effect.dataElement.id] = effect.content;
+ } else {
+ $scope.errorMessages[effect.dataElement.id] = false;
+ }
+ }
+ else {
+ $log.warn("ProgramRuleAction " + effect.id + " is of type HIDEFIELD, bot does not have a dataelement defined");
+ }
+ } else if (effect.action === "SHOWWARNING") {
+ if (effect.dataElement) {
+ if(effect.ineffect) {
+ $scope.warningMessages[effect.dataElement.id] = effect.content;
+ } else {
+ $scope.warningMessages[effect.dataElement.id] = false;
+ }
+ }
+ else {
+ $log.warn("ProgramRuleAction " + effect.id + " is of type HIDEFIELD, bot does not have a dataelement defined");
+ }
+ }
+ }
+ });
+ };
+
+ $scope.executeRules = function () {
+ var flag = {debug: true, verbose: true};
+
+ //repopulate attributes with updated values
+ $scope.selectedTei.attributes = [];
+
+ angular.forEach($scope.attributes, function(metaAttribute){
+ var newAttributeInArray = {attribute:metaAttribute.id,
+ code:metaAttribute.code,
+ displayName:metaAttribute.displayName,
+ type:metaAttribute.valueType
+ };
+ if($scope.selectedTei[newAttributeInArray.attribute]){
+ newAttributeInArray.value = $scope.selectedTei[newAttributeInArray.attribute];
+ }
+
+ $scope.selectedTei.attributes.push(newAttributeInArray);
+ });
+ TrackerRulesExecutionService.executeRules($scope.allProgramRules, 'registration', null, null, $scope.selectedTei, $scope.selectedEnrollment, flag);
+
+ };
+
+ $scope.teiValueUpdated = function(tei, field){
+ $scope.executeRules();
+ };
+
+ //listen for rule effect changes
+ $scope.$on('ruleeffectsupdated', function (event, args) {
+ processRuleEffect(args.event);
+ });
+
+
$scope.interacted = function(field) {
var status = false;
if(field){
=== 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-08-24 09:55:12 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js 2015-08-26 12:29:22 +0000
@@ -771,7 +771,8 @@
}
var valueFound = false;
- if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE"){
+ //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) {
angular.forEach(evs.byStage[programStageId], function(event) {
if(angular.isDefined(event[dataElementId])
@@ -787,7 +788,7 @@
}
}
- else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM"){
+ else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM" && evs){
angular.forEach(evs.all, function(event) {
if(angular.isDefined(event[dataElementId])
&& event[dataElementId] !== null ){
@@ -796,14 +797,14 @@
}
});
}
- else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_CURRENT_EVENT"){
+ else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_CURRENT_EVENT" && evs){
if(angular.isDefined(executingEvent[dataElementId])
&& executingEvent[dataElementId] !== null ){
valueFound = true;
variables = pushVariable(variables, programVariable.name, executingEvent[dataElementId], allDes[dataElementId].dataElement.type, valueFound, '#' );
}
}
- else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_PREVIOUS_EVENT"){
+ 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 previousvalue = null;
@@ -840,7 +841,7 @@
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"){
+ else if(programVariable.programRuleVariableSourceType === "NUMBEROFEVENTS_PROGRAMSTAGE" && evs){
var numberOfEvents = 0;
if( programStageId && evs.byStage[programStageId] ) {
numberOfEvents = evs.byStage[programStageId].length;
@@ -849,14 +850,18 @@
variables = pushVariable(variables, programVariable.name, numberOfEvents, 'int', valueFound, '#' );
}
else {
- //Missing handing of ruletype
- $log.warn("Unknown programRuleVariableSourceType:" + programVariable.programRuleVariableSourceType);
+ //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
+ if(evs)
+ {
+ //If the rules was executed and events was supplied, we should have found an if clause for the the source type, and not ended up in this dead end else.
+ $log.warn("Unknown programRuleVariableSourceType:" + programVariable.programRuleVariableSourceType);
+ }
}
if(!valueFound){
//If there is still no value found, assign default value:
- if(dataElementId) {
+ if(dataElementId && allDes) {
var dataElement = allDes[dataElementId];
if( dataElement ) {
variables = pushVariable(variables, programVariable.name, "", dataElement.dataElement.type, false, '#' );
@@ -996,14 +1001,16 @@
};
var runDhisFunctions = function(expression, variablesHash, flag){
- //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.yearsbetween",parameters:2},
- {name:"dhis.floor",parameters:1},
- {name:"dhis.modulus",parameters:2},
- {name:"dhis.concatenate"},
- {name:"dhis.adddays",parameters:2}];
+ //Called from "runExpression". Only proceed with this logic in case there seems to be dhis function calls: "d2:" is present.
+ if(angular.isDefined(expression) && expression.indexOf("d2:") !== -1){
+ var dhisFunctions = [{name:"d2:daysbetween",parameters:2},
+ {name:"d2:yearsbetween",parameters:2},
+ {name:"d2:floor",parameters:1},
+ {name:"d2:modulus",parameters:2},
+ {name:"d2:concatenate"},
+ {name:"d2:adddays",parameters:2},
+ {name:"d2:zing",parameters:1},
+ {name:"d2:oizp",parameters:1}];
angular.forEach(dhisFunctions, function(dhisFunction){
//Replace each * with a regex that matches each parameter, allowing commas only inside single quotation marks.
@@ -1032,8 +1039,8 @@
}
}
- //Special block for dhis.weeksBetween(*,*) - add such a block for all other dhis functions.
- if(dhisFunction.name === "dhis.daysbetween")
+ //Special block for d2:weeksBetween(*,*) - add such a block for all other dhis functions.
+ if(dhisFunction.name === "d2:daysbetween")
{
var firstdate = $filter('trimquotes')(parameters[0]);
var seconddate = $filter('trimquotes')(parameters[1]);
@@ -1042,7 +1049,7 @@
//Replace the end evaluation of the dhis function:
expression = expression.replace(callToThisFunction, seconddate.diff(firstdate,'days'));
}
- else if(dhisFunction.name === "dhis.yearsbetween")
+ else if(dhisFunction.name === "d2:yearsbetween")
{
var firstdate = $filter('trimquotes')(parameters[0]);
var seconddate = $filter('trimquotes')(parameters[1]);
@@ -1051,13 +1058,13 @@
//Replace the end evaluation of the dhis function:
expression = expression.replace(callToThisFunction, seconddate.diff(firstdate,'years'));
}
- else if(dhisFunction.name === "dhis.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 === "dhis.modulus")
+ else if(dhisFunction.name === "d2:modulus")
{
var dividend = Number(parameters[0]);
var divisor = Number(parameters[1]);
@@ -1065,7 +1072,7 @@
//Replace the end evaluation of the dhis function:
expression = expression.replace(callToThisFunction, rest);
}
- else if(dhisFunction.name === "dhis.concatenate")
+ else if(dhisFunction.name === "d2:concatenate")
{
var returnString = "'";
for (var i = 0; i < parameters.length; i++) {
@@ -1074,7 +1081,7 @@
returnString += "'";
expression = expression.replace(callToThisFunction, returnString);
}
- else if(dhisFunction.name === "dhis.adddays")
+ else if(dhisFunction.name === "d2:adddays")
{
var date = $filter('trimquotes')(parameters[0]);
var daystoadd = $filter('trimquotes')(parameters[1]);
@@ -1082,6 +1089,25 @@
var newdatestring = "'" + newdate + "'";
//Replace the end evaluation of the dhis function:
expression = expression.replace(callToThisFunction, newdatestring);
+ }else if(dhisFunction.name === "d2:zing")
+ {
+ var number = parameters[0];
+ if( number < 0 ) {
+ number = 0;
+ }
+
+ //Replace the end evaluation of the dhis function:
+ expression = expression.replace(callToThisFunction, number);
+ }else if(dhisFunction.name === "d2:oizp")
+ {
+ var number = parameters[0];
+ var output = 1;
+ if( number < 0 ) {
+ output = 0;
+ }
+
+ //Replace the end evaluation of the dhis function:
+ expression = expression.replace(callToThisFunction, output);
}
});
});