app

App Forms: Used to complete reports, tasks, and actions in the app

App forms are used for care guides within the web app, whether accessed in browser or via the Android app. When a user completes an app form, the contents are saved in the database with the type data_record. These docs are known in the app as Reports.

App forms are defined by the following files:

  • A XML form definition using a CHT-enhanced ODK XForm format
  • A XLSForm form definition, easier to write and converts to the XForm (optional)
  • Meta information in the {form_name}.properties.json file (optional)
  • Media files in the {form_name}-media directory (optional). How to include multimedia files.

XForm

A CHT-enhanced version of the ODK XForm standard is supported.

Data needed during the completion of the form (eg patient’s name, prior information) is passed into the inputs group. Reports that have at least one of place_id, patient_id, and patient_uuid at the top level will be associated with that contact.

See Also: Passing contact data to care guides

A typical form ends with a summary group (eg group_summary, or group_review) where important information is shown to the user before they submit the form.

In between the inputs and the closing group is the form flow - a collection of questions that can be grouped into pages. All data fields submitted with a form are stored, but often important information that will need to be accessed from the form is brought to the top level. To make sure forms are properly associated to a contact, make sure at least one of place_id, patient_id, and patient_uuid is stored at the top level of the form.

See Also: Content and Layout

XLSForm

Since writing raw XML can be tedious, we suggest creating the forms using the XLSForm standard, and using the cht-conf command line configurer tool to convert them to XForm format.

typenamelabelrelevantappearancecalculate
begin groupinputsInputs./source = ‘user’field-list
hiddensource
hiddensource_id
begin groupcontact
db:person_idPatient IDdb-object
stringpatient_idMedic IDhidden
stringnamePatient Namehidden
end group
end group
calculate_id../inputs/contact/_id
calculatepatient_id../inputs/contact/patient_id
calculatename../inputs/contact/name
begin groupgroup_summarySummaryfield-list summary
noter_patient_info**${patient_name}** ID: ${r_patient_id}
noter_followupFollow Up <i class="fa fa-flag”></i>
noter_followup_note${r_followup_instructions}
end group

Supported XLSForm Meta Fields

XLSForm has a number of data type options available for meta data collection, of which the following are supported in CHT app forms:

elementdescription
startA timestamp of when the form entry was started, which occurs when the form is fully loaded.
endA timestamp of when the form entry ended, which is when the user hits the Submit button.
todayDay on which the form entry was started.

XPath

Calculations are achieved within app forms using XPath statements in the “calculate” field of XForms and XLSForms. CHT apps support XPath from the ODK XForm spec, which is based on a subset of XPath 1.0, and is evaluated by medic/openrosa-xpath-evaluator. The ODK XForm documentation provides useful notes about the available operators and functions. Additionally, CHT specific functions are available for forms in CHT apps.

CHT XForm Widgets

Some XForm widgets have been added or modified for use in the app:

  • Bikram Sambat Datepicker: Calendar widget using Bikram Sambat calendar. Used by default for appropriate languages.
  • Countdown Timer: A visual timer widget that starts when tapped/clicked, and has an audible alert when done. To use it create a note field with an appearance set to countdown-timer. The duration of the timer is the field’s value, which can be set in the XLSForm’s default column. If this value is not set, the timer will be set to 60 seconds.
  • Contact Selector: Select a contact, such as a person or place, and save their UUID in the report. In v3.10.0 or above, set the field type to string and appearance to select-contact type-{{contact_type_1}} type-{{contact_type_2}} .... If no contact type appearance is specified then all contact types will be returned. For v3.9.0 and below, set the field type to db:{{contact_type}} and appearance to db-object.
  • Rapid Diagnostic Test capture: Take a picture of a Rapid Diagnotistic Test and save it with the report. Works with rdt-capture Android application. To use create a string field with appearance mrdt-verify.
  • Simprints registration: Register a patient with the Simprints biometric tool. To include in a form create a string field with appearance of simprints-reg. Requires the Simprints app connected with hardware, or mock app. Demo only, not ready for production since API key is hardcoded.
  • Display Base64 Image: Available in +3.13.0. To display an image based on a field containing the Base64 encode value, add the appearance display-base64-image to a field type text.
  • Android App Launcher: Available in +3.13.0 and in Android device only. A widget to launch an Android app that receives and sends data back to an app form in CHT-Core. See more details in the Android App Launcher.

The code for these widgets can be found in the CHT Core Framework repository.

Contact Selector

Using a contact selector allows you to get data off the selected contact(person or place) or search for an existing contact.

When using with the appearance column set to db-object. The contact selector will display as a search box allowing you to search for the type of contact specified when building the report. EX: db-person will only search for contacts with type of person.

