contact

Contact Forms: Used for creating and editing people and places

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.

Form details

Survey sheet

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.

typenamelabel::en
begin grouppersonNO_LABEL
hiddenparentParent Id
hiddentypeContact Type
stringnameFull 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.

Input data

contact forms have access to a variety of input data.

Settings sheet

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)

Properties

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

PropertyDescriptionrequired
contextAdded 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.expressionA 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.permissionString permission key required to allow the user to view and submit this form. If blank, this defaults to allowing all access.no
duplicate_checkAdded in 4.19. Allows for configuring or disabling the duplicate detection logic for a particular contact type.no
duplicate_check.expressionA 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.disabledBoolean determining if the duplicate check should be run when submitting this contact form. Default is false.no

Context Expression

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.)

Duplicate Check Expression

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.

Customizing the duplicate contact error message

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.

Expression functions

The following functions are available for use in JavaScript expressions:

SignatureDescription
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.

Normalized Levenshtein Equality

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.

Code sample

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"
  }
}

Generic contact forms

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.

Creating person and place contacts in the same form

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.

Place forms survey sheet

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.


Building > Forms > Configuring > Form Inputs

Data accessible from within CHT forms

Building > Forms > Configuring > Additional Docs

Building > Forms > Configuring > Multimedia in Forms

How to include multimedia files in forms

Building > Forms > Configuring > App Form SMS

Trigger calls and SMS from within the form, or send an SMS once submitted.