Writing rules
Important Update on Rules Execution
Please be informed that all existing rules stored in the rules table will become obsolete by the end of this year, 2024. This means that starting January 1, 2025, these rules will no longer be executed.
However, any rules added through the App Designer and avni-health-modules will continue to work as expected.
If you have any questions or need assistance with migrating your rules, please contact our support team.
Contents:
Introduction
Rule types
Using service methods in the rules
Using other group/household individuals' information in the rules
Types of rules and their support/availability in Data Entry App
Types of rules and their support/availability in transaction data upload
Introduction:
Rules are just normal JavaScript functions that take some input and returns something. You can use the full power of JavaScript in these functions. We also provide you with some helper libraries that make it easier to write rules. We will introduce you to these libraries in the examples below.
All rule functions get passed an object as a parameter. The parameter object has two properties: 1. imports 2. params. The imports object is used to pass down common libraries. The params object is used to pass rule-specific parameters. In params object, we pass the relevant entity e.g. if a rule is invoked when a program encounter is being performed then we pass the ProgramEncounter object. The entities that we pass are an instance of classes defined in avni-models
Shape of common imports object:
{
rulesConfig: {}, //It exposes everything exported by rules-config library. https://github.com/avniproject/rules-config/blob/master/rules.js.
common: {}, // Library we have for common functions https://github.com/avniproject/avni-client/blob/master/packages/openchs-health-modules/health_modules/common.js
lodash: {}, // lodash library
moment: {}, // momentjs library
motherCalculations: {}, //mother program calculations https://github.com/avniproject/avni-health-modules/blob/master/src/health_modules/mother/calculations.js
log: {} //console.log object
}
The global variable ruleServiceLibraryInterfaceForSharingModules has been deprecated and is not supported anymore. If you are using it then do switch to use of imports object.
Shape of common params object:
{
user, //Current User's UserInfo object
myUserGroups //List of Group objects, to which the User is assigned to
}
User: https://github.com/avniproject/avni-models/blob/master/src/UserInfo.js
Group: https://github.com/avniproject/avni-models/blob/master/src/Groups.js
Entities passed to the rule
All rule receives an entity from the params
object. Depending on the rule type an entity can be one of Individual, ProgramEncounter, ProgramEnrolment, Encounter, or ChecklistItem. The shape of the entity object and the supported methods can be viewed from the above links on each entity.
Rule types
- Enrolment summary rule
- Form element rule
- Form element group rule
- Visit schedule rule
- Decision rule
- Validation rule
- Enrolment eligibility check rule
- Encounter eligibility check rule
- Checklists rule
- Work list updation rule
- Subject summary rule
- Hyperlink menu item rule
- Message rule
- Dashboard Card rule
- Manual Programs Eligibility Check Rule
- Edit Form Rule
1. Enrolment summary rule
- Logical scope = Program Enrolment
- Trigger = Before the opening of a subject dashboard with default program selection. On program change of subject dashboard.
- In designer = Program (Enrolment Summary Rule)
- When to use = Display important information in the subject dashboard for a program
You can use this rule to highlight important information about the program on the Subject Dashboard in table format. It can pull data from all the encounters of enrolment and the enrolment itself. You can use this when the information you want to show is not entered by the user in any of the forms and is also not required for any reporting purposes (hence you wouldn't also generate this data via decision rule).
Shape of params object:
{
summaries: [],
programEnrolment: {}, // ProgramEnrolment model
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
You need to return an array of summary objects from this function.
Shape of the summary object:
{
"name": "name of the summary concept",
"value": <text> | <number> | <date> | <datetime> | <concept list in case of Coded question>
}
Example:
({params, imports}) => {
const summaries = [];
const programEnrolment = params.programEnrolment;
const birthWeight = programEnrolment.findObservationInEntireEnrolment('Birth Weight');
if (birthWeight) {
summaries.push({name: 'Birth Weight', value: birthWeight.getValue()});
}
return summaries;
};
2. Form element rule
- Logical scope = Form Element
- Trigger = Before display of form element in the form wizard and on any change done by the user in on that page
- In designer = Form Element (RULES tab)
- When to use =
- Hide/show a form element
- auto calculate the value of a form element
- reset value of a form element
Shape of params object:
{
entity: {}, //it could be one of Individual, ProgramEncounter, ProgramEnrolment, Encounter and ChecklistItem depending on what type of form is this rule attached to
formElement: {}, //form element to which this rule is attached to
questionGroupIndex,
services,
entityContext,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
This function should return an instance of FormElementStatus to show/hide the element, show validation error, set its value, reset a value, or skip answers.
To reset a value, you can use FormElementStatus._resetIfValueIsNull() method.
You can either use FormElementStatusBuilder or use normal JavaScript to build the return value. FormElementStatusBuilder is a helper class provided by Avni that helps writing rules in a declarative way.
Examples using FormElementStatusBuilder.
'use strict';
({params, imports}) => {
const individual = params.entity;
const formElement = params.formElement;
const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({individual, formElement});
statusBuilder.show().when.valueInRegistration("Number of hywas required").is.greaterThan(0);
return statusBuilder.build();
};
({params, imports}) => {
const programEnrolment = params.entity;
const formElement = params.formElement;
const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({programEnrolment, formElement});
statusBuilder.show().when.valueInEnrolment('Is child getting registered at Birth').containsAnswerConceptName("No");
return statusBuilder.build();//this method returns FormElementStatus object with visibility true if the conditions given above matches
};
({params, imports}) => {
const gravidaBreakup = [
'Number of miscarriages',
'Number of abortions',
'Number of stillbirths',
'Number of child deaths',
'Number of living children'
];
const computeGravida = (programEnrolment) => gravidaBreakup
.map((cn) => programEnrolment.getObservationValue(cn))
.filter(Number.isFinite)
.reduce((a, b) => a + b, 1);
const [formElement, programEnrolment] = params.programEnrolment;
const firstPregnancy = programEnrolment.getObservationReadableValue('Is this your first pregnancy?');
const value = firstPregnancy === 'Yes' ? 1 : firstPregnancy === 'No' ? computeGravida(programEnrolment) : undefined;
return new FormElementStatus(formElement.uuid, true, value);
};
'use strict';
({params, imports}) => {
const programEncounter = params.entity;
const formElement = params.formElement;
const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({programEncounter, formElement});
const value = programEncounter.findLatestObservationInEntireEnrolment('Have you received first dose of TT');
statusBuilder.show().whenItem( value.getReadableValue() == 'No').is.truthy;
return statusBuilder.build();
};
'use strict';
({params, imports}) => {
const encounter = params.entity;
const formElement = params.formElement;
const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({encounter, formElement});
statusBuilder.show().when.valueInEncounter("Are machine start and end hour readings recorded").is.yes;
return statusBuilder.build();
};
//In-order to fetch affiliatedGroups set as part of GroupAffiliation Concept in the same form,
//one needs to access params.entityContext.affiliatedGroups variable.
// Old Rule snippet
// const phulwariName = _.get(_.find(programEnrolment.individual.affiliatedGroups, ({voided}) => !voided), ['groupSubject', 'firstName'], '');
// New Rule snippet
const phulwariName = _.get(_.find(params.entityContext.affiliatedGroups, ({voided}) => !voided), ['groupSubject', 'firstName'], '');
Please note that form element rules are not transitive and cannot depend on the result of another form element's form element rule. The rule logic for a particular element will need to cater to this.
i.e. If rule C on element C depends on value of element B and rule B depends on value of element A, updating A will only update B's value and not C's value.
3. Form element group rule
- Scope = Form Element Group
- Trigger = Before display of form element group to the user (including previous or next)
- In designer = Form Element Group (RULES tab)
- When to use = Hide/show a form element group
Sometimes we want to hide the entire form element group based on some conditions. This can be done using a form element group (FEG) rule. There is a rules tab on each FEG where this type of rule can be written. Note that this rule gets executed before form element rule so if the form element is hidden by this rule then the form element rule will not get executed.
Shape of params object:
{
entity: {}, //it could be one of Individual, ProgramEncounter, ProgramEnrolment, Encounter and ChecklistItem depending on what type of form is this rule attached to
formElementGroup: {}, //form element group to which this rule is attached to
services,
entityContext,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
This function should return an array of FormElementStatus
Example:
({params, imports}) => {
const formElementGroup = params.formElementGroup;
return formElementGroup.formElements.map(({uuid}) => {
return new imports.rulesConfig.FormElementStatus(uuid, false, null);
});
};
4. Visit schedule rule
- Logical scope = Encounter (aka Visit), Subject, or Program Enrolment
- Trigger = On completion of an form wizard before final screen is displayed
- In designer = Form (RULES tab)
- When to use = For scheduling one or more encounters in the future
Shape of params object:
{
entity: {}, //it could be one of ProgramEncounter, ProgramEnrolment, Encounter depending on what type of form is this rule attached to.
visitSchedule: []// Array of already scheduled visits.
entityContext
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
You need to return an array of visit schedules from this function.
Shape of the return value
[
<visit schedule object>
...
]
visit schedule object
{
name: "visit name",
encounterType: "encounter type name",
earliestDate: <date>,
maxDate: <date>,
visitCreationStrategy: "Optional. One of default|createNew",
programEnrolment: "<Optional. Used if you want to create a visit in a different program enrolment. If the program enrolment is tied to another subject, the visit will be schedule for that subject. Do not pass this parameter if you want to schedule a general encounter.>",
subjectUUID: "<Optional UUID string. Used if you want to create a general visit for another subject.>"
}
Example
({ params, imports }) => {
const programEnrolment = params.entity;
const scheduleBuilder = new imports.rulesConfig.VisitScheduleBuilder({
programEnrolment
});
scheduleBuilder
.add({
name: "First Birth Registration Visit",
encounterType: "Birth Registration",
earliestDate: programEnrolment.enrolmentDateTime,
maxDate: programEnrolment.enrolmentDateTime
})
.whenItem(programEnrolment.getEncounters(true).length)
.equals(0);
return scheduleBuilder.getAll();
};
Example 2 - Schedule a general visit on a household when a member completes a program enrolment
.
.
scheduleBuilder.add({
name: "TB Family Screening Form",
encounterType: "TB Family Screening Form",
earliestDate: imports.moment(programEnrolment.encounterDateTime).toDate(),
maxDate: imports.moment(programEnrolment.encounterDateTime).add(15, 'days').toDate(),
subjectUUID: programEnrolment.individual.groups[0].groupSubject.uuid
});
.
.
Strategies that Avni uses.
For all the visit schedules that are returned, Avni evaluates how to create a visit. Assume you provide the default visitCreationStrategy (this is the default behaviour). Avni checks if there is already a scheduled visit for the given encounter type. If it is there, then it is updated with the incoming scheduled visit's name and other parameters. This strategy works well in most cases.
- Remember that the VisitSchedule rule gets called whether you create a visit, or edit it.
- Remember not to send multiple visit schedule objects for the same encounter type. If you do, the last one will overwrite the previous objects.
Using the "createNew" visit strategy
Do this only if you know what you are doing. If you add visitCreationStrategy of "createNew", then a new visit will be created no matter what.
You need to be careful while using this strategy because, in edit scenarios, we might end up creating the same kind of visits multiple times.
Using the VisitScheduleBuilder.getAllUniqueVisits
VisitSchedulBuilder class has a getAllUniqueVisits method that provides some shortcuts to reduce the cruft you might have to do while creating scheduled visits. It mostly does the right thing, so you don't have to worry about its logic. However, if you think it is doing something you didn't intend, then you can replace it with your own implementation. Look up the code for more details.
5. Decision rule
- Logical scope = Encounter (aka Visit), Subject, or Program Enrolment
- Trigger = On completion of an form wizard before final screen is displayed
- In designer = Form (RULES tab)
- When to use = To create any additional observations based on all the data filled by the user in the form
Used to add decisions/recommendations to the form. The decisions are displayed on the last page of the form and are also saved in the form's observations.
Shape of params object:
{
entity: {}, //it could be ProgramEncounter, ProgramEnrolment or Encounter depending on what type of form is this rule attached to.
entityContext,
services,
user, //Current User's UserInfo object
myUserGroups, //List of Group objects
decisions: {
"enrolmentDecisions": [],
"encounterDecisions": [],
"registrationDecisions": []
} // Decisions object on which you need to add decisions.
}
Shape of decisions parameter:
{
"enrolmentDecisions": [],
"encounterDecisions": [],
"registrationDecisions": []
}
You need to add to decisions parameter's appropriate field and return it back.
Inside the function, you will build decisions using ComplicationsBuilder and push the decisions to the decisions parameter's appropriate field. The return value will be the modified decisions parameter. You can also choose to not use ComplicationsBuilder and directly construct the return value as per the contract shown below:
Shape of the return value
{
"enrolmentDecisions": [<decision object>, ...],
"encounterDecisions": [<decision object>, ...],
"registrationDecisions": [<decision object>, ...]
}
The shape of <decision object>
{
"name": "name of the decision concept",
"value": <text> | <number> | <date> | <datetime> | <name of anwer concepts in case of Coded question>
}
Example
({params, imports}) => {
const programEncounter = params.entity;
const decisions = params.decisions;
const complicationsBuilder = new imports.rulesConfig.complicationsBuilder({
programEncounter: programEncounter,
complicationsConcept: "Birth status"
});
complicationsBuilder
.addComplication("Baby is over weight")
.when.valueInEncounter("Birth Weight")
.is.greaterThanOrEqualTo(8);
complicationsBuilder
.addComplication("Baby is under weight")
.when.valueInEncounter("Birth Weight")
.is.lessThanOrEqualTo(5);
complicationsBuilder
.addComplication("Baby is normal")
.when.valueInEncounter("Birth Weight")
.is.lessThan(8)
.and.when.valueInEncounter("Birth Weight")
.is.greaterThan(5);
decisions.encounterDecisions.push(complicationsBuilder.getComplications());
return decisions;
};
6. Validation rule
- Logical scope = Encounter (aka Visit), Subject, or Program Enrolment
- Trigger = On completion of an form wizard before final screen is displayed
- In designer = Form (RULES tab)
- When to use = To provide validation error(s) to the user that are not specific to one form element but involved data in multiple form elements.
Used to stop users from filling invalid data
Shape of params object:
{
entity: {}, //it could be ProgramEncounter, ProgramEnrolment or Encounter depending on what type of form is this rule attached to.
entityContext,
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
The return value of this function is an array with validation errors.
Example:
({params, imports}) => {
const validationResults = [];
if(programEncounter.getObservationReadableValue('Parity') > programEncounter.getObservationReadableValue('Gravida')) {
validationResults.push(imports.common.createValidationError('Para Cannot be greater than Gravida'));
}
return validationResults;
};
7. Enrolment Eligibility Check Rule
- Logical scope = Subject
- Trigger = On launch of program list when user enrols a subject into program
- In designer = Program page
- When to use = To restrict the programs which are available for enrolment based on subject's data (e.g. not allowing males to enrol in pregnancy programs)
Shape of params object:
{
entity: {}//Subject will be passed here.
program,
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
Shape of the return value
The return value of this function should be a boolean.
Example:
({params, imports}) => {
const individual = params.entity;
return individual.isFemale() && individual.getAgeInYears() > 5;
};
Notes: The eligibility check is triggered only when someone tries to create a visit manually. Form stitching rules can override this default behaviour.
8. Encounter Eligibility Check Rule
- Logical scope = Subject or Program Enrolment
- Trigger = On launch of new visit (encounter) list
- In designer = Encounter page
- When to use = To restrict the encounters which are available based on subject's full data (e.g. not showing postnatal care form if the delivery form has not been filed yet)
Used to hide some visit types depending on some data. If there existed scheduled encounters for that subject or program enrolment, clicking on an ineligible visit type, will fill up the scheduled encounter.
Shape of params object:
{
entity: {}//Subject will be passed here.
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
Shape of the return value
The return value of this function should be a boolean.
Example:
({params, imports}) => {
const individual = params.entity;
const visitCount = individual.enrolments[0].encounters.filter(e => e.encounterType.uuid === 'a30afe96-cdbb-42d9-bf30-6cf4b07354d1').length;
let visibility = true;
if (_.isEqual(visitCount, 1)) visibility = false;
return visibility;
};
Notes: The eligibility check is triggered only when someone tries to create a visit manually. Form stitching rules can override this default behaviour.
9. Checklists rule
Used to add a checklist to an enrolment
Shape of params object:
{
entity: {} //ProgramEnrolment
checklistDetails: [] // Array of ChecklistDetail
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
Example
({params, imports}) => {
let vaccination = params.checklistDetails.find(cd => cd.name === 'Vaccination');
if (vaccination === undefined) return [];
const vaccinationList = {
baseDate: params.entity.individual.dateOfBirth,
detail: {uuid: vaccination.uuid},
items: vaccination.items.map(vi => ({
detail: {uuid: vi.uuid}
}))
};
return [vaccinationList];
};
10. Work List Updation rule
- Logical scope = Subject, Program Enrolment, or Encounters
- Trigger = On display of system recommendation's page in form wizard
- In designer = Main Menu
- When to use = Stitch together multiple forms which can be filled back to back
The System Recommendations screen of Avni can be configured to direct a user to go to the next task to be done. Typically, if a new encounter is scheduled for a person on the same day, then the system automatically prompts the user to perform that encounter.
This is performed using worklists. A worklist is an array of work items.
The WorkListUpdation rule is used to customize this flow. The WorkLists object is passed on to this rule just before showing the System Recommendations screen. Any modification in the worklists is applied immediately to the flow.
You can add a new WorkItem anywhere after the currentWorkList.currentItem.
Shape of params object:
{
worklists: {},
context: {},
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
Example
https://gist.github.com/hithacker/d0fe89107b974797fbb11ced1feda146
11. Subject summary rule
- Logical scope = Subject registration
- Trigger = Before the opening of the subject dashboard profile tab.
- In designer = Subject (Subject Summary Rule)
- When to use = Display important information in the subject's profile. It can be used to show the summary if there are no programs.
This rule is very similar to the Enrolment summary rule. Except its scope is the Subject's registration.
Shape of params object:
{
individual: {}, // Subject model,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
You need to return an array of summary objects from this function.
Shape of the summary object:
{
"name": "name of the summary concept",
"value": <text> | <number> | <date> | <datetime> | <concept list in case of Coded question>
}
Example:
({params, imports}) => {
const summaries = [];
const individual = params.individual;
const mobileNumber = individual.findObservation('Mobile Number');
if(mobileNumber) {
summaries.push({name: 'Mobile Number', value: mobileNumber.getValueWrapper()});
}
return summaries;
};
12. Hyperlink menu item rule
- Logical scope = User
- Trigger = When More navigation is opened in the mobile app
- In designer = Coming very soon...
- When to use = When a dynamic link has to be provided to the user (these links cannot be specific to subjects)
Shape of params object:
{
user: {}, // User
moment: {}, // moment. note other parameters are not supported yet,
token, //Auth-token of the logged-in user
myUserGroups //List of Group objects
}
User: https://github.com/avniproject/avni-models/blob/master/src/UserInfo.js
You need to return a string that is the full URL that can be opened in a browser.
Example:
({params}) => {return `https://reporting.avniproject.org/public/question/11265388-5909-438e-9d9a-6faaa0c5863f?username=${encodeURIComponent(user.username)}&name=${encodeURIComponent(user.name)}&month=${params.moment().month() + 1}&year=${params.moment().year()}`;}
13. Message rule
- When to use = To configure sending Glific messages
- Logical scope = User, Subject, General and Program Encounter, Program Enrolment
- Trigger =
- For User : Only on creation of an User .
- For Subject, General and Program Encounter, Program Enrolment : On every save (create / update)
- In designer = "User Messaging Config", "Subject Type" , "Encounter type" and "Programs" page
Message Rule can be configured only when 'Messaging' is enabled for the organisation. Its configuration constitutes specifying following details:
- Name identifier name for the Message Rule
- Template Used to indicate the Skeleton of the message with placeholders for parameters
- Receiver Type Used to indicate the target audience for the Glific Whatsap message
- Schedule date and time configuration should return the time to send the message.
- Message content configuration should return the parameters to be filled in the Glific message template selected under 'Select Template' dropdown.
Any number of message Rules can be configured.
Example configuration:
Say, 'common_otp' Glific message template is 'Your OTP for {{1}} is {{2}}. This is valid for {{3}}.' If we want to send a OTP message that says 'Your OTP for receiving books is 1458. This is valid for 2 hours.' to a student after 1 day of their registration, then we need to configure for student subject type as shown in the below image (Note the shape of the return objects):
'use strict';
({params, imports}) => ({
scheduledDateTime: new Date("2023-01-05T10:10:00.000+05:30")
});
'use strict';
({params, imports}) => {
const individual = params.entity;
return {
parameters: ['Verify user phone number', '0123', '1 day']
}
};
Shape of params object:
{
entity: {}, //it could be one of User, Individual, General Encounter, ProgramEncounter or Program Enrolment depending on the type of form this rule is attached to
user, //Current User's UserInfo object
myUserGroups //List of Group objects
}
14. Dashboard Card Rule
The shape of dashboard card rule
{
db: "the realm db instance",
user,
myUserGroups,
// ruleInput object can be null
ruleInput: {
type: "string. see 14.1 below",
dataType: "values can be Default or Range",
subjectType: "SubjectType model object. The subject type of the subjects to query and display to the user",
groupSubjectTypeFilter: {
subjectType: "SubjectType. The group subject type to filter by"
},
observationBasedFilter: {
scope: "string. See 14.2 below",
concept: "Concept. the observation value being referred to by the filter value",
programs: {
"UUID of the program": "Program model object"
},
encounterTypes: {
"UUID of the encounter type": "Encounter Type model object"
}
},
// filterValue can be null or empty array when there are no filters chosen by the user
filterValue: "value chosen by the user. the type of data depends on the type of the filter"
}
}
Filter Value Shapes
Address Filter
{
"uuid":"924674dc-d32b-4276-b7b5-fb782f5511f2",
"name":"Kerala",
"level":4,
"type":"State",
"parentUuid":null,
}
15. Manual Programs Eligibility Check Rule
This rule is used when the user fills a form based on which the eligibility of given program is determined by this rule.
Shape of Input Object
params: {
entity: typeof SubjectProgramEligibility,
subject: typeof Individual,
program: typeof Program,
services,
user, //Current User's UserInfo object
myUserGroups //List of Group objects
},
imports: {}
Return
boolean
16. Edit Form Rule
This rule is used when the user tries to edit a form. If non-boolean value is returned in the value, or the rule fails, then it would be treated as true and edit will be allowed. To check the places where it is available, not available, & not applicable - https://avni.readme.io/docs/rules-concept-guide#edit-form-rule .Value of messageKey has translation support.
Shape of Input Object
params: {entity, services, form, myUserGroups,user},
imports: {}
Shape of return object
{
editable : {
value: true/false
messageKey: 'foo'
}
}
Using params.db object when writing rules
In many of the rules params db object is available to query the offline database directly. The db object is an instance of type Realm on which objects is first method that will get called. This returns Realm Results instance, on which one may further call the filtered method one or more times each time returning realm results. Realm result a list with each item being of type (model object's schema name) originally passed in objects method.
Realm Query Language Reference - https://www.mongodb.com/docs/realm/realm-query-language/
Difference between filter and filtered
filtered
method is like running SQL query executed closer or in the database process and hence it orders of magnitude faster than filter
- which is JavaScript method ran by constructing model object for each item is JS memory and then passing it through the filter function. As much as possible filtered should be used for best performance and user experience.
Example of filtered
({params}) => {
const db = {params};
return db.objects("Individual").filtered(`voided = true AND subjectType.name = "Foo"`);
}
Using service methods in the rules
Often, there is the need to get the context of implementation beyond what the models themselves provide. For example, knowing other subjects in the location might be necessary to run a specific rule. For such scenarios, Avni provides querying the DB using the services passed to the rules.
The services object looks like this
{
individualService: '',
}
Right now only individual service is injected into all the rules. One method which is implemented right now returns an array of subjects in a particular location. The method looks like the following, it takes address-level object and subject type name as its parameters and returns a list of all the subjects in that location.
getSubjectsInLocation(addressLevel, subjectTypeName) {
const allSubjects = ....;
return allSubjects;
}
Note that this function is not implemented for the data entry app and throws a "method not supported" error for all the rules when run from the data entry app.
Service methods available are:
- https://github.com/avniproject/avni-client/blob/master/packages/openchs-android/src/service/facade/IndividualServiceFacade.js
- https://github.com/avniproject/avni-client/blob/master/packages/openchs-android/src/service/facade/AddressLevelServiceFacade.js
Example
The view-filter rule is for the subject data type concept that displays all the subjects of type 'Person' in the passed location.
'use strict';
({params, imports}) => {
const encounter = params.entity;
const formElement = params.formElement;
const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({encounter, formElement});
const individualService = params.services.individualService;
const subjects = individualService.getSubjectsInLocation(encounter.individual.lowestAddressLevel, 'Person');
const uuids = _.map(subjects, ({uuid}) => uuid);
statusBuilder.showAnswers(...uuids);
return statusBuilder.build();
};
Using other group/household individuals' information in the rules
Say, an individual belongs to a group A. Sometimes, there is a need to use data of other individuals in the group A. For example, to auto-populate caste information in an individual's registration form (say, when navigated to individual's registration form when tried to add a member to group/household A), we might need to know the caste information of other individuals in that group/household. For such scenarios, Avni provides a way to access group
object from params.entityContext
.
Example
The below rule is for the case when an individual's concept named Caste
needs to be auto-populated based on other member's data in the same group.
'use strict';
({params, imports}) => {
const individual = params.entity;
const moment = imports.moment;
const formElement = params.formElement;
const _ = imports.lodash;
let visibility = true;
let value = null;
let answersToSkip = [];
let validationErrors = [];
const groupSubject = params.entityContext.group;
if(groupSubject.groupSubjects.length > 0) {
const ind = params.entityContext.group.groupSubjects[0].memberSubject;
const caste = ind.getObservationReadableValue('Caste');
value = caste;
}
return new imports.rulesConfig.FormElementStatus(formElement.uuid, visibility, value, answersToSkip, validationErrors);
};
Types of rules and their support/availability in Data Entry App
Not supported / applicable | Supported via rules-server | Supported in browser |
---|---|---|
Decision rule | Enrolment eligibility check rule | Form Element Rule |
Dashboard Card rule | Encounter eligibility check rule | Form Element GroupRule |
Checklists rule | Visit schedule rule | Enrolment Summary Rule |
Work list updation rule | Message rule | Validation rule |
Hyperlink menu item rule | Hyperlink menu item rule |
Types of rules and their support/availability in transaction data upload
Not supported | Supported via rules-server | Not Applicable |
---|---|---|
Message rule | Visit schedule rule | Hyperlink menu item rule |
Decision rule | Enrolment Summary Rule | |
Validation rule | Form Element GroupRule | |
Form Element Rule | ||
Encounter eligibility check rule | ||
Enrolment eligibility check rule | ||
Hyperlink menu item rule | ||
Work list updation rule | ||
Checklists rule | ||
Dashboard Card rule |
Updated 2 months ago