dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #37924
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19368: Merged the tracker capture rules engine and table-dataentry from folkehelsa branch.
Merge authors:
Abyot Asalefew Gizaw (abyot)
Abyot Asalefew Gizaw abyota@xxxxxxxxx
Lars Helge Øverland (larshelge)
Markus Bekken (markus-bekken)
Øystein Gammersvik ogam@xxxxxxxxxxxx
------------------------------------------------------------
revno: 19368 [merge]
committer: Markus Bekken <markus.bekken@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2015-06-11 20:44:52 +0200
message:
Merged the tracker capture rules engine and table-dataentry from folkehelsa branch.
added:
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/table-entry-form.html
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound-controller.js
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound.html
modified:
dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/Program.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/programrule/ProgramRuleVariableSourceType.java
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.html
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-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-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app_ar.properties
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html
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/tracker-capture.js
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css
--
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/program/Program.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/Program.java 2015-03-31 03:17:35 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/Program.java 2015-04-08 11:07:34 +0000
@@ -67,6 +67,8 @@
extends BaseIdentifiableObject
implements VersionedObject
{
+ private static final long serialVersionUID = -2807997671779497354L;
+
public static final List<String> TYPE_LOOKUP = Arrays.asList( "", "MULTIPLE_EVENTS_WITH_REGISTRATION",
"SINGLE_EVENT_WITH_REGISTRATION", "SINGLE_EVENT_WITHOUT_REGISTRATION" );
=== 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-03-13 08:24:36 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/programrule/ProgramRuleVariableSourceType.java 2015-05-15 11:08:43 +0000
@@ -42,7 +42,8 @@
DATAELEMENT_PREVIOUS_EVENT("dataelement_previous_event"),
CALCULATED_VALUE("calculated_value"),
TEI_ATTRIBUTE("tei_attribute"),
- CONTEXT_VARIABLE("context_variable");
+ CONTEXT_VARIABLE("context_variable"),
+ NUMBEROFEVENTS_PROGRAMSTAGE("numberofevents_programstage");
private final String value;
=== 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-06-10 11:58:22 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js 2015-06-10 13:45:11 +0000
@@ -3,8 +3,9 @@
trackerCapture.controller('DataEntryController',
function($rootScope,
$scope,
- $modal,
- $filter,
+ $modal,
+ $filter,
+ $log,
$timeout,
Paginator,
DateUtils,
@@ -17,8 +18,10 @@
OptionSetService,
ModalService,
CurrentSelection,
- CustomFormService,
- PeriodService) {
+ TrackerRulesExecutionService,
+ CustomFormService,
+ PeriodService) {
+
//Data entry form
$scope.outerForm = {};
$scope.displayCustomForm = false;
@@ -27,7 +30,11 @@
$scope.eventPeriods = [];
$scope.currentPeriod = [];
$scope.filterEvents = true;
+ $scope.showEventsAsTables = false;
+ //variable is set while looping through the program stages later.
+ $scope.stagesCanBeShownAsTable = false;
$scope.showHelpText = {};
+ $scope.hiddenFields = {};
var userProfile = SessionStorageService.get('USER_PROFILE');
var storedBy = userProfile && userProfile.username ? userProfile.username : '';
@@ -46,21 +53,56 @@
{color: 'alert-danger', description: 'overdue'},
{color: 'alert-default', description: 'skipped'}
];
- $scope.showEventColors = false;
-
+ $scope.showEventColors = false;
+
+ //listen for rule effect changes
+ $scope.$on('ruleeffectsupdated', function(event, args) {
+ angular.forEach($rootScope.ruleeffects, function(effect) {
+ if( effect.dataElement ) {
+ //in the data entry controller we only care about the "hidefield" actions
+ if(effect.action === "HIDEFIELD") {
+ if(effect.dataElement) {
+ //Hide the field if the hiding is in effect, and there is no data value in the field.
+ var hide = effect.ineffect && !$scope.currentEvent[effect.dataElement.id];
+ $scope.hiddenFields[effect.dataElement.id] = hide;
+ }
+ else {
+ $log.warn("ProgramRuleAction " + effect.id + " is of type HIDEFIELD, bot does not have a dataelement defined");
+ }
+ }
+ }
+ });
+ });
+ //check if field is hidden
+ $scope.isHidden = function(id) {
+ //In case the field contains a value, we cant hide it.
+ //If we hid a field with a value, it would falsely seem the user was aware that the value was entered in the UI.
+ if($scope.currentEvent[id])
+ {
+ return false;
+ }
+ else
+ {
+ return $scope.hiddenFields[id];
+ }
+ };
+
+
//listen for the selected items
$scope.$on('dashboardWidgets', function() {
$scope.showDataEntryDiv = false;
$scope.showEventCreationDiv = false;
$scope.currentEvent = null;
$scope.currentStage = null;
+ $scope.currentStageEvents = null;
$scope.totalEvents = 0;
$scope.allowEventCreation = false;
$scope.repeatableStages = [];
$scope.eventsByStage = [];
- $scope.programStages = [];
- $scope.prStDes = [];
+ $scope.programStages = [];
+ $rootScope.ruleeffects = {};
+ $scope.prStDes = [];
var selections = CurrentSelection.get();
$scope.selectedOrgUnit = SessionStorageService.get('SELECTED_OU');
@@ -84,6 +126,11 @@
$scope.stagesById[stage.id] = stage;
$scope.eventsByStage[stage.id] = [];
+
+ //If one of the stages has less than 7 data elements, allow sorting as table:
+ if(stage.programStageDataElements.length < 7) {
+ $scope.stagesCanBeShownAsTable = true;
+ }
});
$scope.programStages = orderByFilter($scope.programStages, '-sortOrder').reverse();
@@ -147,8 +194,32 @@
$scope.schedulingEnabled = !$scope.schedulingEnabled;
};
+ $scope.stageCanBeShownAsTable = function(stage) {
+ if(stage.programStageDataElements && stage.programStageDataElements.length < 7) {
+ return true;
+ }
+ return false;
+ };
+
+ $scope.toggleEventsTableDisplay = function() {
+ $scope.showEventsAsTables = !$scope.showEventsAsTables;
+ angular.forEach($scope.programStages, function(stage){
+ if(stage.programStageDataElements.length < 7) {
+ stage.displayEventsInTable = $scope.showEventsAsTables;
+ if($scope.currentStage === stage) {
+ $scope.getDataEntryForm();
+ }
+ }
+ });
+ };
+
$scope.stageNeedsEvent = function(stage){
-
+
+ //In case the event is a table, we sould always allow adding more events(rows)
+ if(stage.displayEventsInTable) {
+ return true;
+ }
+
if($scope.eventsByStage[stage.id].length < 1){
return true;
}
@@ -180,6 +251,10 @@
},
eventPeriods: function(){
return $scope.eventPeriods;
+ },
+ autoCreate: function() {
+ //In case the programstage is a table, autocreate
+ return stage.displayEventsInTable;
}
}
});
@@ -200,9 +275,14 @@
newEvent.coordinate = {};
}
+ //Have to make sure the event is preprocessed - this does not happen unless "Dashboardwidgets" is invoked.
+ newEvent = EventUtils.processEvent(newEvent, stage, $scope.optionSets, $scope.prStDes);
+
+
$scope.eventsByStage[newEvent.programStage].push(newEvent);
$scope.currentEvent = newEvent;
sortEventsByStage('ADD');
+
$scope.currentEvent = null;
$scope.showDataEntry(newEvent, false);
}
@@ -253,18 +333,31 @@
$scope.getDataEntryForm = function(){
$scope.currentStage = $scope.stagesById[$scope.currentEvent.programStage];
+ $scope.currentStageEvents = $scope.eventsByStage[$scope.currentEvent.programStage];
angular.forEach($scope.currentStage.programStageSections, function(section){
section.open = true;
});
$scope.customForm = CustomFormService.getForProgramStage($scope.currentStage, $scope.prStDes);
- $scope.displayCustomForm = $scope.customForm ? true:false;
+ $scope.displayCustomForm = "default";
+ if($scope.customForm){
+ $scope.displayCustomForm = "custom";
+ }
+ else if($scope.currentStage.displayEventsInTable) {
+ $scope.displayCustomForm = "table";
+ }
+
+ $scope.currentEventOriginal = angular.copy($scope.currentEvent);
- $scope.currentEventOriginal = angular.copy($scope.currentEvent);
+ $scope.currentStageEventsOriginal = angular.copy($scope.currentStageEvents);
var period = {event: $scope.currentEvent.event, stage: $scope.currentEvent.programStage, name: $scope.currentEvent.sortingDate};
$scope.currentPeriod[$scope.currentEvent.programStage] = period;
+
+ //Execute rules for the first time, to make the initial page appear correctly.
+ //Subsequent calls will be made from the "saveDataValue" function.
+ TrackerRulesExecutionService.executeRules($scope);
};
function updateCurrentEventInStage(){
@@ -280,7 +373,14 @@
}
};
- $scope.saveDatavalue = function(prStDe, field){
+
+ $scope.saveDatavalue = function(prStDe,field){
+ $scope.saveDatavalueForEvent(prStDe,field,$scope.currentEvent);
+ };
+
+ $scope.saveDatavalueForEvent = function(prStDe,field,eventToSave,object){
+ //Blank out the input-saved class on the last saved due date:
+ $scope.eventDateSaved = false;
//console.log('the field: ', field);
$scope.currentElement = {};
@@ -294,12 +394,17 @@
return false;
}
- //input is valid
- var value = $scope.currentEvent[prStDe.dataElement.id];
- //var value = inputField.$viewValue;
-
- if($scope.currentEventOriginal[prStDe.dataElement.id] !== value){
-
+ //input is valid
+ var value = eventToSave[prStDe.dataElement.id];
+
+ var oldValue = null;
+ angular.forEach($scope.currentStageEventsOriginal, function (eventOriginal) {
+ if(eventOriginal.event === eventToSave.event) {
+ oldValue = eventOriginal[prStDe.dataElement.id];
+ }
+ });
+
+ if(oldValue !== value){
if(value){
if(prStDe.dataElement.type === 'date'){
value = DateUtils.formatFromUserToApi(value);
@@ -311,19 +416,21 @@
}
}
- $scope.currentElement = {id: prStDe.dataElement.id, saved: false};
-
- var ev = { event: $scope.currentEvent.event,
- orgUnit: $scope.currentEvent.orgUnit,
- program: $scope.currentEvent.program,
- programStage: $scope.currentEvent.programStage,
- status: $scope.currentEvent.status,
- trackedEntityInstance: $scope.currentEvent.trackedEntityInstance,
+ $scope.updateSuccess = false;
+
+ $scope.currentElement = {id: prStDe.dataElement.id, event:eventToSave.event, saved: false};
+
+ var ev = { event: eventToSave.event,
+ orgUnit: eventToSave.orgUnit,
+ program: eventToSave.program,
+ programStage: eventToSave.programStage,
+ status: eventToSave.status,
+ trackedEntityInstance: eventToSave.trackedEntityInstance,
dataValues: [
{
dataElement: prStDe.dataElement.id,
value: value,
- providedElsewhere: $scope.currentEvent.providedElsewhere[prStDe.dataElement.id] ? true : false
+ providedElsewhere: eventToSave.providedElsewhere[prStDe.dataElement.id] ? true : false
}
]
};
@@ -332,7 +439,12 @@
sortEventsByStage('UPDATE');
$scope.currentElement.saved = true;
- $scope.currentEventOriginal = angular.copy($scope.currentEvent);
+
+ $scope.currentEventOriginal = angular.copy($scope.currentEvent);
+
+ $scope.currentStageEventsOriginal = angular.copy($scope.currentStageEvents);
+
+ TrackerRulesExecutionService.executeRules($scope);
});
}
@@ -365,44 +477,46 @@
sortEventsByStage('UPDATE');
$scope.updateSuccess = true;
- });
- }
- };
-
- $scope.saveEventDate = function(){
-
+ });
+ }
+ };
+
+ $scope.saveEventDate = function () {
+ $scope.saveEventDateForEvent($scope.currentEvent);
+ };
+
+ $scope.saveEventDateForEvent = function(eventToSave){
$scope.eventDateSaved = false;
- if($scope.currentEvent.eventDate === ''){
- $scope.invalidDate = true;
+ if(eventToSave.eventDate === ''){
+ $scope.invalidDate = eventToSave.event;
return false;
}
- var rawDate = angular.copy($scope.currentEvent.eventDate);
- var convertedDate = DateUtils.format($scope.currentEvent.eventDate);
+ var rawDate = angular.copy(eventToSave.eventDate);
+ var convertedDate = DateUtils.format(eventToSave.eventDate);
if(rawDate !== convertedDate){
$scope.invalidDate = true;
return false;
}
- var e = {event: $scope.currentEvent.event,
- enrollment: $scope.currentEvent.enrollment,
- dueDate: DateUtils.formatFromUserToApi($scope.currentEvent.dueDate),
- status: $scope.currentEvent.status === 'SCHEDULE' ? 'ACTIVE' : $scope.currentEvent.status,
- program: $scope.currentEvent.program,
- programStage: $scope.currentEvent.programStage,
- orgUnit: $scope.currentEvent.dataValues && $scope.currentEvent.dataValues.length > 0 ? $scope.currentEvent.orgUnit : $scope.selectedOrgUnit.id,
- eventDate: DateUtils.formatFromUserToApi($scope.currentEvent.eventDate),
- trackedEntityInstance: $scope.currentEvent.trackedEntityInstance
+ var e = {event: eventToSave.event,
+ enrollment: eventToSave.enrollment,
+ dueDate: DateUtils.formatFromUserToApi(eventToSave.dueDate),
+ status: eventToSave.status === 'SCHEDULE' ? 'ACTIVE' : eventToSave.status,
+ program: eventToSave.program,
+ programStage: eventToSave.programStage,
+ orgUnit: eventToSave.dataValues && eventToSave.length > 0 ? eventToSave.orgUnit : $scope.selectedOrgUnit.id,
+ eventDate: DateUtils.formatFromUserToApi(eventToSave.eventDate),
+ trackedEntityInstance: eventToSave.trackedEntityInstance
};
DHIS2EventFactory.updateForEventDate(e).then(function(data){
- $scope.currentEvent.sortingDate = $scope.currentEvent.eventDate;
+ eventToSave.sortingDate = eventToSave.eventDate;
$scope.invalidDate = false;
- $scope.eventDateSaved = true;
- $scope.currentEvent.statusColor = EventUtils.getEventStatusColor($scope.currentEvent);
- $scope.currentEvent.visited = true;
-
+ $scope.eventDateSaved = eventToSave.event;
+ eventToSave.statusColor = EventUtils.getEventStatusColor(eventToSave);
+ sortEventsByStage();
updateCurrentEventInStage();
sortEventsByStage('UPDATE');
});
@@ -485,6 +599,8 @@
DHIS2EventFactory.update(dhis2Event).then(function(response){
$scope.currentEventOriginal = angular.copy($scope.currentEvent);
+ $scope.currentStageEventsOriginal = angular.copy($scope.currentStageEvents);
+
if(type === 'LAT' || type === 'LATLNG' ){
$scope.latitudeSaved = true;
}
@@ -529,15 +645,29 @@
$scope.note = '';
};
- $scope.getInputNotifcationClass = function(id, custom){
- if($scope.currentElement.id){
- if($scope.currentElement.saved && ($scope.currentElement.id === id)){
+ $scope.getInputDueDateClass = function(event) {
+ if(event.event === $scope.eventDateSaved) {
+ return 'input-success';
+ }
+ else {
+ return '';
+ }
+
+ };
+
+ $scope.getInputNotifcationClass = function(id, custom, event){
+ if(!event) {
+ event = $scope.currentEvent;
+ }
+ if($scope.currentElement.id && $scope.currentElement.event){
+ if($scope.currentElement.saved && ($scope.currentElement.id === id && $scope.currentElement.event === event.event)){
+
if(custom){
return 'input-success';
}
return 'form-control input-success';
}
- if(!$scope.currentElement.saved && ($scope.currentElement.id === id)){
+ if(!$scope.currentElement.saved && ($scope.currentElement.id === id && $scope.currentElement.event === event.event)){
if(custom){
return 'input-error';
}
@@ -859,7 +989,8 @@
DialogService,
stagesById,
dummyEvent,
- eventPeriods){
+ eventPeriods,
+ autoCreate){
$scope.stagesById = stagesById;
$scope.programStageId = dummyEvent.programStage;
$scope.eventPeriods = eventPeriods;
@@ -944,6 +1075,12 @@
}
});
+
+ };
+
+ //If the caller wants to create right away, go ahead and save.
+ if(autoCreate) {
+ $scope.save();
};
$scope.cancel = function(){
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry.html 2015-06-02 12:25:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry.html 2015-06-10 11:55:41 +0000
@@ -3,6 +3,8 @@
{{dataentryWidget.title| translate}}
<span class="pull-right widget-link">
+
+ <a href ng-click="toggleEventsTableDisplay()" title="{{showEventsAsTables ? 'toggle_table_view_off' : 'toggle_table_view_on'| translate}}" ng-show="stagesCanBeShownAsTable"><span ng-if="!showEventsAsTables"><i class="fa fa-bars vertical-center"></i></span><span ng-if="showEventsAsTables"><i class="fa fa-square vertical-center"></i></span></a>
<a href ng-click="filterEvents = !filterEvents" title="{{filterEvents ? 'list_events' : 'filter_events'| translate}}" ng-show="eventFilteringRequired"><span ng-if="!filterEvents"><i class="fa fa-filter vertical-center"></i></span><span ng-if="filterEvents"><i class="fa fa-list vertical-center"></i></span></a>
<a href ng-click="toggleLegend()" title="{{'event_color_legend'| translate}}" class="small-horizonal-spacing"><i class="fa fa-info-circle vertical-center"></i></a>
<a class="small-horizonal-spacing" href ng-click="expandCollapse(dataentryWidget)">
@@ -34,6 +36,7 @@
<!-- event add/filter icon begins -->
<span class='pull-right'>
+
<span ng-if="stageNeedsEvent(stage)">
<a href title="{{'create_new_event'| translate}}" ng-click="showCreateEvent(stage)" style="vertical-align:middle"><i class="fa fa-plus"></i></a>
</span>
@@ -76,7 +79,8 @@
<div ng-if="currentEvent">
<!-- event dates/scheduling begin -->
- <div class="row" ng-if="!currentStage.periodType">
+ <div class="row" ng-if="(displayCustomForm !== 'table') && !currentStage.periodType">
+
<div class="col-md-6">
{{currentEvent.reportDateDescription}}
<input type="text"
@@ -145,9 +149,12 @@
<!-- data entry form begins -->
<div ng-if="currentEvent.eventDate">
- <div class="clear vertical-spacing" ng-if="displayCustomForm" ng-include="'../dhis-web-commons/angular-forms/custom-form.html'"></div>
- <div class="clear vertical-spacing" ng-if="!displayCustomForm" ng-include="'components/dataentry/default-form.html'"></div>
- </div>
+ <div class="clear vertical-spacing" ng-if="displayCustomForm === 'custom'" ng-include="'../dhis-web-commons/customform/custom-form.html'"></div>
+ <div class="clear vertical-spacing" ng-if="displayCustomForm === 'default'" ng-include="'components/dataentry/default-form.html'"></div>
+ </div>
+ <!-- the table form is insensitive to the event date being present. In this situation the eventdate is set inside the table form -->
+ <div class="clear vertical-spacing" ng-if="displayCustomForm === 'table'" ng-include="'components/dataentry/table-entry-form.html'"></div>
+
<!-- data entry form ends -->
<!-- data entry/event buttons begins -->
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-form.html 2015-06-03 12:18:22 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-form.html 2015-06-10 11:55:41 +0000
@@ -14,7 +14,7 @@
</th>
</tr>
</thead>
- <tr class="col-md-12" ng-repeat="prStDe in currentStage.programStageDataElements">
+ <tr class="col-md-12" ng-repeat="prStDe in currentStage.programStageDataElements" ng-if="!isHidden(prStDe.dataElement.id)">
<td class="col-md-5">
{{prStDe.dataElement.formName ? prStDe.dataElement.formName : prStDe.dataElement.name}}
<a ng-if="prStDes[prStDe.dataElement.id].dataElement.description" Title="{{prStDes[prStDe.dataElement.id].dataElement.description}}" ng-init="showHelpText[prStDe.dataElement.id] = false;" ng-click="showHelpText[prStDe.dataElement.id] = !showHelpText[prStDe.dataElement.id]">
@@ -158,7 +158,8 @@
<div ng-if='currentEvent && currentStage.programStageSections.length'>
<accordion close-others='false'>
- <accordion-group heading="{{section.name}}" is-open="section.open" ng-repeat='section in currentStage.programStageSections'>
+ <!-- The inline style was needed to override overflow:hidden in the css. Can possibly be replaced with a CSS update. -->
+ <accordion-group heading="{{section.name}}" is-open="section.open" ng-repeat='section in currentStage.programStageSections' style="overflow:visible">
<table class="dhis2-list-table-striped">
<thead>
<tr class="col-md-12">
@@ -174,7 +175,7 @@
</tr>
</thead>
<tbody id="list">
- <tr class="col-md-12" ng-repeat="de in section.programStageDataElements">
+ <tr class="col-md-12" ng-repeat="de in section.programStageDataElements" ng-if="!isHidden(de.dataElement.id)">
<td class="col-md-5">
{{prStDes[de.dataElement.id].dataElement.formName ? prStDes[de.dataElement.id].dataElement.formName : prStDes[de.dataElement.id].dataElement.name}}
<a ng-if="prStDes[de.dataElement.id].dataElement.description" Title="{{prStDes[de.dataElement.id].dataElement.description}}" ng-init="showHelpText[de.dataElement.id] = false;" ng-click="showHelpText[de.dataElement.id] = !showHelpText[de.dataElement.id]">
=== added file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/table-entry-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/table-entry-form.html 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/table-entry-form.html 2015-06-11 18:44:52 +0000
@@ -0,0 +1,153 @@
+<form name="tableEntryOuterForm" novalidate>
+ <table class="table-borderless table-striped" ng-if='currentEvent'>
+ <thead>
+ <tr class="col-md-12">
+ <th class="col-md-2" >
+ {{currentEvent.reportDateDescription}}
+ </th>
+ <th class="col-md-2" ng-repeat="prStDe in currentStage.programStageDataElements" ng-if="!isHidden(prStDe.dataElement.id)">
+ {{prStDe.dataElement.formName ? prStDe.dataElement.formName : prStDe.dataElement.name}}
+ </th>
+ </tr>
+ </thead>
+ <tr class="col-md-12" ng-repeat="eventRow in currentStageEvents" ng-if="currentStageEvents">
+ <td>
+ <input type="text"
+ placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
+ class="form-control"
+ ng-class="getInputDueDateClass(eventRow)"
+ d2-date
+ ng-model="eventRow.eventDate"
+ ng-disabled="eventRow.status === 'SKIPPED' || eventRow.enrollmentStatus !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-required="true"
+ blur-or-change="saveEventDateForEvent(eventRow)"/>
+ <span ng-if="invalidDate === eventRow.event" class="error">{{'date_required'| translate}}</span>
+ </td>
+ <td class="col-md-2" ng-repeat="prStDe in currentStage.programStageDataElements" ng-if="!isHidden(prStDe.dataElement.id)">
+ <ng-form name="innerform">
+ <div ng-switch="prStDe.dataElement.type">
+ <div ng-switch-when="int">
+ <input type="number"
+ number-type={{prStDe.dataElement.numberType}}
+ ng-class='getInputNotifcationClass(prStDe.dataElement.id,false,eventRow)'
+ ng-model="eventRow[prStDe.dataElement.id]"
+ d2-number-validator
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-blur="saveDatavalueForEvent(prStDe,innerform.foo,eventRow)"
+ name="foo"/>
+ <span ng-show="dataEntryOuterForm.submitted && innerform.foo.$invalid" class="error">{{'number_required'| translate}}</span>
+ </div>
+ <div ng-switch-when="string">
+ <div ng-if="prStDe.dataElement.optionSet">
+ <div ng-if="!selectedProgram.dataEntryMethod || optionSets[prStDe.dataElement.optionSet.id].options.length >= 7">
+ <input type="text"
+ ng-class='getInputNotifcationClass(prStDe.dataElement.id,false,eventRow)'
+ ng-model="eventRow[prStDe.dataElement.id]"
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ typeahead="option.name as option.name for option in optionSets[prStDe.dataElement.optionSet.id].options | filter:$viewValue | limitTo:20"
+ typeahead-open-on-focus
+ typeahead-editable="false"
+ ng-blur="saveDatavalueForEvent(prStDe,innerform.foo,eventRow)"
+ name="foo"/>
+ <span ng-show="dataEntryOuterForm.submitted && innerform.foo.$invalid || !eventRow[prStDe.dataElement.id] && currentElement.id === prStDe.dataElement.id" class="error">{{'invalid'| translate}}</span>
+ </div>
+ <div ng-if="selectedProgram.dataEntryMethod && optionSets[prStDe.dataElement.optionSet.id].options.length < 7">
+ <label>
+ <input type="radio"
+ ng-class='getInputNotifcationClass(prStDe.dataElement.id,true,eventRow)'
+ name={{eventRow[prStDe.dataElement.id]}}
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-model="eventRow[prStDe.dataElement.id]"
+ ng-change="saveDatavalueForEvent(prStDe,null,eventRow)"
+ value=""> {{'no_value' | translate}}<br>
+ </label><br>
+ <span ng-repeat="option in optionSets[prStDe.dataElement.optionSet.id].options">
+ <label>
+ <input type="radio"
+ ng-class='getInputNotifcationClass(prStDe.dataElement.id,true,eventRow)'
+ name={{eventRow[prStDe.dataElement.id]}}
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-model="eventRow[prStDe.dataElement.id]"
+ ng-change="saveDatavalueForEvent(prStDe,null,eventRow)"
+ value={{option.name}}> {{option.name}}
+ </label><br>
+ </span>
+ </div>
+ </div>
+ <div ng-if="!prStDe.dataElement.optionSet">
+ <input type="text"
+ ng-class='getInputNotifcationClass(prStDe.dataElement.id,false,eventRow)'
+ ng-model="eventRow[prStDe.dataElement.id]"
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-blur="saveDatavalueForEvent(prStDe,innerform.foo,eventRow)"
+ name="foo"/>
+ </div>
+ </div>
+ <div ng-switch-when="bool">
+ <select ng-class='getInputNotifcationClass(prStDe.dataElement.id,false,eventRow)'
+ ng-model="eventRow[prStDe.dataElement.id]"
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-change="saveDatavalueForEvent(prStDe,innerform.foo,eventRow)"
+ name="foo">
+ <option value="">{{'please_select'| translate}}</option>
+ <option value="false">{{'no'| translate}}</option>
+ <option value="true">{{'yes'| translate}}</option>
+ </select>
+ </div>
+ <div ng-switch-when="date">
+ <input type="text"
+ placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
+ d2-date
+ max-date="prStDe.allowFutureDate ? '' : 0"
+ ng-class='getInputNotifcationClass(prStDe.dataElement.id,false,eventRow)'
+ ng-model="eventRow[prStDe.dataElement.id]"
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ blur-or-change="saveDatavalueForEvent(prStDe,innerform.foo,eventRow)"
+ name="foo"/>
+ </div>
+ <div ng-switch-when="trueOnly">
+ <input type="checkbox"
+ ng-class='getInputNotifcationClass(prStDe.dataElement.id,false,eventRow)'
+ ng-model="eventRow[prStDe.dataElement.id]"
+ ng-required={{prStDe.compulsory}}
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-change="saveDatavalueForEvent(prStDe,innerform.foo,eventRow)"
+ name="foo"/>
+ </div>
+ </div>
+ <div class="alert alert-warning alert-dismissible" role="alert" ng-if="warningMessages[prStDe.dataElement.id]">
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
+ {{warningMessages[prStDe.dataElement.id]}}
+ </div>
+ <div class="alert alert-danger alert-dismissible" role="alert" ng-if="errorMessages[prStDe.dataElement.id]">
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
+ {{errorMessages[prStDe.dataElement.id]}}
+ </div>
+ </ng-form>
+ <span ng-show="dataEntryOuterForm.submitted && innerform.foo.$invalid" class="error">{{'required'| translate}}</span>
+ </td>
+ <!--<td class="col-md-2" ng-if="allowProvidedElsewhereExists">
+ <div class="align-center" ng-show="prStDe.allowProvidedElsewhere">
+ <input type="checkbox"
+ ng-model="eventRow.providedElsewhere[prStDe.dataElement.id]"
+ ng-disabled="selectedEnrollment.status !== 'ACTIVE' || eventRow.editingNotAllowed"
+ ng-change="saveDatavalueLocation(prStDe)"/>
+ </div>
+ </td>-->
+ </tr>
+ </table>
+ <div class="col-md-12"style="text-align: right">
+ <button type="button"
+ class="btn btn-default"
+ ng-click="showCreateEvent(currentStage)">
+ {{'add'| translate}}
+ </button>
+ </div>
+</form>
=== 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-06-02 12:25:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js 2015-06-10 11:55:41 +0000
@@ -51,7 +51,7 @@
}
$scope.selectedOrgUnit = SessionStorageService.get('SELECTED_OU');
- $scope.selectedEnrollment = {dateOfEnrollment: '', dateOfIncident: ''};
+ $scope.selectedEnrollment = {dateOfEnrollment: $scope.today, dateOfIncident: $scope.today};
$scope.trackedEntities = {available: []};
TEService.getAll().then(function(entities){
=== added directory 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound'
=== added file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound-controller.js 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound-controller.js 2015-06-02 05:55:14 +0000
@@ -0,0 +1,58 @@
+trackerCapture.controller('RuleBoundController',
+ function(
+ $rootScope,
+ $scope,
+ $log) {
+
+
+ $scope.widget = $scope.$parent.$parent.biggerWidget ? $scope.$parent.$parent.biggerWidget
+ : $scope.$parent.$parent.smallerWidget ? $scope.$parent.$parent.smallerWidget : null;
+ $scope.widgetTitle = $scope.widget.title;
+ $scope.widgetCode = $scope.widget.code;
+
+
+ $scope.displayTextEffects = {};
+ $scope.displayKeyDataEffects = {};
+
+ //listen for updated rule effects
+ $scope.$on('ruleeffectsupdated', function(event, args) {
+ var textInEffect = false;
+ var keyDataInEffect = false;
+
+ //Bind non-bound rule effects, if any.
+ angular.forEach($rootScope.ruleeffects, function(effect) {
+ if(effect.location == $scope.widgetCode){
+ //This effect is affecting the local widget
+
+ if(effect.action == "DISPLAYTEXT") {
+ //this action is display text. Make sure the displaytext is
+ //added to the local list of displayed texts
+ if(!angular.isObject($scope.displayTextEffects[effect.id])){
+ $scope.displayTextEffects[effect.id] = effect;
+ }
+ if(effect.ineffect)
+ {
+ textInEffect = true;
+ }
+ }
+ else if(effect.action == "DISPLAYKEYVALUEPAIR") {
+ //this action is display text. Make sure the displaytext is
+ //added to the local list of displayed texts
+ if(!angular.isObject($scope.displayTextEffects[effect.id])){
+ $scope.displayKeyDataEffects[effect.id] = effect;
+ }
+ if(effect.ineffect)
+ {
+ keyDataInEffect = true;
+ }
+ } else {
+ $log.warn("action: '" + effect.action + "' not supported by rulebound-controller.js")
+ }
+ }
+ });
+
+ $scope.showKeyDataSection = keyDataInEffect;
+ $scope.showTextSection = textInEffect;
+
+ });
+});
\ No newline at end of file
=== added file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound.html 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/rulebound/rulebound.html 2015-06-02 05:55:14 +0000
@@ -0,0 +1,29 @@
+<div class="panel panel-info" ng-controller="RuleBoundController">
+ <div class="panel-heading handle bold">
+ {{widgetTitle | translate}}
+ <span class="pull-right">
+ <a class="small-horizonal-spacing" href ng-click="expandCollapse(this.widget)">
+ <span ng-show="this.widget.expand"><i class="fa fa-chevron-up" title="{{'collapse' | translate}}"></i></span>
+ <span ng-show="!this.widget.expand"><i class="fa fa-chevron-down" title="{{'expand' | translate}}"></i></span>
+ </a>
+ <a class="small-horizonal-spacing" href ng-click="removeWidget(this.widget)" title="{{'remove' | translate}}"><i class="fa fa-times-circle"></i></a>
+ </span>
+ </div>
+ <div ng-show="this.widget.expand" class="dashboard-element-container">
+ <div class="panel-body" ng-if="showKeyDataSection">
+ <div ng-repeat="(key,item) in displayKeyDataEffects" ng-show="item.ineffect" class="info-container">
+ <div class="info-container-heading">{{item.content | trimquotes }}</div>
+ <div class="info-container-text">{{item.data | trimquotes}}</div>
+ </div>
+ </div>
+ <table class="table table-striped dashboard-element-container" ng-if="showTextSection">
+ <tr ng-repeat="(key,item) in displayTextEffects" ng-if="item.ineffect">
+ <td>
+ <div>
+ {{item.content | trimquotes}}{{item.data | trimquotes}}
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+</div>
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties 2015-06-02 12:25:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties 2015-06-10 11:55:41 +0000
@@ -106,6 +106,8 @@
new_event=New event
create_new_event=Create new event
create_new_event_repeatable=Create new event from a repeatable stage
+toggle_table_view_on=Show events as tables where possible
+toggle_table_view_off=Show data entry forms for each event
event_recorded_on=Event recorded on
at=at
show_more=Click for more
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app_ar.properties'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app_ar.properties 2015-01-20 11:51:34 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app_ar.properties 2015-06-08 08:54:37 +0000
@@ -1,261 +1,263 @@
-tracked_entity_management=\u0627\u062F\u0631\u0627\u0629 \u0627\u0644\u0643\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0645\u062A\u062A\u0628\u0639\u0629
-registering_unit=\u0648\u062D\u062F\u0629 \u062A\u0633\u062C\u064A\u0644
-program=\u0628\u0631\u0646\u0627\u0645\u062C
-please_select=\u0631\u062C\u0627\u0621\u064B \u0627\u062E\u062A\u0631
-please_select_a_program=\u0631\u062C\u0627\u0621\u064B \u0627\u062E\u062A\u0631 \u0628\u0631\u0646\u0627\u0645\u062C\u0627\u064B
-no_program_exists=\u0644\u0627\u064A\u0648\u062C\u062F \u0628\u0631\u0646\u0627\u0645\u062C
-please_select_a_program_for_enrollment=\u0631\u062C\u0627\u0621\u064B \u0627\u062E\u062A\u0631 \u0628\u0631\u0646\u0627\u0645\u062C\u0627\u064B \u0644\u0644\u062A\u0633\u062C\u064A\u0644
-please_select_program_report=\u0631\u062C\u0627\u0621\u064B \u0627\u062E\u062A\u0631 \u0628\u0631\u0646\u0627\u0645\u062C\u0627\u064B \u0644\u0644\u0625\u0639\u062F\u0627\u062F
-please_select_a_relationship=\u0631\u062C\u0627\u0621\u064B \u0627\u062E\u062A\u0631 \u0639\u0644\u0627\u0642\u0629
-no_relationship=\u0644\u0627\u062A\u0648\u062C\u062F \u0639\u0644\u0627\u0642\u0629
-relationship_not_possible=\u0644\u0627 \u064A\u0648\u062C\u062F \u0645\u062B\u0627\u0644 \u0644\u0643\u064A\u0627\u0646 \u0645\u062A\u0628\u0639 . \u0627\u0644\u0639\u0644\u0627\u0642\u0629 \u063A\u064A\u0631 \u0645\u0645\u0643\u0646\u0629
-no_program_exists_enrollment=\u0644\u0627\u064A\u0648\u062C\u062F \u0628\u0631\u0646\u0627\u0645\u062C \u0644\u0639\u0644\u0627\u0642\u0629 \u0627\u0644\u062A\u0646\u0638\u064A\u0645 \u0627\u0644\u0645\u062E\u062A\u0627\u0631\u0629 . \u0627\u0644\u062A\u0633\u062C\u064A\u0644 \u063A\u064A\u0631 \u0645\u0645\u0643\u0646
-no_program_exists_report=\u0644\u0627\u064A\u0648\u062C\u062F \u0628\u0631\u0646\u0627\u0645\u062C \u0644\u0639\u0644\u0627\u0642\u0629 \u0627\u0644\u062A\u0646\u0638\u064A\u0645 \u0627\u0644\u0645\u062E\u062A\u0627\u0631\u0629 . \u0627\u0644\u0627\u0639\u062F\u0627\u062F \u063A\u064A\u0631 \u0645\u0645\u0643\u0646
-not_yet_enrolled_data_entry=\u063A\u064A\u0631 \u0645\u0633\u062C\u0644 \u062D\u062A\u0649 \u0627\u0644\u0644\u0622\u0646 . \u0627\u062F\u062E\u0627\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u063A\u064A\u0631 \u0645\u0645\u0643\u0646
-not_yet_enrolled_enrollment=\u063A\u064A\u0631 \u0645\u0633\u062C\u0644 \u062D\u062A\u0649 \u0627\u0644\u0644\u0622\u0646 . \u0631\u062C\u0627\u0621\u064B \u0633\u062C\u0644
-not_active_enrollment_exists=\u0644\u0627\u064A\u0648\u062C\u062F \u062A\u0633\u062C\u064A\u0644 \u0641\u0639\u0627\u0644 \u0644\u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u0645\u062E\u062A\u0627\u0631
-not_yet_enrolled_note=\u063A\u064A\u0631 \u0645\u0633\u062C\u0644 \u062D\u062A\u0649 \u0627\u0644\u0622\u0646 . \u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u0645\u0644\u0627\u062D\u0638\u0629 \u063A\u064A\u0631 \u0645\u0645\u0643\u0646
-not_yet_enrolled_report=\u063A\u064A\u0631 \u0645\u0633\u062C\u0644 \u062D\u062A\u0649 \u0627\u0644\u0622\u0646 . \u0627\u0644\u0627\u0639\u062F\u0627\u062F \u063A\u064A\u0631 \u0645\u0645\u0643\u0646
-no_data_report=\u0644\u0627\u064A\u0648\u062C\u062F \u062A\u0633\u062C\u064A\u0644 \u0644\u064A\u062A\u0645 \u0627\u0644\u0627\u0639\u062F\u0627\u062F
-empty_notes=\u0644\u0627\u0626\u062D\u0629 \u0627\u0644\u0645\u0644\u0627\u062D\u0638\u0627\u062A \u0627\u0644\u0641\u0627\u0631\u063A\u0629
-no_event_is_yet_created=\u0644\u0627\u064A\u0648\u062C\u062F \u062D\u062F\u062B \u0645\u062A\u0648\u0641\u0631 \u0644\u0627\u062F\u062E\u0627\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A . \u0631\u062C\u0627\u0621\u064B \u0627\u0646\u0634\u0626 \u0648\u0627\u062D\u062F\u0627\u064B
-event_creation=\u0631\u062C\u0627\u0621\u064B \u0627\u0646\u0634\u0626 \u0648\u0627\u062D\u062F\u0627\u064B \u0645\u0646 \u0627\u0644\u0623\u0633\u0641\u0644
-not_selected=\u063A\u064A\u0631 \u0645\u062E\u062A\u0627\u0631
+tracked_entity_management=\u0627\u062f\u0631\u0627\u0629 \u0627\u0644\u0643\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0645\u062a\u062a\u0628\u0639\u0629
+registering_unit=\u0648\u062d\u062f\u0629 \u062a\u0633\u062c\u064a\u0644
+program=\u0628\u0631\u0646\u0627\u0645\u062c
+please_select=\u0631\u062c\u0627\u0621\u064b \u0627\u062e\u062a\u0631
+please_select_a_program=\u0631\u062c\u0627\u0621\u064b \u0627\u062e\u062a\u0631 \u0628\u0631\u0646\u0627\u0645\u062c\u0627\u064b
+no_program_exists=\u0644\u0627\u064a\u0648\u062c\u062f \u0628\u0631\u0646\u0627\u0645\u062c
+please_select_a_program_for_enrollment=\u0631\u062c\u0627\u0621\u064b \u0627\u062e\u062a\u0631 \u0628\u0631\u0646\u0627\u0645\u062c\u0627\u064b \u0644\u0644\u062a\u0633\u062c\u064a\u0644
+please_select_program_report=\u0631\u062c\u0627\u0621\u064b \u0627\u062e\u062a\u0631 \u0628\u0631\u0646\u0627\u0645\u062c\u0627\u064b \u0644\u0644\u0625\u0639\u062f\u0627\u062f
+please_select_a_relationship=\u0631\u062c\u0627\u0621\u064b \u0627\u062e\u062a\u0631 \u0639\u0644\u0627\u0642\u0629
+no_relationship=\u0644\u0627\u062a\u0648\u062c\u062f \u0639\u0644\u0627\u0642\u0629
+relationship_not_possible=\u0644\u0627 \u064a\u0648\u062c\u062f \u0645\u062b\u0627\u0644 \u0644\u0643\u064a\u0627\u0646 \u0645\u062a\u0628\u0639 . \u0627\u0644\u0639\u0644\u0627\u0642\u0629 \u063a\u064a\u0631 \u0645\u0645\u0643\u0646\u0629
+no_program_exists_enrollment=\u0644\u0627\u064a\u0648\u062c\u062f \u0628\u0631\u0646\u0627\u0645\u062c \u0644\u0639\u0644\u0627\u0642\u0629 \u0627\u0644\u062a\u0646\u0638\u064a\u0645 \u0627\u0644\u0645\u062e\u062a\u0627\u0631\u0629 . \u0627\u0644\u062a\u0633\u062c\u064a\u0644 \u063a\u064a\u0631 \u0645\u0645\u0643\u0646
+no_program_exists_report=\u0644\u0627\u064a\u0648\u062c\u062f \u0628\u0631\u0646\u0627\u0645\u062c \u0644\u0639\u0644\u0627\u0642\u0629 \u0627\u0644\u062a\u0646\u0638\u064a\u0645 \u0627\u0644\u0645\u062e\u062a\u0627\u0631\u0629 . \u0627\u0644\u0627\u0639\u062f\u0627\u062f \u063a\u064a\u0631 \u0645\u0645\u0643\u0646
+not_yet_enrolled_data_entry=\u063a\u064a\u0631 \u0645\u0633\u062c\u0644 \u062d\u062a\u0649 \u0627\u0644\u0644\u0622\u0646 . \u0627\u062f\u062e\u0627\u0644 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u063a\u064a\u0631 \u0645\u0645\u0643\u0646
+not_yet_enrolled_enrollment=\u063a\u064a\u0631 \u0645\u0633\u062c\u0644 \u062d\u062a\u0649 \u0627\u0644\u0644\u0622\u0646 . \u0631\u062c\u0627\u0621\u064b \u0633\u062c\u0644
+not_active_enrollment_exists=\u0644\u0627\u064a\u0648\u062c\u062f \u062a\u0633\u062c\u064a\u0644 \u0641\u0639\u0627\u0644 \u0644\u0644\u0628\u0631\u0646\u0627\u0645\u062c \u0627\u0644\u0645\u062e\u062a\u0627\u0631
+not_yet_enrolled_note=\u063a\u064a\u0631 \u0645\u0633\u062c\u0644 \u062d\u062a\u0649 \u0627\u0644\u0622\u0646 . \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u0645\u0644\u0627\u062d\u0638\u0629 \u063a\u064a\u0631 \u0645\u0645\u0643\u0646
+not_yet_enrolled_report=\u063a\u064a\u0631 \u0645\u0633\u062c\u0644 \u062d\u062a\u0649 \u0627\u0644\u0622\u0646 . \u0627\u0644\u0627\u0639\u062f\u0627\u062f \u063a\u064a\u0631 \u0645\u0645\u0643\u0646
+no_data_report=\u0644\u0627\u064a\u0648\u062c\u062f \u062a\u0633\u062c\u064a\u0644 \u0644\u064a\u062a\u0645 \u0627\u0644\u0627\u0639\u062f\u0627\u062f
+empty_notes=\u0644\u0627\u0626\u062d\u0629 \u0627\u0644\u0645\u0644\u0627\u062d\u0638\u0627\u062a \u0627\u0644\u0641\u0627\u0631\u063a\u0629
+no_event_is_yet_created=\u0644\u0627\u064a\u0648\u062c\u062f \u062d\u062f\u062b \u0645\u062a\u0648\u0641\u0631 \u0644\u0627\u062f\u062e\u0627\u0644 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a . \u0631\u062c\u0627\u0621\u064b \u0627\u0646\u0634\u0626 \u0648\u0627\u062d\u062f\u0627\u064b
+event_creation=\u0631\u062c\u0627\u0621\u064b \u0627\u0646\u0634\u0626 \u0648\u0627\u062d\u062f\u0627\u064b \u0645\u0646 \u0627\u0644\u0623\u0633\u0641\u0644
+not_selected=\u063a\u064a\u0631 \u0645\u062e\u062a\u0627\u0631
create=\u0627\u0646\u0634\u0626
view_all=\u0627\u0639\u0631\u0636 \u0627\u0644\u0643\u0644
-list_all_entities=\u0627\u062F\u0631\u062C \u0643\u0644 \u0627\u0644\u0643\u064A\u0627\u0646\u0627\u062A
-list_all=\u0627\u062F\u0631\u062C \u0627\u0644\u0643\u0644
-list=\u0627\u062F\u0631\u062C
-invalid=\u063A\u064A\u0631 \u0635\u0627\u0644\u062D
+list_all_entities=\u0627\u062f\u0631\u062c \u0643\u0644 \u0627\u0644\u0643\u064a\u0627\u0646\u0627\u062a
+list_all=\u0627\u062f\u0631\u062c \u0627\u0644\u0643\u0644
+list=\u0627\u062f\u0631\u062c
+invalid=\u063a\u064a\u0631 \u0635\u0627\u0644\u062d
required=\u0645\u0637\u0644\u0648\u0628
number_required=\u0631\u0642\u0645 \u0645\u0637\u0644\u0648\u0628
-date_required=\u0628\u064A\u0627\u0646\u0627\u062A \u0645\u0637\u0644\u0648\u0628\u0629
-filter=\u062A\u0635\u0641\u064A\u0629
-search=\u0628\u062D\u062B
-_search=\u0627\u0628\u062D\u062B
-advanced_search=\u0628\u062D\u062B \u0645\u062A\u0642\u062F\u0645
-search_for=\u0627\u0628\u062D\u062B \u0639\u0646
-type_your_search_criteria_here=\u062F\u0648\u0650\u0646 \u0645\u0639\u0627\u064A\u064A\u0631 \u0627\u0644\u0628\u062D\u062B \u0647\u0646\u0627
-search_input_required=\u0631\u062C\u0627\u0621\u064B \u062D\u062F\u062F \u0645\u0639\u0627\u064A\u064A\u0631 \u0627\u0644\u0628\u062D\u062B
-in_progress=\u0642\u064A\u062F \u0627\u0644\u0639\u0645\u0644
-registered_entities=\u0643\u064A\u0627\u0646\u0627\u062A \u0645\u0633\u062C\u0644\u0629
-empty_entity_list=\u0644\u0627\u062A\u0648\u062C\u062F \u0643\u064A\u0627\u0646\u0627\u062A \u0645\u0633\u062C\u0644\u0629
-empty=\u0641\u0627\u0631\u063A
-form_is_empty_fill_at_least_one=\u0627\u0644\u0627\u0633\u062A\u0645\u0627\u0631\u0629 \u0641\u0627\u0631\u063A\u0629 . \u0631\u062C\u0627\u0621\u064B \u0627\u0645\u0644\u0623 \u0648\u0627\u062D\u062F\u0629 \u0639\u0644\u0649 \u0627\u0644\u0623\u0642\u0644
-total_number_of_pages=\u0639\u062F\u062F \u0627\u0644\u0635\u0641\u062D\u0627\u062A
-rows_per_page=\u0639\u062F\u062F \u0627\u0644\u0635\u0641\u0648\u0641 \u0641\u064A \u0643\u0644 \u0635\u0641\u062D\u0629
-jump_to_page=\u0627\u0646\u062A\u0642\u0644 \u0627\u0644\u0649 \u0635\u0641\u062D\u0629
-page=\u0635\u0641\u062D\u0629
-first=\u0623\u0648\u0644\u0627\u064B
+date_required=\u0628\u064a\u0627\u0646\u0627\u062a \u0645\u0637\u0644\u0648\u0628\u0629
+filter=\u062a\u0635\u0641\u064a\u0629
+search=\u0628\u062d\u062b
+_search=\u0627\u0628\u062d\u062b
+advanced_search=\u0628\u062d\u062b \u0645\u062a\u0642\u062f\u0645
+search_for=\u0627\u0628\u062d\u062b \u0639\u0646
+type_your_search_criteria_here=\u062f\u0648\u0650\u0646 \u0645\u0639\u0627\u064a\u064a\u0631 \u0627\u0644\u0628\u062d\u062b \u0647\u0646\u0627
+search_input_required=\u0631\u062c\u0627\u0621\u064b \u062d\u062f\u062f \u0645\u0639\u0627\u064a\u064a\u0631 \u0627\u0644\u0628\u062d\u062b
+in_progress=\u0642\u064a\u062f \u0627\u0644\u0639\u0645\u0644
+registered_entities=\u0643\u064a\u0627\u0646\u0627\u062a \u0645\u0633\u062c\u0644\u0629
+empty_entity_list=\u0644\u0627\u062a\u0648\u062c\u062f \u0643\u064a\u0627\u0646\u0627\u062a \u0645\u0633\u062c\u0644\u0629
+empty=\u0641\u0627\u0631\u063a
+form_is_empty_fill_at_least_one=\u0627\u0644\u0627\u0633\u062a\u0645\u0627\u0631\u0629 \u0641\u0627\u0631\u063a\u0629 . \u0631\u062c\u0627\u0621\u064b \u0627\u0645\u0644\u0623 \u0648\u0627\u062d\u062f\u0629 \u0639\u0644\u0649 \u0627\u0644\u0623\u0642\u0644
+total_number_of_pages=\u0639\u062f\u062f \u0627\u0644\u0635\u0641\u062d\u0627\u062a
+rows_per_page=\u0639\u062f\u062f \u0627\u0644\u0635\u0641\u0648\u0641 \u0641\u064a \u0643\u0644 \u0635\u0641\u062d\u0629
+jump_to_page=\u0627\u0646\u062a\u0642\u0644 \u0627\u0644\u0649 \u0635\u0641\u062d\u0629
+page=\u0635\u0641\u062d\u0629
+first=\u0623\u0648\u0644\u0627\u064b
previous=\u0627\u0644\u0633\u0627\u0628\u0642
-next=\u0627\u0644\u062A\u0627\u0644\u064A
-last=\u0627\u062E\u064A\u0631
-go_to_dashboard=\u0627\u0630\u0647\u0628 \u0627\u0644\u0649 \u0644\u0648\u062D\u0629 \u0627\u0644\u0639\u062F\u0627\u062F\u0627\u062A
+next=\u0627\u0644\u062a\u0627\u0644\u064a
+last=\u0627\u062e\u064a\u0631
+go_to_dashboard=\u0627\u0630\u0647\u0628 \u0627\u0644\u0649 \u0644\u0648\u062d\u0629 \u0627\u0644\u0639\u062f\u0627\u062f\u0627\u062a
go=\u0627\u0630\u0647\u0628
-edit=\u062A\u062D\u0631\u064A\u0631
-edit_profile=\u062D\u0631\u0631 \u0627\u0644\u062A\u0634\u0643\u064A\u0644 \u0627\u0644\u062C\u0627\u0646\u0628\u064A
-association=\u0646\u0638\u0650\u0645 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0627\u062A
+edit=\u062a\u062d\u0631\u064a\u0631
+edit_profile=\u062d\u0631\u0631 \u0627\u0644\u062a\u0634\u0643\u064a\u0644 \u0627\u0644\u062c\u0627\u0646\u0628\u064a
+association=\u0646\u0638\u0650\u0645 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0627\u062a
change_location=\u0646\u0638\u0650\u0645 \u0627\u0644\u0645\u0648\u0642\u0639
-details_history=\u062A\u0641\u0627\u0635\u064A\u0644/\u062A\u0627\u0631\u064A\u062E
-history=\u062A\u0627\u0631\u064A\u062E
+details_history=\u062a\u0641\u0627\u0635\u064a\u0644/\u062a\u0627\u0631\u064a\u062e
+history=\u062a\u0627\u0631\u064a\u062e
complete=\u0627\u0643\u0645\u0644
terminate=\u0627\u0646\u0647\u0627\u0621
-completed=\u0645\u0643\u062A\u0645\u0644
-terminated=\u0645\u0646\u062A\u0647\u064A
-delete=\u0627\u062D\u0630\u0641
+completed=\u0645\u0643\u062a\u0645\u0644
+terminated=\u0645\u0646\u062a\u0647\u064a
+delete=\u0627\u062d\u0630\u0641
remove=\u0627\u0632\u0644
-entity=\u0643\u064A\u0627\u0646
-clear=\u0627\u0645\u0633\u062D
+entity=\u0643\u064a\u0627\u0646
+clear=\u0627\u0645\u0633\u062d
add=\u0623\u0636\u0641
-add_note=\u0623\u0636\u0641 \u0645\u0644\u0627\u062D\u0638\u0629
-search_note=\u0627\u0628\u062D\u062B \u0639\u0646 \u0645\u0644\u0627\u062D\u0638\u0627\u062A
-add_new_note_here=\u0627\u0636\u0641 \u0645\u0644\u0627\u062D\u0638\u0629 \u062C\u062F\u064A\u062F\u0629 \u0647\u0646\u0627
-skipped=\u0645\u062A\u062E\u0637\u0649
-skip=\u062A\u062E\u0637\u0649
-unskip=\u062C\u062F\u0648\u0644\u0629 \u0633\u0627\u0628\u0642\u0629
-complete=\u0645\u0643\u062A\u0645\u0644
-incomplete=\u063A\u064A\u0631 \u0645\u0643\u062A\u0645\u0644
-validate=\u062A\u062D\u0642\u0642 \u0645\u0646 \u0627\u0644\u0635\u062D\u0629
-status=\u062D\u0627\u0644\u0629
-details=\u062A\u0641\u0627\u0635\u064A\u0644
-_details=\u062A\u0641\u0627\u0635\u064A\u0644
-created_by=\u0645\u0633\u062C\u0644 \u0628\u0648\u0627\u0633\u0637\u0629
-date=\u062A\u0627\u0631\u064A\u062E
-_date=\u062A\u0627\u0631\u064A\u062E
-add_new=\u0623\u0635\u0641 \u062C\u062F\u064A\u062F\u0627\u064B
-event_name=\u0627\u0633\u0645 \u0627\u0644\u062D\u062F\u062B
-new_event=\u062D\u062F\u062B \u062C\u062F\u064A\u062F
-create_new_event=\u0627\u0646\u0634\u0626 \u062D\u062F\u062B\u0627\u064B \u062C\u062F\u064A\u062F\u0627\u064B
-create_new_event_repeatable=\u0627\u0646\u0634\u0626 \u062D\u062F\u062B\u0627\u064B \u062C\u062F\u064A\u062F\u0627\u064B \u0645\u0646 \u0645\u0631\u062D\u0644\u0629 \u0645\u0645\u0643\u0646 \u0627\u0639\u0627\u062F\u062A\u0647\u0627
-close_search=\u0627\u063A\u0644\u0642 \u0627\u0644\u0628\u062D\u062B
-search_attributes=\u062E\u0635\u0627\u0626\u0635 \u0627\u0644\u0628\u062D\u062B
-available_search_attributes=\u062E\u0635\u0627\u0626\u0635 \u0627\u0644\u0628\u062D\u062B \u0627\u0644\u0645\u062A\u0648\u0641\u0631\u0629
-selected_search_attributes=\u062E\u0635\u0627\u0626\u0635 \u0627\u0644\u0628\u062D\u062B \u0627\u0644\u0645\u062E\u062A\u0627\u0631\u0629
-search_for_dashboard=\u0627\u0628\u062D\u062B \u0639\u0646 \u0639\u0646\u0627\u0635\u0631 \u0644\u0648\u062D\u0629 \u0627\u0644\u0639\u062F\u0627\u062F\u0627\u062A
-dashboard=\u0644\u0648\u062D\u0629 \u0627\u0644\u0639\u062F\u0627\u062F\u0627\u062A
-home=\u0627\u0644\u0635\u0641\u062D\u0629 \u0627\u0644\u0631\u0626\u0633\u064A\u0629
-back=\u0644\u0644\u062E\u0644\u0641
-profile=\u062A\u0634\u0643\u064A\u0644 \u062C\u0627\u0646\u0628\u064A
-enrollment=\u062A\u0633\u062C\u064A\u0644
-delete_enrollment=\u0627\u0644\u063A\u0650 \u0627\u0644\u062A\u0633\u062C\u064A\u0644
-terminate_enrollment=\u0627\u0646\u0647\u0650 \u0627\u0644\u062A\u0633\u062C\u064A\u0644
-complete_enrollment=\u0627\u0643\u0645\u0644 \u0627\u0644\u062A\u0633\u062C\u0628\u0644
-enrollment_date=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0633\u062C\u064A\u0644
-notes=\u0645\u0644\u0627\u062D\u0638\u0627\u062A
+add_note=\u0623\u0636\u0641 \u0645\u0644\u0627\u062d\u0638\u0629
+search_note=\u0627\u0628\u062d\u062b \u0639\u0646 \u0645\u0644\u0627\u062d\u0638\u0627\u062a
+add_new_note_here=\u0627\u0636\u0641 \u0645\u0644\u0627\u062d\u0638\u0629 \u062c\u062f\u064a\u062f\u0629 \u0647\u0646\u0627
+skipped=\u0645\u062a\u062e\u0637\u0649
+skip=\u062a\u062e\u0637\u0649
+unskip=\u062c\u062f\u0648\u0644\u0629 \u0633\u0627\u0628\u0642\u0629
+complete=\u0645\u0643\u062a\u0645\u0644
+incomplete=\u063a\u064a\u0631 \u0645\u0643\u062a\u0645\u0644
+validate=\u062a\u062d\u0642\u0642 \u0645\u0646 \u0627\u0644\u0635\u062d\u0629
+status=\u062d\u0627\u0644\u0629
+details=\u062a\u0641\u0627\u0635\u064a\u0644
+_details=\u062a\u0641\u0627\u0635\u064a\u0644
+created_by=\u0645\u0633\u062c\u0644 \u0628\u0648\u0627\u0633\u0637\u0629
+date=\u062a\u0627\u0631\u064a\u062e
+_date=\u062a\u0627\u0631\u064a\u062e
+add_new=\u0623\u0635\u0641 \u062c\u062f\u064a\u062f\u0627\u064b
+event_name=\u0627\u0633\u0645 \u0627\u0644\u062d\u062f\u062b
+new_event=\u062d\u062f\u062b \u062c\u062f\u064a\u062f
+create_new_event=\u0627\u0646\u0634\u0626 \u062d\u062f\u062b\u0627\u064b \u062c\u062f\u064a\u062f\u0627\u064b
+create_new_event_repeatable=\u0627\u0646\u0634\u0626 \u062d\u062f\u062b\u0627\u064b \u062c\u062f\u064a\u062f\u0627\u064b \u0645\u0646 \u0645\u0631\u062d\u0644\u0629 \u0645\u0645\u0643\u0646 \u0627\u0639\u0627\u062f\u062a\u0647\u0627
+toggle_table_view_on=\u0645\u0634\u0627\u0647\u062f\u0629 \u0623\u062d\u062f\u0627\u062b \u0643\u062c\u062f\u0627\u0648\u0644 \u062d\u064a\u062b\u0645\u0627 \u0623\u0645\u0643\u0646(\u062c\u0648\u062c\u0644 \u062a\u0631\u062c\u0645\u0629)
+toggle_table_view_off=\u0646\u0645\u0627\u0630\u062c \u0625\u062f\u062e\u0627\u0644 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u062a\u0638\u0647\u0631 \u0644\u0643\u0644 \u062d\u062f\u062b(\u062c\u0648\u062c\u0644 \u062a\u0631\u062c\u0645\u0629)
+close_search=\u0627\u063a\u0644\u0642 \u0627\u0644\u0628\u062d\u062b
+search_attributes=\u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u0628\u062d\u062b
+available_search_attributes=\u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u0628\u062d\u062b \u0627\u0644\u0645\u062a\u0648\u0641\u0631\u0629
+selected_search_attributes=\u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u0628\u062d\u062b \u0627\u0644\u0645\u062e\u062a\u0627\u0631\u0629
+search_for_dashboard=\u0627\u0628\u062d\u062b \u0639\u0646 \u0639\u0646\u0627\u0635\u0631 \u0644\u0648\u062d\u0629 \u0627\u0644\u0639\u062f\u0627\u062f\u0627\u062a
+dashboard=\u0644\u0648\u062d\u0629 \u0627\u0644\u0639\u062f\u0627\u062f\u0627\u062a
+home=\u0627\u0644\u0635\u0641\u062d\u0629 \u0627\u0644\u0631\u0626\u0633\u064a\u0629
+back=\u0644\u0644\u062e\u0644\u0641
+profile=\u062a\u0634\u0643\u064a\u0644 \u062c\u0627\u0646\u0628\u064a
+enrollment=\u062a\u0633\u062c\u064a\u0644
+delete_enrollment=\u0627\u0644\u063a\u0650 \u0627\u0644\u062a\u0633\u062c\u064a\u0644
+terminate_enrollment=\u0627\u0646\u0647\u0650 \u0627\u0644\u062a\u0633\u062c\u064a\u0644
+complete_enrollment=\u0627\u0643\u0645\u0644 \u0627\u0644\u062a\u0633\u062c\u0628\u0644
+enrollment_date=\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0633\u062c\u064a\u0644
+notes=\u0645\u0644\u0627\u062d\u0638\u0627\u062a
relationship=\u0639\u0644\u0627\u0642\u0629
-relationships=\u0639\u0644\u0627\u0642\u0627\u062A
+relationships=\u0639\u0644\u0627\u0642\u0627\u062a
add_relationship=\u0627\u0636\u0641 \u0639\u0644\u0627\u0642\u0629
a_is_to_b=\u0639\u0644\u0627\u0642\u0629 (\u0623) \u0644\u0640\u0640 (\u0628)
b_is_to_a=\u0639\u0644\u0627\u0642\u0629 (\u0628) \u0644\u0640\u0640 (\u0623)
-please_select_source=\u0631\u062C\u0627\u0621\u064B \u0627\u062E\u062A\u0631 \u0645\u0635\u062F\u0631\u0627\u064B
-register_new=\u0633\u062C\u0644 \u062C\u062F\u064A\u062F\u0627\u064B
-search_from_existing=\u0627\u0628\u062D\u062B \u0628\u0645\u0639\u064A\u0627\u0631 \u0645\u062A\u0649 \u062A\u0645 \u0627\u0644\u0627\u0646\u0634\u0627\u0621
+please_select_source=\u0631\u062c\u0627\u0621\u064b \u0627\u062e\u062a\u0631 \u0645\u0635\u062f\u0631\u0627\u064b
+register_new=\u0633\u062c\u0644 \u062c\u062f\u064a\u062f\u0627\u064b
+search_from_existing=\u0627\u0628\u062d\u062b \u0628\u0645\u0639\u064a\u0627\u0631 \u0645\u062a\u0649 \u062a\u0645 \u0627\u0644\u0627\u0646\u0634\u0627\u0621
name=\u0627\u0633\u0645
-dataentry=\u0627\u062F\u062E\u0627\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A
-custom_form=\u0627\u0633\u062A\u0645\u0627\u0631\u0629 \u0645\u062E\u0635\u0635\u0629
-default_form=\u0627\u0633\u062A\u0645\u0627\u0631\u0629 \u0627\u0641\u062A\u0631\u0627\u0636\u064A\u0629
+dataentry=\u0627\u062f\u062e\u0627\u0644 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a
+custom_form=\u0627\u0633\u062a\u0645\u0627\u0631\u0629 \u0645\u062e\u0635\u0635\u0629
+default_form=\u0627\u0633\u062a\u0645\u0627\u0631\u0629 \u0627\u0641\u062a\u0631\u0627\u0636\u064a\u0629
menu=\u0642\u0627\u0626\u0645\u0629
-tracker_reports=\u062A\u0642\u0627\u0631\u064A\u0631 \u0645\u064F\u062A\u064E\u062A\u064E\u0628\u0650\u0639\u064E\u0629
-reports=\u062A\u0642\u0627\u0631\u064A\u0631
-registration_and_data_entry=\u0627\u0644\u062A\u0633\u062C\u064A\u0644 \u0648 \u0627\u062F\u062E\u0627\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A
-program_summary_report=\u062A\u0642\u0631\u064A\u0631 \u0645\u0644\u062E\u0635 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C
-program_summary=\u0645\u0644\u062E\u0635 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C
-program_summary_description=\u0627\u0635\u062F\u0631 \u062A\u0642\u0631\u064A\u0631\u0627\u064B \u0645\u0644\u062E\u0635\u0627\u064B \u0644\u0648\u062D\u062F\u0629 \u062A\u0646\u0638\u064A\u0645 \u0628\u0631\u0646\u0627\u0645\u062C \u0645\u062E\u0635\u0635\u0629 \u0648 \u0627\u0637\u0627\u0631\u0627\u064B \u0632\u0645\u0646\u064A\u0627\u064B . \u064A\u0642\u062F\u0645 \u0627\u0644\u062A\u0642\u0631\u064A\u0631 \u0644\u0627\u0626\u062D\u0629\u064B \u0628\u0623\u0645\u062B\u0644\u0629 \u0627\u0644\u0643\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0645\u062A\u0628\u0639\u0629 \u0628\u0627\u0644\u0627\u0636\u0627\u0641\u0629 \u0627\u0644\u0649 \u062E\u064A\u0627\u0631 \u0627\u0644\u062A\u0639\u0645\u0642 \u0648 \u0631\u0624\u064A\u0629 \u0627\u0644\u0642\u064A\u0645 \u0627\u0644\u0641\u0639\u0644\u064A\u0629
-program_statistics_report=\u062A\u0642\u0631\u064A\u0631 \u0627\u062D\u0635\u0627\u0626\u064A\u0627\u062A \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C
-program_statistics=\u0627\u062D\u0635\u0627\u0626\u064A\u0627\u062A \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C
-program_statistics_description= \u0627\u0635\u062F\u0631 \u062A\u0642\u0631\u064A\u0631\u0627\u064B\u0627\u062D\u0635\u0627\u0626\u064A\u0627\u064B \u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0645\u0639\u064A\u0646 . \u064A\u062D\u0648\u064A \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0639\u0644\u0649 \u0633\u0628\u064A\u0644 \u0627\u0644\u0645\u062B\u0627\u0644 \u0648\u0635\u0641\u0627\u064B \u0639\u0627\u0645\u0627\u064B \u0639\u0646 \u0627\u0644\u0627\u0646\u0642\u0637\u0627\u0639\u0627\u062A \u0627\u0648 \u0646\u0633\u0628 \u0627\u0644\u0627\u0643\u0645\u0627\u0644 \u0641\u064A \u0627\u0637\u0627\u0631 \u0632\u0645\u0646\u064A \u0645\u0623\u062E\u0648\u0630 \u0628\u0639\u064A\u0646 \u0627\u0644\u0627\u0639\u062A\u0628\u0627\u0631 \u0641\u064A \u0648\u062D\u062F\u0629 \u062A\u0646\u0638\u064A\u0645 \u0645\u062E\u0635\u0635\u0629
-upcoming_events=\u0627\u062D\u062F\u0627\u062B \u062A\u0627\u0644\u064A\u0629
-upcoming_events_description=\u0627\u0635\u062F\u0631 \u062A\u0642\u0631\u064A\u0631\u0627\u064B \u062C\u062F\u0648\u0644\u064A\u0627\u064B \u064A\u0638\u0647\u0631 \u0641\u064A\u0647 \u0623\u0645\u062B\u0644\u0629 \u0639\u0646 \u0643\u064A\u0627\u0646\u0627\u062A \u0645\u062A\u0628\u0639\u0629 \u0648 \u0623\u062D\u062F\u0627\u062B\u0647\u0645 \u0627\u0644\u0645\u0631\u062A\u0642\u0628\u0629 \u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0648 \u0648\u0642\u062A \u0645\u062D\u062F\u062F\u064A\u0646 . \u062A\u0635\u0646\u064A\u0641 \u0627\u0644\u0628\u062D\u062B \u0648 \u0627\u0638\u0647\u0627\u0631\\u0627\u062E\u0641\u0627\u0621 \u0627\u0644\u0639\u0645\u0644\u064A\u0627\u062A \u0645\u0645\u0643\u0646\u0627\u062A \u0641\u064A \u0627\u0644\u0623\u0639\u0645\u062F\u0629
-overdue_events=\u0627\u062D\u062F\u0627\u062B \u063A\u064A\u0631 \u0645\u0646\u062C\u0632\u0629
-overdue_events_description=\u0627\u0635\u062F\u0631 \u0644\u0627\u0626\u062D\u0629 \u0645\u0646 \u0623\u062D\u062F\u0627\u062B \u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0645\u062E\u062A\u0627\u0631 . \u064A\u0638\u0647\u0631 \u0627\u0644\u062A\u0642\u0631\u064A\u0631 \u0642\u0627\u0626\u0645\u0629\u064B \u0645\u0646 \u0623\u0645\u062B\u0644\u0629 \u0627\u0644\u0643\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0645\u062A\u0628\u0639\u0629 \u0648 \u0623\u062D\u062F\u0627\u062B\u0647\u0645 \u0627\u0644\u062A\u064A \u0644\u0645 \u062A\u062A\u0645 \u0641\u064A \u0648\u0642\u062A\u0647\u0627 . \u0627\u0644\u062A\u0635\u0641\u064A\u0629 \u0648 \u0627\u0644\u0628\u062D\u062B \u0623\u064A\u0636\u0627\u064B \u0645\u0645\u0643\u0646\u0627\u0646 .
-enrollment_stats=\u0627\u062D\u0635\u0627\u0626\u064A\u0627\u062A \u0627\u0644\u062A\u0633\u062C\u064A\u0644
-event_stats=\u0627\u062D\u0635\u0627\u0626\u064A\u0627\u062A \u0627\u0644\u062D\u062F\u062B
-upcoming_event=\u0627\u0644\u062D\u062F\u062B \u0627\u0644\u062A\u0627\u0644\u064A
-legend=\u0634\u0631\u0648\u062D\u0627\u062A
-event_color_legend=\u0645\u0627\u0630\u0627 \u062A\u0639\u0646\u064A \u0627\u0644\u0623\u0644\u0648\u0627\u0646
-completed=\u0645\u0643\u062A\u0645\u0644
-executed=\u0645\u064F\u0646\u064E\u0641\u064E\u0630
-ontime=\u0641\u064A \u0648\u0642\u062A\u0647
-overdue=\u063A\u064A\u0631 \u0645\u0646\u062C\u0632
-skipped=\u0645\u062A\u062E\u0637\u0649
-report=\u062A\u0642\u0631\u064A\u0631
-_report=\u062A\u0642\u0631\u064A\u0631
-visit_schedule=\u062C\u062F\u0648\u0644 \u0632\u064A\u0627\u0631\u0629
-mark_for_followup=\u0627\u0634\u0631 \u0645\u0646 \u0623\u062C\u0644 \u0627\u0644\u0645\u0644\u062D\u0642
-unmark_for_followup=\u0627\u062D\u0630\u0641 \u0645\u0646 \u0627\u0644\u0645\u0644\u062D\u0642
-only_marked_for_followup=\u0641\u0642\u0637 \u0627\u0634\u0650\u0631 \u0639\u0644\u064A\u0647 \u0645\u0646 \u0623\u062C\u0644 \u0627\u0644\u0645\u0644\u062D\u0642
-registered_data=\u0642\u064A\u0645\u0629 \u0645\u0633\u062C\u0644\u0629
-no_value=\u0644\u0627 \u0642\u064A\u0645\u0629
-no_data_found=\u0644\u0645 \u062A\u0648\u062C\u062F \u0628\u064A\u0627\u0646\u0627\u062A
-no_data=\u0644\u0627 \u062A\u0648\u062C\u062F \u0628\u064A\u0627\u0646\u0627\u062A
-data_exists=\u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0645\u062A\u0648\u0627\u062C\u062F\u0629
-data_registerd=\u0644\u0627 \u062A\u0648\u062C\u062F \u0628\u064A\u0627\u0646\u0627\u062A \u0645\u0633\u062C\u0644\u0629
-no_visit_made=\u0644\u0627 \u062A\u0648\u062C\u062F \u0632\u064A\u0627\u0631\u0627\u062A \u062A\u0627\u0645\u0629
-visit_not_made=\u0644\u0645 \u062A\u062A\u0645 \u0627\u0644\u0632\u064A\u0627\u0631\u0629
-_visit=\u0632\u064C\u0631
-visit=\u0632\u064A\u0627\u0631\u0629
-visits=\u0632\u064A\u0627\u0631\u0627\u062A
-current_selections=\u0627\u0644\u0627\u062E\u062A\u064A\u0627\u0631\u0627\u062A \u0627\u0644\u062D\u0627\u0644\u064A\u0629
-org_unit=\u0648\u062D\u062F\u0629 \u0627\u0644\u062A\u0646\u0638\u064A\u0645
-org_unit_scope=\u0645\u062F\u0649 \u0648\u062D\u062F\u0629 \u0627\u0644\u062A\u0646\u0638\u064A\u0645
-SELECTED=\u0645\u062E\u062A\u0627\u0631\\u0629
-CHILDREN=\u0627\u0644\u0623\u0637\u0641\u0627\u0644 \u0627\u0644\u0645\u062A\u0648\u0633\u0637\u0648\u0646
+tracker_reports=\u062a\u0642\u0627\u0631\u064a\u0631 \u0645\u064f\u062a\u064e\u062a\u064e\u0628\u0650\u0639\u064e\u0629
+reports=\u062a\u0642\u0627\u0631\u064a\u0631
+registration_and_data_entry=\u0627\u0644\u062a\u0633\u062c\u064a\u0644 \u0648 \u0627\u062f\u062e\u0627\u0644 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a
+program_summary_report=\u062a\u0642\u0631\u064a\u0631 \u0645\u0644\u062e\u0635 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062c
+program_summary=\u0645\u0644\u062e\u0635 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062c
+program_summary_description=\u0627\u0635\u062f\u0631 \u062a\u0642\u0631\u064a\u0631\u0627\u064b \u0645\u0644\u062e\u0635\u0627\u064b \u0644\u0648\u062d\u062f\u0629 \u062a\u0646\u0638\u064a\u0645 \u0628\u0631\u0646\u0627\u0645\u062c \u0645\u062e\u0635\u0635\u0629 \u0648 \u0627\u0637\u0627\u0631\u0627\u064b \u0632\u0645\u0646\u064a\u0627\u064b . \u064a\u0642\u062f\u0645 \u0627\u0644\u062a\u0642\u0631\u064a\u0631 \u0644\u0627\u0626\u062d\u0629\u064b \u0628\u0623\u0645\u062b\u0644\u0629 \u0627\u0644\u0643\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0645\u062a\u0628\u0639\u0629 \u0628\u0627\u0644\u0627\u0636\u0627\u0641\u0629 \u0627\u0644\u0649 \u062e\u064a\u0627\u0631 \u0627\u0644\u062a\u0639\u0645\u0642 \u0648 \u0631\u0624\u064a\u0629 \u0627\u0644\u0642\u064a\u0645 \u0627\u0644\u0641\u0639\u0644\u064a\u0629
+program_statistics_report=\u062a\u0642\u0631\u064a\u0631 \u0627\u062d\u0635\u0627\u0626\u064a\u0627\u062a \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062c
+program_statistics=\u0627\u062d\u0635\u0627\u0626\u064a\u0627\u062a \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062c
+program_statistics_description= \u0627\u0635\u062f\u0631 \u062a\u0642\u0631\u064a\u0631\u0627\u064b\u0627\u062d\u0635\u0627\u0626\u064a\u0627\u064b \u0644\u0628\u0631\u0646\u0627\u0645\u062c \u0645\u0639\u064a\u0646 . \u064a\u062d\u0648\u064a \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062c \u0639\u0644\u0649 \u0633\u0628\u064a\u0644 \u0627\u0644\u0645\u062b\u0627\u0644 \u0648\u0635\u0641\u0627\u064b \u0639\u0627\u0645\u0627\u064b \u0639\u0646 \u0627\u0644\u0627\u0646\u0642\u0637\u0627\u0639\u0627\u062a \u0627\u0648 \u0646\u0633\u0628 \u0627\u0644\u0627\u0643\u0645\u0627\u0644 \u0641\u064a \u0627\u0637\u0627\u0631 \u0632\u0645\u0646\u064a \u0645\u0623\u062e\u0648\u0630 \u0628\u0639\u064a\u0646 \u0627\u0644\u0627\u0639\u062a\u0628\u0627\u0631 \u0641\u064a \u0648\u062d\u062f\u0629 \u062a\u0646\u0638\u064a\u0645 \u0645\u062e\u0635\u0635\u0629
+upcoming_events=\u0627\u062d\u062f\u0627\u062b \u062a\u0627\u0644\u064a\u0629
+upcoming_events_description=\u0627\u0635\u062f\u0631 \u062a\u0642\u0631\u064a\u0631\u0627\u064b \u062c\u062f\u0648\u0644\u064a\u0627\u064b \u064a\u0638\u0647\u0631 \u0641\u064a\u0647 \u0623\u0645\u062b\u0644\u0629 \u0639\u0646 \u0643\u064a\u0627\u0646\u0627\u062a \u0645\u062a\u0628\u0639\u0629 \u0648 \u0623\u062d\u062f\u0627\u062b\u0647\u0645 \u0627\u0644\u0645\u0631\u062a\u0642\u0628\u0629 \u0644\u0628\u0631\u0646\u0627\u0645\u062c \u0648 \u0648\u0642\u062a \u0645\u062d\u062f\u062f\u064a\u0646 . \u062a\u0635\u0646\u064a\u0641 \u0627\u0644\u0628\u062d\u062b \u0648 \u0627\u0638\u0647\u0627\u0631\\u0627\u062e\u0641\u0627\u0621 \u0627\u0644\u0639\u0645\u0644\u064a\u0627\u062a \u0645\u0645\u0643\u0646\u0627\u062a \u0641\u064a \u0627\u0644\u0623\u0639\u0645\u062f\u0629
+overdue_events=\u0627\u062d\u062f\u0627\u062b \u063a\u064a\u0631 \u0645\u0646\u062c\u0632\u0629
+overdue_events_description=\u0627\u0635\u062f\u0631 \u0644\u0627\u0626\u062d\u0629 \u0645\u0646 \u0623\u062d\u062f\u0627\u062b \u0644\u0628\u0631\u0646\u0627\u0645\u062c \u0645\u062e\u062a\u0627\u0631 . \u064a\u0638\u0647\u0631 \u0627\u0644\u062a\u0642\u0631\u064a\u0631 \u0642\u0627\u0626\u0645\u0629\u064b \u0645\u0646 \u0623\u0645\u062b\u0644\u0629 \u0627\u0644\u0643\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0645\u062a\u0628\u0639\u0629 \u0648 \u0623\u062d\u062f\u0627\u062b\u0647\u0645 \u0627\u0644\u062a\u064a \u0644\u0645 \u062a\u062a\u0645 \u0641\u064a \u0648\u0642\u062a\u0647\u0627 . \u0627\u0644\u062a\u0635\u0641\u064a\u0629 \u0648 \u0627\u0644\u0628\u062d\u062b \u0623\u064a\u0636\u0627\u064b \u0645\u0645\u0643\u0646\u0627\u0646 .
+enrollment_stats=\u0627\u062d\u0635\u0627\u0626\u064a\u0627\u062a \u0627\u0644\u062a\u0633\u062c\u064a\u0644
+event_stats=\u0627\u062d\u0635\u0627\u0626\u064a\u0627\u062a \u0627\u0644\u062d\u062f\u062b
+upcoming_event=\u0627\u0644\u062d\u062f\u062b \u0627\u0644\u062a\u0627\u0644\u064a
+legend=\u0634\u0631\u0648\u062d\u0627\u062a
+event_color_legend=\u0645\u0627\u0630\u0627 \u062a\u0639\u0646\u064a \u0627\u0644\u0623\u0644\u0648\u0627\u0646
+completed=\u0645\u0643\u062a\u0645\u0644
+executed=\u0645\u064f\u0646\u064e\u0641\u064e\u0630
+ontime=\u0641\u064a \u0648\u0642\u062a\u0647
+overdue=\u063a\u064a\u0631 \u0645\u0646\u062c\u0632
+skipped=\u0645\u062a\u062e\u0637\u0649
+report=\u062a\u0642\u0631\u064a\u0631
+_report=\u062a\u0642\u0631\u064a\u0631
+visit_schedule=\u062c\u062f\u0648\u0644 \u0632\u064a\u0627\u0631\u0629
+mark_for_followup=\u0627\u0634\u0631 \u0645\u0646 \u0623\u062c\u0644 \u0627\u0644\u0645\u0644\u062d\u0642
+unmark_for_followup=\u0627\u062d\u0630\u0641 \u0645\u0646 \u0627\u0644\u0645\u0644\u062d\u0642
+only_marked_for_followup=\u0641\u0642\u0637 \u0627\u0634\u0650\u0631 \u0639\u0644\u064a\u0647 \u0645\u0646 \u0623\u062c\u0644 \u0627\u0644\u0645\u0644\u062d\u0642
+registered_data=\u0642\u064a\u0645\u0629 \u0645\u0633\u062c\u0644\u0629
+no_value=\u0644\u0627 \u0642\u064a\u0645\u0629
+no_data_found=\u0644\u0645 \u062a\u0648\u062c\u062f \u0628\u064a\u0627\u0646\u0627\u062a
+no_data=\u0644\u0627 \u062a\u0648\u062c\u062f \u0628\u064a\u0627\u0646\u0627\u062a
+data_exists=\u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0645\u062a\u0648\u0627\u062c\u062f\u0629
+data_registerd=\u0644\u0627 \u062a\u0648\u062c\u062f \u0628\u064a\u0627\u0646\u0627\u062a \u0645\u0633\u062c\u0644\u0629
+no_visit_made=\u0644\u0627 \u062a\u0648\u062c\u062f \u0632\u064a\u0627\u0631\u0627\u062a \u062a\u0627\u0645\u0629
+visit_not_made=\u0644\u0645 \u062a\u062a\u0645 \u0627\u0644\u0632\u064a\u0627\u0631\u0629
+_visit=\u0632\u064c\u0631
+visit=\u0632\u064a\u0627\u0631\u0629
+visits=\u0632\u064a\u0627\u0631\u0627\u062a
+current_selections=\u0627\u0644\u0627\u062e\u062a\u064a\u0627\u0631\u0627\u062a \u0627\u0644\u062d\u0627\u0644\u064a\u0629
+org_unit=\u0648\u062d\u062f\u0629 \u0627\u0644\u062a\u0646\u0638\u064a\u0645
+org_unit_scope=\u0645\u062f\u0649 \u0648\u062d\u062f\u0629 \u0627\u0644\u062a\u0646\u0638\u064a\u0645
+SELECTED=\u0645\u062e\u062a\u0627\u0631\\u0629
+CHILDREN=\u0627\u0644\u0623\u0637\u0641\u0627\u0644 \u0627\u0644\u0645\u062a\u0648\u0633\u0637\u0648\u0646
DESCENDANTS=\u0643\u0644 \u0627\u0644\u0623\u0637\u0641\u0627\u0644
-ACCESSIBLE=\u0627\u0644\u0643\u0644 \u064A\u0645\u0643\u0646 \u0627\u0644\u0648\u0635\u0648\u0644 \u0627\u0644\u064A\u0647
-data_element=\u0639\u0646\u0635\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A
-value=\u0642\u064A\u0645\u0629
-provided_elsewhere=\u0645\u0632\u0648\u062F\u0629 \u0628\u0645\u0643\u0627\u0646 \u0622\u062E\u0631
-expand=\u0648\u0633\u0650\u0639\\u0645\u062F\u0650\u062F
-collapse=\u0627\u0646\u0647\u064A\u0627\u0631
-show_hide_columns=\u0627\u0638\u0647\u0631\\u0627\u062E\u0641\u0650 \u0623\u0639\u0645\u062F\u0629
-select_columns_to_show=\u0627\u062E\u062A\u0631 \u0623\u0639\u0645\u062F\u0629 \u0644\u064A\u062A\u0645 \u0627\u0638\u0647\u0627\u0631\u0647\u0627
-show_hide_widgets=\u0627\u0638\u0647\u0631\\u0627\u062E\u0641\u0650 \u0627\u0644\u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0627\u0644\u0645\u0635\u063A\u0631\u0629
-select_widgets_to_show=\u0627\u062E\u062A\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0627\u0644\u0645\u0635\u063A\u0631\u0629 \u0644\u064A\u062A\u0645 \u0625\u0638\u0647\u0627\u0631\u0647\u0627
-close=\u0627\u063A\u0644\u0627\u0642
+ACCESSIBLE=\u0627\u0644\u0643\u0644 \u064a\u0645\u0643\u0646 \u0627\u0644\u0648\u0635\u0648\u0644 \u0627\u0644\u064a\u0647
+data_element=\u0639\u0646\u0635\u0631 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a
+value=\u0642\u064a\u0645\u0629
+provided_elsewhere=\u0645\u0632\u0648\u062f\u0629 \u0628\u0645\u0643\u0627\u0646 \u0622\u062e\u0631
+expand=\u0648\u0633\u0650\u0639\\u0645\u062f\u0650\u062f
+collapse=\u0627\u0646\u0647\u064a\u0627\u0631
+show_hide_columns=\u0627\u0638\u0647\u0631\\u0627\u062e\u0641\u0650 \u0623\u0639\u0645\u062f\u0629
+select_columns_to_show=\u0627\u062e\u062a\u0631 \u0623\u0639\u0645\u062f\u0629 \u0644\u064a\u062a\u0645 \u0627\u0638\u0647\u0627\u0631\u0647\u0627
+show_hide_widgets=\u0627\u0638\u0647\u0631\\u0627\u062e\u0641\u0650 \u0627\u0644\u062a\u0637\u0628\u064a\u0642\u0627\u062a \u0627\u0644\u0645\u0635\u063a\u0631\u0629
+select_widgets_to_show=\u0627\u062e\u062a\u0631 \u0627\u0644\u062a\u0637\u0628\u064a\u0642\u0627\u062a \u0627\u0644\u0645\u0635\u063a\u0631\u0629 \u0644\u064a\u062a\u0645 \u0625\u0638\u0647\u0627\u0631\u0647\u0627
+close=\u0627\u063a\u0644\u0627\u0642
generate=\u0627\u0646\u0634\u0627\u0621
print=\u0637\u0628\u0627\u0639\u0629
-excel_export=\u062A\u0635\u062F\u064A\u0631 \u0627\u0643\u0633\u0644
-list_programs=\u0627\u062F\u0631\u062C \u0627\u0644\u0628\u0631\u0627\u0645\u062C
-program_stage=\u0645\u0631\u062D\u0644\u0629 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C
-due_date=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0627\u0633\u062A\u062D\u0642\u0627\u0642
-event_date=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062D\u062F\u062B
-enable_rescheduling=\u062A\u0645\u0643\u064A\u0646 \u0627\u0639\u0627\u062F\u0629 \u0627\u0644\u062C\u062F\u0648\u0644\u0629
-disable_rescheduling=\u0639\u062F\u0645 \u062A\u0645\u0643\u064A\u0646 \u0627\u0639\u0627\u062F\u0629 \u0627\u0644\u062C\u062F\u0648\u0644\u0629
-show_hide_scheduling=\u0627\u0638\u0647\u0631\\u0627\u062E\u0641\u0650 \u0627\u0633\u062A\u0645\u0627\u0631\u0629 \u0627\u0644\u062C\u062F\u0648\u0644\u0629
-show_hide_messaging=\u0627\u0638\u0647\u0631\\u0627\u062E\u0641\u0650 \u0627\u0633\u062A\u0645\u0627\u0631\u0629 \u0627\u0644\u0631\u0633\u0627\u0626\u0644
+excel_export=\u062a\u0635\u062f\u064a\u0631 \u0627\u0643\u0633\u0644
+list_programs=\u0627\u062f\u0631\u062c \u0627\u0644\u0628\u0631\u0627\u0645\u062c
+program_stage=\u0645\u0631\u062d\u0644\u0629 \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062c
+due_date=\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0627\u0633\u062a\u062d\u0642\u0627\u0642
+event_date=\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062d\u062f\u062b
+enable_rescheduling=\u062a\u0645\u0643\u064a\u0646 \u0627\u0639\u0627\u062f\u0629 \u0627\u0644\u062c\u062f\u0648\u0644\u0629
+disable_rescheduling=\u0639\u062f\u0645 \u062a\u0645\u0643\u064a\u0646 \u0627\u0639\u0627\u062f\u0629 \u0627\u0644\u062c\u062f\u0648\u0644\u0629
+show_hide_scheduling=\u0627\u0638\u0647\u0631\\u0627\u062e\u0641\u0650 \u0627\u0633\u062a\u0645\u0627\u0631\u0629 \u0627\u0644\u062c\u062f\u0648\u0644\u0629
+show_hide_messaging=\u0627\u0638\u0647\u0631\\u0627\u062e\u0641\u0650 \u0627\u0633\u062a\u0645\u0627\u0631\u0629 \u0627\u0644\u0631\u0633\u0627\u0626\u0644
messaging=\u0631\u0633\u0627\u0626\u0644
-scheduling=\u062C\u062F\u0648\u0644\u0629
-reschedule=\u0627\u0639\u0627\u062F\u0629 \u062C\u062F\u0648\u0644\u0629
-rescheduling=\u0627\u0639\u062F \u062C\u062F\u0648\u0644\u0629
-scheduling_messaging=\u0627\u0644\u062C\u062F\u0648\u0644\u0629 \u0648 \u0627\u0644\u0631\u0633\u0627\u0626\u0644
-not_yet_enrolled_scheduling=\u063A\u064A\u0631 \u0645\u0633\u062C\u0644 \u0627\u0644\u0649 \u0627\u0644\u0644\u0622\u0646 . \u0627\u0644\u062C\u062F\u0648\u0644\u0629 \u063A\u064A\u0631 \u0645\u0645\u0643\u0646\u0629
-schedule_details=\u062A\u0641\u0627\u0635\u064A\u0644 \u0627\u0644\u062C\u062F\u0648\u0644
-no_event_to_schedule=\u0644\u0627 \u062A\u0648\u062C\u062F \u0623\u062D\u062F\u0627\u062B \u0644\u062C\u062F\u0648\u0644\u062A\u0647\u0627
-enroll=\u0633\u062C\u064D\u0644
-new_enrollment=\u062A\u0633\u062C\u0628\u0644 \u062C\u062F\u064A\u062F
-start_date=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0633\u062C\u064A\u0644
-end_date=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0627\u0646\u062A\u0647\u0627\u0621
+scheduling=\u062c\u062f\u0648\u0644\u0629
+reschedule=\u0627\u0639\u0627\u062f\u0629 \u062c\u062f\u0648\u0644\u0629
+rescheduling=\u0627\u0639\u062f \u062c\u062f\u0648\u0644\u0629
+scheduling_messaging=\u0627\u0644\u062c\u062f\u0648\u0644\u0629 \u0648 \u0627\u0644\u0631\u0633\u0627\u0626\u0644
+not_yet_enrolled_scheduling=\u063a\u064a\u0631 \u0645\u0633\u062c\u0644 \u0627\u0644\u0649 \u0627\u0644\u0644\u0622\u0646 . \u0627\u0644\u062c\u062f\u0648\u0644\u0629 \u063a\u064a\u0631 \u0645\u0645\u0643\u0646\u0629
+schedule_details=\u062a\u0641\u0627\u0635\u064a\u0644 \u0627\u0644\u062c\u062f\u0648\u0644
+no_event_to_schedule=\u0644\u0627 \u062a\u0648\u062c\u062f \u0623\u062d\u062f\u0627\u062b \u0644\u062c\u062f\u0648\u0644\u062a\u0647\u0627
+enroll=\u0633\u062c\u064d\u0644
+new_enrollment=\u062a\u0633\u062c\u0628\u0644 \u062c\u062f\u064a\u062f
+start_date=\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0633\u062c\u064a\u0644
+end_date=\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0627\u0646\u062a\u0647\u0627\u0621
from=\u0645\u0646
to=\u0627\u0644\u0649
-exact_date=\u0627\u0644\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062F\u0642\u064A\u0642
-exact_value=\u0627\u0644\u0642\u064A\u0645\u0629 \u0627\u0644\u062F\u0642\u064A\u0642\u0629
-EQ=\u0645\u062A\u0633\u0627\u0648\u064A\u0627\u0646 \ \u0645\u062A\u0633\u0627\u0648\u064A\u0648\u0646
-GT=\u0623\u0643\u062B\u0631 \u0645\u0646
-GE=\u0623\u0643\u062B\u0631 \u0645\u0646 \u0627\u0644\u0645\u0633\u0627\u0648\u064A
+exact_date=\u0627\u0644\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062f\u0642\u064a\u0642
+exact_value=\u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u062f\u0642\u064a\u0642\u0629
+EQ=\u0645\u062a\u0633\u0627\u0648\u064a\u0627\u0646 \ \u0645\u062a\u0633\u0627\u0648\u064a\u0648\u0646
+GT=\u0623\u0643\u062b\u0631 \u0645\u0646
+GE=\u0623\u0643\u062b\u0631 \u0645\u0646 \u0627\u0644\u0645\u0633\u0627\u0648\u064a
LT=\u0623\u0642\u0644 \u0645\u0646
-LE=\u0623\u0642\u0644 \u0645\u0646 \u0627\u0644\u0645\u0633\u0627\u0648\u064A
-NE=\u063A\u064A\u0631 \u0645\u0633\u0627\u0648\u064D
-IS=\u064A\u0643\u0648\u0646
+LE=\u0623\u0642\u0644 \u0645\u0646 \u0627\u0644\u0645\u0633\u0627\u0648\u064a
+NE=\u063a\u064a\u0631 \u0645\u0633\u0627\u0648\u064d
+IS=\u064a\u0643\u0648\u0646
RANGE=\u0646\u0637\u0627\u0642
-like=\u064A\u0634\u0628\u0647
-not_like=\u0644\u0627 \u064A\u0634\u0628\u0647
-boolean=\u0646\u0638\u0627\u0645 \u0627\u0644\u0639\u062F \u0627\u0644\u062B\u0646\u0627\u0626\u064A
+like=\u064a\u0634\u0628\u0647
+not_like=\u0644\u0627 \u064a\u0634\u0628\u0647
+boolean=\u0646\u0638\u0627\u0645 \u0627\u0644\u0639\u062f \u0627\u0644\u062b\u0646\u0627\u0626\u064a
yes=\u0645\u0648\u0627\u0641\u0642
-no=\u063A\u064A\u0631 \u0645\u0648\u0627\u0641\u0642
-records=\u0633\u062C\u0644\u0627\u062A
-record=\u0633\u062C\u0644
-_record=\u0633\u062C\u0650\u0644
-_records=\u0633\u062C\u0644\u0627\u062A
+no=\u063a\u064a\u0631 \u0645\u0648\u0627\u0641\u0642
+records=\u0633\u062c\u0644\u0627\u062a
+record=\u0633\u062c\u0644
+_record=\u0633\u062c\u0650\u0644
+_records=\u0633\u062c\u0644\u0627\u062a
found=\u0623\u0633\u0650\u0633
-move_to_selected=\u0627\u0646\u0642\u0644 \u0644\u0644\u0645\u062E\u062A\u0627\u0631/\u0629
-move_all_to_selected=\u0627\u0646\u0642\u0644 \u0627\u0644\u062C\u0645\u064A\u0639 \u0644\u0644\u0645\u062E\u062A\u0627\u0631/\u0629
-new=\u062C\u062F\u064A\u062F
-add_new=\u0627\u0636\u0641 \u062C\u062F\u064A\u062F\u0627\u064B
-register_new=\u0633\u062C\u0650\u0644 \u062C\u062F\u064A\u062F\u0627\u064B
-registration=\u0627\u0644\u062A\u0633\u062C\u064A\u0644
-registration_date=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0633\u062C\u064A\u0644
-register=\u062A\u0633\u062C\u064A\u0644
-_register=\u0633\u062C\u0650\u0644
-registration_error=\u062E\u0637\u0623 \u0641\u064A \u0627\u0644\u062A\u0633\u062C\u064A\u0644
-update_error=\u062E\u0637\u0627\u064B \u0641\u064A \u0627\u0644\u062A\u062D\u062F\u064A\u062B
-event_creation_error=\u062E\u0637\u0623 \u0641\u064A \u0627\u0646\u0634\u0627\u0621 \u0627\u0644\u062D\u062F\u062B
-relationship_error=\u062E\u0637\u0627\u064B \u0641\u064A \u0645\u0647\u0645\u0629 / \u0648\u0638\u064A\u0641\u0629 \u0627\u0644\u0639\u0644\u0627\u0642\u0629
-event_orgunit_name=\u0648\u062D\u062F\u0629 \u0627\u0644\u062A\u0646\u0638\u064A\u0645
+move_to_selected=\u0627\u0646\u0642\u0644 \u0644\u0644\u0645\u062e\u062a\u0627\u0631/\u0629
+move_all_to_selected=\u0627\u0646\u0642\u0644 \u0627\u0644\u062c\u0645\u064a\u0639 \u0644\u0644\u0645\u062e\u062a\u0627\u0631/\u0629
+new=\u062c\u062f\u064a\u062f
+add_new=\u0627\u0636\u0641 \u062c\u062f\u064a\u062f\u0627\u064b
+register_new=\u0633\u062c\u0650\u0644 \u062c\u062f\u064a\u062f\u0627\u064b
+registration=\u0627\u0644\u062a\u0633\u062c\u064a\u0644
+registration_date=\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0633\u062c\u064a\u0644
+register=\u062a\u0633\u062c\u064a\u0644
+_register=\u0633\u062c\u0650\u0644
+registration_error=\u062e\u0637\u0623 \u0641\u064a \u0627\u0644\u062a\u0633\u062c\u064a\u0644
+update_error=\u062e\u0637\u0627\u064b \u0641\u064a \u0627\u0644\u062a\u062d\u062f\u064a\u062b
+event_creation_error=\u062e\u0637\u0623 \u0641\u064a \u0627\u0646\u0634\u0627\u0621 \u0627\u0644\u062d\u062f\u062b
+relationship_error=\u062e\u0637\u0627\u064b \u0641\u064a \u0645\u0647\u0645\u0629 / \u0648\u0638\u064a\u0641\u0629 \u0627\u0644\u0639\u0644\u0627\u0642\u0629
+event_orgunit_name=\u0648\u062d\u062f\u0629 \u0627\u0644\u062a\u0646\u0638\u064a\u0645
category=\u0641\u0626\u0629
-entity_type=\u0646\u0648\u0639 \u0627\u0644\u0643\u064A\u0627\u0646
-save=\u0627\u062D\u0641\u0638
-save_and_add_new=\u0627\u062D\u0641\u0638 \u0648 \u0627\u0636\u0641 \u062C\u062F\u064A\u062F\u0627\u064B
-save_and_go_back=\u0627\u062D\u0641\u0638 \u0648 \u0627\u0631\u062C\u0639 \u0644\u0644\u062E\u0644\u0641
-save_and_continue=\u0627\u062D\u0641\u0638 \u0648 \u0623\u0643\u0645\u0644
-save_relationship=\u0627\u062D\u0641\u0638 \u0627\u0644\u0639\u0644\u0627\u0642\u0629
-go_back=\u0627\u0631\u062C\u0639 \u0644\u0644\u062E\u0644\u0641
-cancel=\u0627\u0644\u063A\u0650
-are_you_sure_to_delete_enrollment=\u0647\u0644 \u0623\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u062D\u0630\u0641 \u0639\u0645\u0644\u064A\u0629 \u0627\u0644\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u0645\u062E\u062A\u0627\u0631\u0629 \u061F
-are_you_sure_to_complete_enrollment=\u0647\u0644 \u0623\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u0627\u0643\u0645\u0627\u0644 \u0639\u0645\u0644\u064A\u0629 \u0627\u0644\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u0645\u062E\u062A\u0627\u0631\u0629 \u061F
-are_you_sure_to_terminate_enrollment=\u0647\u0644 \u0627\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u0627\u0646\u0647\u0627\u0621 \u0639\u0645\u0644\u064A\u0629 \u0627\u0644\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u0645\u062E\u062A\u0627\u0631\u0629 \u061F
-are_you_sure_to_delete_event=\u0647\u0644 \u0623\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u062D\u0630\u0641 \u0627\u0644\u062D\u062F\u062B \u0627\u0644\u0645\u062E\u062A\u0627\u0631 \u061F
-are_you_sure_to_complete_event=\u0647\u0644 \u0623\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u0627\u0643\u0645\u0627\u0644 \u0627\u0644\u062D\u062F\u062B \u0627\u0644\u0645\u062E\u062A\u0627\u0631 \u061F
-are_you_sure_to_incomplete_event=\u0647\u0644 \u0623\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u0639\u062F\u0645 \u0627\u0643\u0645\u0627\u0644 \u0627\u0644\u062D\u062F\u062B \u0627\u0644\u0645\u062E\u062A\u0627\u0631 \u061F
-are_you_sure_to_skip_event=\u0647\u0644 \u0623\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u062E\u0637\u064A \u0627\u0644\u062D\u062F\u062B \u0627\u0644\u0645\u062E\u062A\u0627\u0631 \u061F
-are_you_sure_to_unskip_event=\u0647\u0644 \u0623\u0646\u062A \u0645\u062A\u0623\u0643\u062F \u0645\u0646 \u0623\u0646\u0643 \u062A\u0631\u063A\u0628 \u0641\u064A \u062C\u062F\u0648\u0644\u0629 \u0627\u0644\u062D\u062F\u062B \u0627\u0644\u0645\u062E\u062A\u0627\u0631 \u0644\u0644\u0633\u0627\u0628\u0642 \u061F
-more=\u0645\u0632\u064A\u062F
-under_construction=\u0645\u0627\u0632\u0627\u0644 \u0642\u064A\u062F \u0627\u0644\u0627\u0646\u0634\u0627\u0621
-advanced_search=\u0628\u062D\u062B \u0645\u062A\u0642\u062F\u0645
-profile=\u062A\u0634\u0643\u064A\u0644 \u062C\u0627\u0646\u0628\u064A
-applications=\u0628\u0631\u0627\u0645\u062C
-more_applications=\u0645\u0632\u064A\u062F \u0645\u0646 \u0627\u0644\u0628\u0631\u0627\u0645\u062C
+entity_type=\u0646\u0648\u0639 \u0627\u0644\u0643\u064a\u0627\u0646
+save=\u0627\u062d\u0641\u0638
+save_and_add_new=\u0627\u062d\u0641\u0638 \u0648 \u0627\u0636\u0641 \u062c\u062f\u064a\u062f\u0627\u064b
+save_and_go_back=\u0627\u062d\u0641\u0638 \u0648 \u0627\u0631\u062c\u0639 \u0644\u0644\u062e\u0644\u0641
+save_and_continue=\u0627\u062d\u0641\u0638 \u0648 \u0623\u0643\u0645\u0644
+save_relationship=\u0627\u062d\u0641\u0638 \u0627\u0644\u0639\u0644\u0627\u0642\u0629
+go_back=\u0627\u0631\u062c\u0639 \u0644\u0644\u062e\u0644\u0641
+cancel=\u0627\u0644\u063a\u0650
+are_you_sure_to_delete_enrollment=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u062d\u0630\u0641 \u0639\u0645\u0644\u064a\u0629 \u0627\u0644\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u0645\u062e\u062a\u0627\u0631\u0629 \u061f
+are_you_sure_to_complete_enrollment=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u0627\u0643\u0645\u0627\u0644 \u0639\u0645\u0644\u064a\u0629 \u0627\u0644\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u0645\u062e\u062a\u0627\u0631\u0629 \u061f
+are_you_sure_to_terminate_enrollment=\u0647\u0644 \u0627\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u0627\u0646\u0647\u0627\u0621 \u0639\u0645\u0644\u064a\u0629 \u0627\u0644\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u0645\u062e\u062a\u0627\u0631\u0629 \u061f
+are_you_sure_to_delete_event=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u062d\u0630\u0641 \u0627\u0644\u062d\u062f\u062b \u0627\u0644\u0645\u062e\u062a\u0627\u0631 \u061f
+are_you_sure_to_complete_event=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u0627\u0643\u0645\u0627\u0644 \u0627\u0644\u062d\u062f\u062b \u0627\u0644\u0645\u062e\u062a\u0627\u0631 \u061f
+are_you_sure_to_incomplete_event=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u0639\u062f\u0645 \u0627\u0643\u0645\u0627\u0644 \u0627\u0644\u062d\u062f\u062b \u0627\u0644\u0645\u062e\u062a\u0627\u0631 \u061f
+are_you_sure_to_skip_event=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u062a\u062e\u0637\u064a \u0627\u0644\u062d\u062f\u062b \u0627\u0644\u0645\u062e\u062a\u0627\u0631 \u061f
+are_you_sure_to_unskip_event=\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u062c\u062f\u0648\u0644\u0629 \u0627\u0644\u062d\u062f\u062b \u0627\u0644\u0645\u062e\u062a\u0627\u0631 \u0644\u0644\u0633\u0627\u0628\u0642 \u061f
+more=\u0645\u0632\u064a\u062f
+under_construction=\u0645\u0627\u0632\u0627\u0644 \u0642\u064a\u062f \u0627\u0644\u0627\u0646\u0634\u0627\u0621
+advanced_search=\u0628\u062d\u062b \u0645\u062a\u0642\u062f\u0645
+profile=\u062a\u0634\u0643\u064a\u0644 \u062c\u0627\u0646\u0628\u064a
+applications=\u0628\u0631\u0627\u0645\u062c
+more_applications=\u0645\u0632\u064a\u062f \u0645\u0646 \u0627\u0644\u0628\u0631\u0627\u0645\u062c
settings=\u0636\u0628\u0637
-account=\u062D\u0633\u0627\u0628
-help=\u0645\u0633\u0627\u0639\u062F\u0629
-log_out=\u062A\u0633\u062C\u064A\u0644 \u062E\u0631\u0648\u062C
-about_dhis2=\u062D\u0648\u0644 DHIS 2
+account=\u062d\u0633\u0627\u0628
+help=\u0645\u0633\u0627\u0639\u062f\u0629
+log_out=\u062a\u0633\u062c\u064a\u0644 \u062e\u0631\u0648\u062c
+about_dhis2=\u062d\u0648\u0644 DHIS 2
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html 2015-06-02 12:25:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html 2015-06-10 11:55:41 +0000
@@ -114,6 +114,7 @@
<script type="text/javascript" src="components/relationship/relationship-controller.js"></script>
<script type="text/javascript" src="components/profile/profile-controller.js"></script>
<script type="text/javascript" src="components/notes/notes-controller.js"></script>
+ <script type="text/javascript" src="components/rulebound/rulebound-controller.js"></script>
<!-- Menu scripts -->
<script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.translate.js"></script>
=== 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-10 11:58:22 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js 2015-06-10 13:45:11 +0000
@@ -10,7 +10,7 @@
var store = new dhis2.storage.Store({
name: "dhis2tc",
adapters: [dhis2.storage.IndexedDBAdapter, dhis2.storage.DomSessionStorageAdapter, dhis2.storage.InMemoryAdapter],
- objectStores: ['programs', 'programStages', 'trackedEntities', 'trackedEntityForms', 'attributes', 'relationshipTypes', 'optionSets', 'programValidations', 'ouLevels']
+ objectStores: ['programs', 'programStages', 'trackedEntities', 'trackedEntityForms', 'attributes', 'relationshipTypes', 'optionSets', 'programValidations', 'ouLevels', 'programRuleVariables', 'programRules', 'programRuleActions']
});
return{
currentStore: store
@@ -1065,6 +1065,128 @@
};
})
+ /* Returns a function for getting rules for a specific program */
+.factory('TrackerRulesFactory', function($q,$rootScope,TCStorageService){
+ return{
+ getOldProgramStageRules :function(programUid, programstageUid) {
+ var rules = this.getProgramRules(programUid);
+
+ //Only keep the rules actually matching the program stage we are in, or rules with no program stage defined.
+ var programStageRules = [];
+ angular.forEach(rules, function(rule) {
+ if(rule.programstage_uid == null || rule.programstage_uid == "" || rule.programstage_uid == programstageUid) {
+ programStageRules.push(rule);
+ }
+ });
+
+ return programStageRules;
+ },
+
+ getProgramStageRules : function(programUid, programStageUid){
+ var def = $q.defer();
+
+ TCStorageService.currentStore.open().done(function(){
+ TCStorageService.currentStore.getAll('programRules').done(function(rules){
+ TCStorageService.currentStore.getAll('programRuleActions').done(function(actions){
+ //The hash will serve as a direct-lookup for linking the actions to rules later.
+ var programRulesHash = {};
+ //The array will ultimately be returned to the caller.
+ var programRulesArray = [];
+ //Loop through and add the rules belonging to this program and program stage
+ angular.forEach(rules, function(rule){
+ if(rule.program.id == programUid) {
+ if(!rule.programStage || !rule.programStage.id || rule.programStage.id == programStageUid) {
+ rule.actions = [];
+ programRulesHash[rule.id] = rule;
+ programRulesArray.push(rule);
+ }
+ }
+ });
+
+ //Loop through and attach all actions to the correct rules:
+ angular.forEach(actions, function(action){
+ if(programRulesHash[action.programRule.id])
+ {
+ programRulesHash[action.programRule.id].actions.push(action);
+ }
+ });
+
+ $rootScope.$apply(function(){
+ def.resolve(programRulesArray);
+ });
+ });
+ });
+ });
+
+ return def.promise;
+ }
+ };
+})
+
+/* Returns user defined variable names and their corresponding UIDs and types for a specific program */
+.factory('TrackerRuleVariableFactory', function($rootScope, $q, TCStorageService){
+ return{
+ getProgramRuleVariables : function(programUid){
+ var def = $q.defer();
+
+ TCStorageService.currentStore.open().done(function(){
+
+ TCStorageService.currentStore.getAll('programRuleVariables').done(function(variables){
+
+ //The array will ultimately be returned to the caller.
+ var programRuleVariablesArray = [];
+ //Loop through and add the variables belonging to this program
+ angular.forEach(variables, function(variable){
+ if(variable.program.id == programUid) {
+ programRuleVariablesArray.push(variable);
+ }
+ });
+
+ $rootScope.$apply(function(){
+ def.resolve(programRuleVariablesArray);
+ });
+ });
+ });
+
+ return def.promise;
+ }
+ };
+})
+
+/* Returns user defined variable names and their corresponding UIDs and types for a specific program */
+.factory('TrackerWidgetsConfigurationFactory', function(){
+ return{
+ getWidgetConfiguration : function(programUid){
+ //If no config exists, return default config
+
+ return [
+ {title: 'Details', type: 'rulebound', code:"det", show: true, expand: true, horizontalplacement:"left", index:0},
+ {title: 'enrollment', type:'enrollment', show: false, expand: true, horizontalplacement:"left", index:1},
+ {title: 'dataentry', type: 'dataentry', show: true, expand: true, horizontalplacement:"left", index:2},
+ {title: 'report', type: 'report', show: false, expand: true, horizontalplacement:"left", index:3},
+ {title: 'current_selections', type: 'current_selections', show: false, expand: true, horizontalplacement:"right", index:0},
+ {title: 'profile', type: 'profile', show: false, expand: true, horizontalplacement:"right", index:1},
+ {title: 'Conditions/Complications', type:'rulebound', code:"con", show: true, expand: true, horizontalplacement:"right", index:2},
+ {title: 'relationships', type: 'relationships', show: false, expand: true, horizontalplacement:"right", index:3},
+ {title: 'notes', type: 'notes', show: true, expand: true, horizontalplacement:"right", index:4},
+ {title: 'Summary', type: 'rulebound', code:"sum", show: true, expand: true, horizontalplacement:"left", index:4}
+ ];
+ },
+ getDefaultWidgetConfiguration: function() {
+ return [
+ {title: 'enrollment', type:'enrollment', show: true, expand: true, horizontalplacement:"left", index:0},
+ {title: 'dataentry', type: 'dataentry', show: true, expand: true, horizontalplacement:"left", index:1},
+ {title: 'report', type: 'report', show: true, expand: true, horizontalplacement:"left", index:2},
+ {title: 'current_selections', type: 'current_selections', show: false, expand: true, horizontalplacement:"right", index:0},
+ {title: 'profile', type: 'profile', show: true, expand: true, horizontalplacement:"right", index:1},
+ {title: 'relationships', type: 'relationships', show: true, expand: true, horizontalplacement:"right", index:2},
+ {title: 'notes', type: 'notes', show: true, expand: true, horizontalplacement:"right", index:3}
+ ];
+ }
+ };
+
+})
+
.service('EntityQueryFactory', function(OperatorFactory, DateUtils){
this.getAttributesQuery = function(attributes, enrollment){
@@ -1650,5 +1772,476 @@
}
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($scope) {
+ var thePromisedVariables = $q.defer();
+ var variables = [];
+
+ $scope.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.push({variablename:variablename,
+ variablevalue:variablevalue,
+ variabletype:variabletype,
+ hasValue:variablefound
+ });
+ };
+
+ TrackerRuleVariableFactory.getProgramRuleVariables($scope.currentEvent.program).then(function(programVariables){
+
+ // The following section will need a different implementation for event capture:
+ var allEventsSorted = [];
+ var currentEvent = $scope.currentEvent;
+ var eventsSortedPerProgramStage = [];
+
+ for(var key in $scope.eventsByStage){
+ if($scope.eventsByStage.hasOwnProperty(key)){
+ eventsSortedPerProgramStage[key] = [];
+ angular.forEach($scope.eventsByStage[key], function(event){
+ allEventsSorted.push(event);
+ eventsSortedPerProgramStage[key].push(event);
+ });
+ eventsSortedPerProgramStage[key] = orderByFilter(eventsSortedPerProgramStage[key], '-sortingDate').reverse();
+ }
+ }
+ allEventsSorted = orderByFilter(allEventsSorted, '-sortingDate').reverse();
+
+ var allDes = {};
+ 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;
+ $scope.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;
+ $scope.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;
+ $scope.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) {
+ $scope.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($scope.selectedEntity.attributes , function(attribute) {
+ if(!valueFound) {
+ if(attribute.attribute === programVariable.trackedEntityAttribute.id) {
+ valueFound = true;
+ $scope.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;
+ $scope.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 ) {
+ $scope.pushVariable(programVariable.name, "", dataElement.dataElement.type );
+ }
+ else {
+ $log.warn("Variable #{" + programVariable.name + "} is linked to a dataelement that is not part of the program");
+ $scope.pushVariable(programVariable.name, "", "string" );
+ }
+ }
+ else {
+ $scope.pushVariable(programVariable.name, "", "string" );
+ }
+ }
+ });
+
+ //add context variables:
+ //last parameter "valuefound" is always true for event date
+ $scope.pushVariable('eventdate', currentEvent.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($scope) {
+ //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 variablesWithValueHash = {};
+
+ $scope.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]);
+ }
+ else {
+ $log.warn("Expression " + expression + " conains variable " + variablepresent
+ + " - but this variable is not defined." );
+ }
+
+ });
+ }
+ return expression;
+ };
+
+ $scope.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] = $scope.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(variablesWithValueHash[parameters[0]]){
+ 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;
+ };
+
+ $scope.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 = $scope.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 = $scope.runDhisFunctions(expression);
+ answer = eval(dhisfunctionsevaluated);
+ }
+ return answer;
+ };
+
+
+ VariableService.getVariables($scope).then(function(variables){
+ TrackerRulesFactory.getProgramStageRules($scope.selectedProgram.id, $scope.currentStage.id).then(function(rules){
+ //But run rules in priority - lowest number first(priority null is last)
+ rules = orderByFilter(rules, 'priority');
+
+ //Make a variables hash to allow direct lookup:
+ angular.forEach(variables, function(variable) {
+ variablesHash[variable.variablename] = variable.variablevalue;
+ });
+
+ //Make a variables-exists/hasvalue hash to allow direct lookup:
+ angular.forEach(variables, function(variable) {
+ variablesWithValueHash[variable.variablename] = variable.hasValue;
+ });
+
+ 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 = {};
+ }
+
+ 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 = $scope.replaceVariables(expression);
+ }
+ //run expression:
+ ruleEffective = $scope.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[action.id] )){
+ $rootScope.ruleeffects[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 nameWithoutDollarSign = action.data.replace('#{','').replace('}','');
+ if(angular.isDefined(variablesHash[nameWithoutDollarSign]))
+ {
+ //The variable exists, and is replaced with its corresponding value
+ $rootScope.ruleeffects[action.id].data =
+ variablesHash[nameWithoutDollarSign];
+ }
+ 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[action.id].data = $scope.replaceVariables(action.data);
+ //In a scenario where the data contains a complex expression, evaluate the expression to compile(calculate) the result:
+ $rootScope.ruleeffects[action.id].data = $scope.runExpression($rootScope.ruleeffects[action.id].data, action.data, "action:" + action.id);
+ }
+ }
+
+ //Update the rule effectiveness if it changed in this evaluation;
+ if($rootScope.ruleeffects[action.id].ineffect != ruleEffective)
+ {
+ //There is a change in the rule outcome, we need to update the effect object.
+ updatedEffectsExits = true;
+ $rootScope.ruleeffects[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[action.id].action === "ASSIGNVARIABLE" && $rootScope.ruleeffects[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[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] !== $rootScope.ruleeffects[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] = $rootScope.ruleeffects[action.id].data;
+ }
+ }
+ });
+ });
+
+ //Broadcast rules finished if there was any actual changes.
+ if(updatedEffectsExits){
+ $rootScope.$broadcast("ruleeffectsupdated");
+ }
+ }
+
+ return true;
+ });
+ });
+ }
};
});
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js 2015-06-11 09:09:20 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js 2015-06-11 18:44:52 +0000
@@ -29,7 +29,7 @@
dhis2.tc.store = new dhis2.storage.Store({
name: 'dhis2tc',
adapters: [dhis2.storage.IndexedDBAdapter, dhis2.storage.DomSessionStorageAdapter, dhis2.storage.InMemoryAdapter],
- objectStores: ['programs', 'programStages', 'trackedEntities', 'trackedEntityForms', 'attributes', 'relationshipTypes', 'optionSets', 'programValidations', 'ouLevels']
+ objectStores: ['programs', 'programStages', 'trackedEntities', 'trackedEntityForms', 'attributes', 'relationshipTypes', 'optionSets', 'programValidations', 'ouLevels', 'programRuleVariables', 'programRules', 'programRuleActions']
});
(function($) {
@@ -141,6 +141,12 @@
promise = promise.then( getMetaTrackeEntityAttributes );
promise = promise.then( getTrackedEntityAttributes );
promise = promise.then( getOptionSetsForAttributes );
+ promise = promise.then( getMetaProgramRuleVariables );
+ promise = promise.then( getProgramRuleVariables );
+ promise = promise.then( getMetaProgramRules );
+ promise = promise.then( getProgramRules );
+ promise = promise.then( getMetaProgramRuleActions );
+ promise = promise.then( getProgramRuleActions );
promise = promise.then( getMetaProgramValidations );
promise = promise.then( getProgramValidations );
promise = promise.then( getTrackedEntityForms );
@@ -490,6 +496,335 @@
};
}
+function getMetaProgramRuleActions( data )
+{
+ if( !data ){
+ return;
+ }
+
+ var def = $.Deferred();
+
+ var programRuleIds = [];
+ _.each( _.values( data.programRules ), function ( programRule ) {
+ if( programRule.id ) {
+ programRuleIds.push( programRule.id );
+ }
+ });
+
+ $.ajax({
+ url: '../api/programRuleActions.json',
+ type: 'GET',
+ data:'paging=false&fields=id,programRule[id]'
+ }).done( function(response) {
+ var programRuleActions = [];
+ _.each( _.values( response.programRuleActions ), function ( programRuleAction ) {
+ if( programRuleAction &&
+ programRuleAction.id &&
+ programRuleAction.programRule &&
+ programRuleAction.programRule.id &&
+ programRuleIds.indexOf( programRuleAction.programRule.id ) !== -1) {
+
+ programRuleActions.push( programRuleAction );
+ }
+ });
+
+ def.resolve( {programRuleActions: programRuleActions, programs: data.programs} );
+
+ }).fail(function(){
+ def.resolve( null );
+ });
+
+ return def.promise();
+}
+
+function getProgramRuleActions( data )
+{
+ if( !data || !data.programRuleActions ){
+ return;
+ }
+
+ var mainDef = $.Deferred();
+ var mainPromise = mainDef.promise();
+
+ var def = $.Deferred();
+ var promise = def.promise();
+
+ var builder = $.Deferred();
+ var build = builder.promise();
+
+ _.each( _.values( data.programRuleActions ), function ( programRuleAction ) {
+ build = build.then(function() {
+ var d = $.Deferred();
+ var p = d.promise();
+ dhis2.tc.store.get('programRuleActions', programRuleAction.id).done(function(obj) {
+ if(!obj) {
+ promise = promise.then( getProgramRuleAction( programRuleAction.id ) );
+ }
+ d.resolve();
+ });
+
+ return p;
+ });
+ });
+
+ build.done(function() {
+ def.resolve();
+
+ promise = promise.done( function () {
+ mainDef.resolve( data.programs );
+ } );
+ }).fail(function(){
+ mainDef.resolve( null );
+ });
+
+ builder.resolve();
+
+ return mainPromise;
+}
+
+function getProgramRuleAction( id )
+{
+ return function() {
+ return $.ajax( {
+ url: '../api/programRuleActions.json',
+ type: 'GET',
+ data: 'paging=false&filter=id:eq:' + id +'&fields=id,programRule[id],programRuleActionType,dataElement[id],location,content,data,'
+ }).done( function( response ){
+
+ _.each( _.values( response.programRuleActions ), function ( programRuleAction ) {
+
+ if( programRuleAction &&
+ programRuleAction.id &&
+ programRuleAction.programRule &&
+ programRuleAction.programRule.id ) {
+
+ dhis2.tc.store.set( 'programRuleActions', programRuleAction );
+ }
+ });
+ });
+ };
+}
+
+function getMetaProgramRuleVariables( programs )
+{
+ if( !programs ){
+ return;
+ }
+
+ var def = $.Deferred();
+
+ var programIds = [];
+ _.each( _.values( programs ), function ( program ) {
+ if( program.id ) {
+ programIds.push( program.id );
+ }
+ });
+
+ $.ajax({
+ url: '../api/programRuleVariables.json',
+ type: 'GET',
+ data:'paging=false&fields=id,program[id]'
+ }).done( function(response) {
+ var programRuleVariables = [];
+ _.each( _.values( response.programRuleVariables ), function ( programRuleVariable ) {
+ if( programRuleVariable &&
+ programRuleVariable.id &&
+ programRuleVariable.program &&
+ programRuleVariable.program.id &&
+ programIds.indexOf( programRuleVariable.program.id ) !== -1) {
+
+ programRuleVariables.push( programRuleVariable );
+ }
+
+ });
+
+ def.resolve( {programRuleVariables: programRuleVariables, programs: programs} );
+
+ }).fail(function(){
+ def.resolve( null );
+ });
+
+ return def.promise();
+}
+
+function getProgramRuleVariables( data )
+{
+ if( !data || !data.programRuleVariables ){
+ return;
+ }
+
+ var mainDef = $.Deferred();
+ var mainPromise = mainDef.promise();
+
+ var def = $.Deferred();
+ var promise = def.promise();
+
+ var builder = $.Deferred();
+ var build = builder.promise();
+
+ _.each( _.values( data.programRuleVariables ), function ( programRuleVariable ) {
+ build = build.then(function() {
+ var d = $.Deferred();
+ var p = d.promise();
+ dhis2.tc.store.get('programRuleVariables', programRuleVariable.id).done(function(obj) {
+ if(!obj) {
+ promise = promise.then( getProgramRuleVariable( programRuleVariable.id ) );
+ }
+ d.resolve();
+ });
+
+ return p;
+ });
+ });
+
+ build.done(function() {
+ def.resolve();
+
+ promise = promise.done( function () {
+ mainDef.resolve( data.programs );
+ } );
+ }).fail(function(){
+ mainDef.resolve( null );
+ });
+
+ builder.resolve();
+
+ return mainPromise;
+}
+
+function getProgramRuleVariable( id )
+{
+ return function() {
+ return $.ajax( {
+ url: '../api/programRuleVariables.json',
+ type: 'GET',
+ data: 'paging=false&filter=id:eq:' + id +'&fields=id,program[id],name,programRuleVariableSourceType,trackedEntityAttribute[id],dataElement[id],programStage[id]'
+ }).done( function( response ){
+
+ _.each( _.values( response.programRuleVariables ), function ( programRuleVariable ) {
+
+ if( programRuleVariable &&
+ programRuleVariable.id &&
+ programRuleVariable.program &&
+ programRuleVariable.program.id ) {
+
+ dhis2.tc.store.set( 'programRuleVariables', programRuleVariable );
+ }
+ });
+ });
+ };
+}
+
+function getMetaProgramRules( programs )
+{
+ if( !programs ){
+ return;
+ }
+
+ var def = $.Deferred();
+
+ var programIds = [];
+ _.each( _.values( programs ), function ( program ) {
+ if( program.id ) {
+ programIds.push( program.id );
+ }
+ });
+
+ $.ajax({
+ url: '../api/programRules.json',
+ type: 'GET',
+ data:'paging=false&fields=id,program[id]'
+ }).done( function(response) {
+ var programRules = [];
+ _.each( _.values( response.programRules ), function ( programRule ) {
+ if( programRule &&
+ programRule.id &&
+ programRule.program &&
+ programRule.program.id &&
+ programIds.indexOf( programRule.program.id ) !== -1) {
+
+ programRules.push( programRule );
+ }
+
+ });
+
+ def.resolve( {programRules: programRules, programs: programs} );
+
+ }).fail(function(){
+ def.resolve( null );
+ });
+
+ return def.promise();
+}
+
+function getProgramRules( data )
+{
+ if( !data || !data.programRules ){
+ return;
+ }
+
+ var mainDef = $.Deferred();
+ var mainPromise = mainDef.promise();
+
+ var def = $.Deferred();
+ var promise = def.promise();
+
+ var builder = $.Deferred();
+ var build = builder.promise();
+
+ _.each( _.values( data.programRules ), function ( programRule ) {
+ build = build.then(function() {
+ var d = $.Deferred();
+ var p = d.promise();
+ dhis2.tc.store.get('programRules', programRule.id).done(function(obj) {
+ if(!obj) {
+ promise = promise.then( getProgramRule( programRule.id ) );
+ }
+ d.resolve();
+ });
+
+ return p;
+ });
+ });
+
+ build.done(function() {
+ def.resolve();
+
+ promise = promise.done( function () {
+ mainDef.resolve( data );
+ } );
+ }).fail(function(){
+ mainDef.resolve( null );
+ });
+
+ builder.resolve();
+
+ return mainPromise;
+}
+
+function getProgramRule( id )
+{
+ return function() {
+ return $.ajax( {
+ url: '../api/programRules.json',
+ type: 'GET',
+ data: 'paging=false&filter=id:eq:' + id +'&fields=id,name,description,condition,program[id],programstage[id],priority'
+ }).done( function( response ){
+
+ _.each( _.values( response.programRules ), function ( programRule ) {
+
+ if( programRule &&
+ programRule.id &&
+ programRule.program &&
+ programRule.program.id ) {
+
+ dhis2.tc.store.set( 'programRules', programRule );
+ }
+ });
+ });
+ };
+}
+
function getMetaProgramValidations( programs )
{
if( !programs ){
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css 2015-06-02 12:25:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css 2015-06-10 11:55:41 +0000
@@ -384,6 +384,33 @@
margin-left: 5px;
}
+.info-container {
+ display: inline-block;
+ height:60px;
+ opacity: 1.0;
+ white-space: normal;
+ text-align: center;
+ border: 1px solid #aaa;
+ border-radius: 4px;
+ padding:0px;
+ margin-right: 10px
+}
+.info-container-text{
+ font-weight: bold;
+ padding: 5px 10px;
+ margin:0px;
+}
+.info-container-heading {
+ font-weight: normal;
+ padding: 5px 10px;
+ margin:0px;
+ background-color: #f5f5f5;
+ border-bottom: 1px solid transparent;
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
+}
+
+
/*----------------------------------------------------------------------------*/
/* Tracked entity
/*----------------------------------------------------------------------------*/
@@ -426,8 +453,6 @@
.dashboard-widget-container {
height: auto;
- overflow-x:auto;
- overflow-y:auto;
}
.widget-link {