When used as a field you can pull the current contact. This is can be used to link reports to a person or place where you started the form from. Getting the data of _id or patient_id and setting those to patient_id or patient_uuid on the final report will link that report so it displays on their contact summary page.

Example of getting the data from the contact and assigning it to the fields neccessary to link the report.

typenamelabelrelevantappearancecalculate
begin groupcontact
db:person_idPatient IDdb-object
stringpatient_idMedic IDhidden
stringnamePatient Namehidden
end group
calculatepatient_uuidPatient UUID../contact/_id
calculatepatient_idPatient ID../contact/patient_id

Android App Launcher

Available in +3.13.0

This widget requires the cht-android app in order to work, and will be disabled for users running the CHT in a browser.

Use the Android App Launcher widget in a form to configure an intent to launch an Android app installed in the mobile device. The widget will send values from input fields type text to the app and will assign the app’s response into output fields type text. The only supported field type is text. The widget will automatically display a button to launch the app.

To define the widget, create a group with the appearance android-app-launcher, then define the Android intent fields with type text. The fields action, category, type, uri, packageName and flags are optional. Every Android app has specific ways of launching with intents, so check the app’s documentation and assign the corresponding values in the default column. See example below:

typenamelabelappearancerepeat_countdefault
begin groupcamara-appNO_LABELandroid-app-launcher
textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
textcategoryNO_LABEL
texttypeNO_LABEL
texturiNO_LABEL
textpackageNameNO_LABEL
textflagsNO_LABEL
end groupcamara-app

To define the widget’s input fields and send data as Android Intent’s extras, create a group inside the widget with the appearance android-app-inputs. In order to assign the app’s response to the widget’s output fields, create a group with the appearance android-app-outputs.

Important to remember: The fields inside the input and the output groups should to match in name and location to what the Android app receives and returns, otherwise the communication between the widget and the Android app won’t work properly.

typenamelabelappearancerepeat_countdefault
begin groupcamara-appNO_LABELandroid-app-launcher
textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
begin groupcamara-app-inputsNO_LABELandroid-app-inputs
textlocationLocation
textdestinationDestination
end groupcamara-app-inputs
begin groupcamara-app-outputsNO_LABELandroid-app-outputs
textpicturePicture
textdateDate
end groupcamara-app-outputs
end groupcamara-app

To instruct the widget to process nested data objects, create a new group inside the input or the output group with the appearance android-app-object. Objects cannot be assigned to a field, it should be a group with fields to map the properties to fields that share the same name.

Important to remember: The nested group’s name should match in name and location to what the Android app receives and returns, otherwise it won’t be able to find the nested object.

typenamelabelappearancerepeat_countdefault
begin groupcamara-appNO_LABELandroid-app-launcher
textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
begin groupcamara-app-inputsNO_LABELandroid-app-inputs
textlocationLocation
begin groupphoto_configurationNO_LABELandroid-app-object
textapertureAperture
textshutter_speedShutter Speed
end groupphoto_configuration
end groupcamara-app-inputs
begin groupcamara-app-outputsNO_LABELandroid-app-outputs
textpicturePicture
textdateDate
end groupcamara-app-outputs
end groupcamara-app

To instruct the widget to process an array of strings or numbers, create a new repeat with fix size in the repeat_count column and place it inside the input or the output group with the appearance android-app-value-list, then create 1 field type text to store every array’s value, only 1 field is allowed. To process an array of objects, use the appearance android-app-object-list instead.

Important to remember: The repeat's name should match in name and location to what the Android app receives and returns, otherwise it won’t be able to find the array.

typenamelabelappearancerepeat_countdefault
begin groupcamara-appNO_LABELandroid-app-launcher
textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
textflagsNO_LABEL268435456
begin groupcamara-app-inputsNO_LABELandroid-app-inputs
textlocationLocation
begin repeatphoto_filtersNO_LABELandroid-app-value-list2
textfilterFilter
end repeat
end groupcamara-app-inputs
begin groupcamara-app-outputsNO_LABELandroid-app-outputs
textdateDate
begin repeatcaptureNO_LABELandroid-app-object-list3
textlight_percentageLight
textcontrast_percentageContrast
begin grouppatient_detailsNO_LABELandroid-app-object
textpicturePatient picture
textpatient_idPatient ID
textpatient_namePatient name
end grouppatient_details
end repeat
end groupcamara-app-outputs
end groupcamara-app

CHT XPath Functions

difference-in-months

Calculates the number of whole calendar months between two dates. This is often used when determining a child’s age for immunizations or assessments.

z-score

In Enketo forms you have access to an XPath function to calculate the z-score value for a patient. The function accesses table data stored in CouchDB.

