Building > Forms > Configuring > Form Inputs
Data accessible from within CHT forms
Contact forms are used to create and edit contacts (persons and places). Each contact-type should ideally have two forms; one for creation, and another for editing.
These forms are stored in the forms/contact
sub-folder of the project config directory. The naming convention used should be <contact_type_id-{create|edit}>.xlsx
.
To collect information about the contact, use a top-level group with the id of the contact_type as the name
of the group (e.g. person
when adding or editing a person contact). Information in this group will be saved to the contact’s document in the database.
type | name | label::en |
---|---|---|
begin group | person | NO_LABEL |
hidden | parent | Parent Id |
hidden | type | Contact Type |
string | name | Full Name |
… | ||
end group |
The parent
, type
, and name
fields are mandatory on forms that are adding contacts. parent
will be automatically populated with the id of the parent contact. type
will be automatically set to the contact_type id when saving the new contact.
contact
forms have access to a variety of input data.
The form_id
should follow the pattern contact:CONTACT_TYPE_ID:ACTION
where CONTACT_TYPE_ID is the contact_type id for the contact and ACTION is create
or edit
. (e.g. contact:clinic:create
)
The meta information in the {contact_type_id}-{create|edit}.properties.json
file defines additional configuration controlling when the form is available and checks that will be performed when submitting the form.
forms/contact/{contact_type_id}-{create|edit}.properties.json
Property | Description | required |
---|---|---|
context | Added in 3.10 . The contact form context defines when the form should be available in the app. Note: this applies only to the contact form, not the contacts themselves. | no |
context.expression | A JavaScript expression evaluated when a contact profile is viewed. This can limit which users have access to the contact form. See below for more details. | no |
context.permission | String permission key required to allow the user to view and submit this form. If blank, this defaults to allowing all access. | no |
duplicate_check | Added in 4.19 . Allows for configuring or disabling the duplicate detection logic for a particular contact type. | no |
duplicate_check.expression | A JavaScript expression evaluated when submitting the contact form. The expression defines the logic used for determining when a contact is considered to be a duplicate. See below for more details. | no |
duplicate_check.disabled | Boolean determining if the duplicate check should be run when submitting this contact form. Default is false . | no |
The contact form context expression can be used to limit which users have access to the contact form. If the expression evaluates to true
, the form will be listed as an available action on the proper contact profiles.
In the expression, the user
input is available. (Note that unlike in the app form expressions, the contact
and summary
inputs are not currently available for contact form expressions.)
The duplicate check expression is a boolean check executed against each of the sibling contacts of the contact being created/modified. “Sibling contacts” are contacts of the same type that share the same parent contact. When the expression evaluates to true
, the contact being created/edited will be considered a duplicate of the existing sibling.
In the expression, both the current
contact doc (the contact currently being created/edited) and the existing
sibling contact doc are available. The default duplicate expression is:
levenshteinEq(current.name, existing.name, 3) && ageInYears(current) === ageInYears(existing)
This expression will consider contacts to be duplicate if the Levenshtein distance between the two names is less than or equal to 3 (meaning the names are very similar or exactly the same) and (for persons) if the contacts have the same age (in years).
When designing custom duplicate check expressions, consider how the contact data collected might evolve over time. If properties are added/removed/renamed on the contact doc, your duplicate expression logic will need to account for this if it references these properties (e.g. support for falling-back to older properties to ensure broader compatibility).
Always consider the nature and quality of your data. As data quality improves (e.g., consistent naming conventions), duplicate check expressions for some contact types can be refined to reduce both false positives and false negatives.
The default message shown to the user when a duplicate contact is found can be modified by adding a custom translation for the duplicate_check.contact.duplication_message
key.
Additionally, different messages can be shown for different contact types by setting duplicate_check.contact.${contact_type_id}.duplication_message
keys. This can be useful if you want to prompt the user with the likely reason the duplicate contacts matched based on your custom duplicate check expression logic.
The following functions are available for use in JavaScript expressions:
Signature | Description |
---|---|
ageInDays(contact) | Returns the current age of the given contact in days. |
ageInMonth(contact) | Returns the current age of the given contact in months. |
ageInYears(contact) | Returns the current age of the given contact in years. |
levenshteinEq(string0, string1, threshold = 3) | (Added 4.19.0 ) Returns true if the Levenshtein distance between the given strings is less than or equal to the given threshold. Otherwise false . |
normalizedLevenshteinEq(string0, string1, threshold = 0.42857142857142855) | (Added 4.19.0 ) Similar to the levenshteinEq function except the distance value is “normalized” (by dividing by the length of the longest string) before comparing to the threshold. See below for more details. |
In cases where the length of the input strings can vary greatly, the normalizedLevenshteinEq
function can be preferable to the levenshteinEq
function. The normalizedLevenshteinEq
function divides the Levenshtein distance by the length of the longest string before comparing the result to the threshold. In effect, this makes the matching more strict for shorter strings (the actual distance must be shorter for them to be considered equal) and more lenient for longer strings (the actual distance can be longer, but the strings will still be considered equal).
This can help make the same threshold limit value appropriate for use across a greater variety of string lengths. For example, if the threshold is set to 0.3
, then an actual distance of 2
would be considered equal (when using normalizedLevenshteinEq
) for strings of length 9
, but not for two strings of length 5
. (Because (2 / 9 = 0.2222) <= 0.3
and (2 / 5 = 0.4) > 0.3
.)
When using normalized Levenshtein equality, make sure you consider the behavior of a particular threshold value at the edges of your expected string length range. For example, the distance between far
and car
is 1
. If you want your logic to consider john
and jon
as equal, you will need to use a normalized Levenshtein equality threshold of at least 1 / 4 = 0.25
. However, for strings with a length of 25
, that threshold will allow a distance of 5
to be considered equal.
In this sample properties file, the person create form would only be accessible for CHW Supervisors with the can_export_contacts
permission. Additionally, a new person should not have the exact same name as an existing sibling contact.
forms/contact/person-create.properties.json
{
"context": {
"expression": "user.role === 'chw_supervisor'",
"permission": "can_export_contacts"
},
"duplicate_check": {
"expression": "current.name === existing.name"
}
}
If your place contact forms are similar across all levels of your specified project hierarchy, you can templatise the form creation process. You will need to create the following files in forms/contact
: place-types.json
, PLACE_TYPE-create.xlsx
and PLACE_TYPE-edit.xlsx
.
place-types.json
maps the place contact_type id to a human-readable description that will be shown on the app’s user interface.
Both PLACE_TYPE-create.xlsx
and PLACE_TYPE-edit.xlsx
will contain two placeholder values PLACE_TYPE
and PLACE_NAME
which will be replaced by the keys and values specified in place-types.json
respectively during form conversion. Also, copies of the different place-type forms will be created (if they don’t exist) during the form conversion process with PLACE_TYPE
being replaced with the keys specified in place-types.json
.
Convert and build the contact forms into your application using the convert-contact-forms
and upload-contact-forms
actions in cht-conf
.
cht --local convert-contact-forms upload-contact-forms
For examples on how to structure the above files you can have a look at the default configuration in CHT-core.
Contact forms for creating a place can also optionally create one or more person-type documents. One of these person contacts can be linked to the created place as the primary contact.
Below is a simple structure of a place form showing all the necessary components.
Section 1 is similar to what has been described earlier for person forms.
Section 2 specifies the contact that will be linked to the place being created. parent
, type
and contact_type
and name
are mandatory. This also applies to the place-type definition in section 4. contact
on the other hand is not mandatory for the successful creation of a place. It is usually more convenient to create a place and its primary contact at the same time.
You can also create additional contacts linked to the place being created when you have a structure similar to that shown in section 3.
Data accessible from within CHT forms
How to include multimedia files in forms
Trigger calls and SMS from within the form, or send an SMS once submitted.
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.