The z-score function takes four parameters:

  • The name of z-score table to use, which corresponds to value of the database document’s _id attribute.
  • Patient’s sex, which corresponds to the data object’s name. In the example below male for this parameter corresponds to charts[].data.male in the database document.
  • First parameter for the table lookup, such as age. Value maps to the key value in the database document.
  • Second parameter for the table lookup, such as height. Value is compared against the points in the databae document.

Example Use

This example XForm form shows the use of the z-score function. To calculate the z-score for a patient given their sex, age, and weight the XPath calculation is as follows:

z-score('weight-for-age', ../my_sex, ../my_age, ../my_weight)

The data used by this function needs to be added to CouchDB. The example below shows the structure of the database document. It creates a weight-for-age table, where you can see that a male aged 0 at 2.08kg has a z-score of -3. Your database doc will be substantially larger, so you may find the conversion script helpful to convert z-score tables to the required doc format.

{
  "_id": "zscore-charts",
  "charts": [
    {
      "id": "weight-for-age",
      "data": {
        "male": [
          {
            "key": 0,
            "points": [
              1.701,
              2.08,
              2.459,
              2.881,
              3.346,
              3.859,
              4.419,
              5.031,
              5.642
            ]
          }
        ]
      }
    }
  ]
}

parse-timestamp-to-date

Available in +3.13.0.

Use this function to parse from a timestamp number to a date. This is useful when using other XForm utility functions that receive date type as parameter, see example below:

typenamelabelcalculationdefault
stringstart_date_timeNO_LABEL1628945040308
stringstart_date_time_formattedStarted on:format-date-time(parse-timestamp-to-date(${start_date_time}), “%e/%b/%Y %H:%M:%S”)

CHT Special Fields

The NationalQuintile and UrbanQuintile fields on a form will be assigned to all people belonging to the place. This is helpful when household surveys have quintile information which could be used to target health services for individuals.

Read More: Tracking Wealth Quintiles

Uploading Binary Attachments

Forms can include arbitrary binary data which is submitted and included in the doc as an attachment. If this is an image type it’ll then be displayed inline in the report UI.

To mark an element as having binary data add an extra column in the XLSForm called instance::type and specify binary in the element’s row.

Properties

The meta information in the {form_name}.properties.json file defines the form’s title and icon, as well as when and where the form should be available.

forms/app/{form_name}.properties.json

propertydescriptionrequired
titleThe form’s title seen in the app. Uses a localization array using the 2-letter code, not the translation keys discussed in the Localization section.yes
iconIcon associated with the form. The value is the image’s key in the resources.json file, as described in the Icons sectionyes
subject_keyOverride the default report list title with a custom translation key. The translation uses a summary of the report as the evaluation context so you can include report fields in your value, for example: Case registration {{case_id}}. Useful properties available in the summary include: from (the phone number of the sender), phone (the phone number of the report contact), form (the form code), subject.name (the name of the subject), and case_id (the generated case id). Defaults to the name of the report subject.no
hidden_fieldsArray of Strings of form fields to hide in the view report UI in the app. This is only applied to future reports and will not change how existing reports are displayed.no
contextThe context defines when and where the form should be available in the appno
context.personBoolean determining if the form can be seen in the Action list for a person’s profile. This is still subject to the expression.no
context.placeBoolean determining if the form can be seen in the Action list for a person’s profile. This is still subject to the expression.no
context.expressionA JavaScript expression which is evaluated when a contact profile or the reports tab is viewed. If the expression evaluates to true, the form will be listed as an available action. The inputs contact, user, and summary are available. By default, forms are not shown on the reports tab, use "expression": "!contact" to show the form on the Reports tab since there is no contact for this scenario.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

Code sample

In this sample properties file, the associated form would only show on a person’s page, and only if their sex is unspecified or female and they are between 10 and 65 years old:

forms/app/pregnancy.properties.json

  {
    "title": [
      {
        "locale": "en",
        "content": "New Pregnancy"
      },
      {
        "locale": "hi",
        "content": "नई गर्भावस्था"
      }
    ],
    "icon": "pregnancy-1",
    "hidden_fields": [ "private", "internal" ],
    "context": {
      "person": true,
      "place": false,
      "expression": "contact.type === 'person' && (!contact.sex || contact.sex === 'female') && (!contact.date_of_birth || (ageInYears(contact) >= 10 && ageInYears(contact) < 65))",
      "permission": "can_register_pregnancies"
    }
  }

Build

Convert and build the app forms into your application using the convert-app-forms and upload-app-forms actions in cht-conf.

cht --local convert-app-forms upload-app-forms