Skip to main content

Questionnaire configuration

This configuration is used to configure a Questionnaire. A Questionnaire is a data entry form used that is compliant to the FHIR specifications for a Questionnaire

tip

Use this configuration in other configs like profile and register.

FHIR Core reads user data from label fields, date pickers, radiobutton, checkboxes e.t.c through forms called Questionnaires We can group these into 3 categories.fields

A general questionnaire config

{
"id":"recordAsSick",
"title": "Record as Sick",
"titleColor": "@{patientTextColor}",
"visible": "@{isChildUnder2months}",
"enabled": "@{patientActive}",
"actions": [
{
"trigger": "ON_CLICK",
"workflow": "LAUNCH_QUESTIONNAIRE",
"questionnaire": {
"id": "questionnaire-uuid",
"title": "Record sick child",
"resourceIdentifier": "@{patientId}"
}
}
]
}

The above is a simple question that can be used to record a sick patient.

Config properties

PropertyDescriptionRequiredDefault
idQuestionnaire Unique Stringnonull
titleDisplay text shown as on the buttonyes
titleColorDisplay text color
visibleA string to show if the questionnaire button should be shownnotrue
enabledA string to show if the questionnaire button should be clickablenotrue
actionsA list of actions that you would like to be executedyes if you are using a questionnaire
actions (trigger)Defines when you want to launch the questionsyes
actions(workflow)Import to execute logic related to the questionnaireyes
linkIdsContains configurations for linkIds for fields in the Questionnaires that are used to control application workflow and content; ranging from read only fields to fields used to capure location/barcode details. Each linkId is identified by a type (READ_ONLY, LOCATION, BARCODE, IDENTIFIER)no
typeThe item type, e.g. date, choice or group. A group type allows you to create a section within a questionnaire that controls and treats the items within the group as a single item. This is suitable for block sections that can be toggled based on condition or answer expression logicnonull
repeatsBoolean that controls whether an item is repeated or not, used to control a single item or group item that creates a repeated groupnofalse

Questionnaire config actions

"questionnaire": {
"id": "questionnaire-uuid",
"title": "Record sick child",
"resourceIdentifier": "@{patientId}",
"linkIds": [
{
"linkId": "122d7ffe-3137-46b5-a28d-5c1a335c4899",
"type": "READ_ONLY",
},
{
"linkId": "145fb5a7-66b0-455d-9cff-a0d0d5994caf",
"type": "LOCATION",
}
]
}

Questionnaire block config properties

PropertyDescriptionRequiredDefault
idUnique uuid that determines what questionnaire i.e form to launchyes
titlelabel text of the questionnaireyes
resourceIdentifierFHIR resource to pull/add or updateyes

The questionnaire id inside the questionnaire block

Sample questionnaire with a planDefinition

{
"title": "Register Pregnancy",
"titleColor": "@{patientTextColor}",
"visible": "@{canRegisterPregnancy}",
"enabled": "@{patientActive}",
"actions": [
{
"trigger": "ON_CLICK",
"workflow": "LAUNCH_QUESTIONNAIRE",
"questionnaire": {
"id": "questionnaire-uuid",
"title": "Record to ANC",
"resourceIdentifier": "@{patientId}",
"planDefinitions": [
"planDefinition-uuid"
]
}
}
]
}

The above questionnaire JSON config adds planDefinition in an array. For each item in the array, we pull related plan Definitions. These are used when generating other tasks, CarePlans and related resources.See https://fhircore.smartregister.org/writing-fhir/plan-definiton

Extra config properties

PropertyDescriptionRequiredDefault
planDefinitionsA list of questionnaire planDefinition uuidsnonull
cqlInputResourcesA list of CQL Library id's. The referenced Libraries are executed after questionnaire submissionnonull
barcodeLinkIdThe link ID for barcode widget used in the Questionnairenonull
saveQuestionnaireResponseIndicate whether to save QuestionnaireResponse or notyestrue
onSubmitActionsConfigurations for actions invoked post Questionnaire submissionnonull
extractedResourceUniquePropertyExpressionsConfigurations for unique properties used to identify resources during Questionnaire editnonull
uniqueIdAssignmentConfiguration for unique identifier assignmentnonull

Dynamic data pass between Profiles and Questionnaires

For you to pass data between profiles and questionnaires you can make use of action config params which are executed when LAUNCH_QUESTIONNAIRE is invoked.

Data extraction happens during rules execution and is persisted in computedValuesMap which is later used to interpolate values annotated with @value. See [working with rules] (https://docs.opensrp.io/engineering/android-app/configuring/working-with-rules).

For example, in the underlying_conditions questionnaire you would like to show or hide the Cancer option based on whether the patient has cancer or not. The solution would be to pass has-cancer BOOLEAN from adult_profile_config to underlying_conditions.

Assuming that the LAUNCH_QUESTIONAIRE onClick function of adult_profile_config takes you to underlying_conditions questionnaire screen, below is a practical example of how the data would be passed.

Cancer LAUNCH_QUESTIONAIRE

Sample JSON

  1. Write rules to extract the data you need.
"rules":[
{
"name": "hasCancer",
"condition": "true",
"priority": 1,
"actions": [
"data.put('hasCancer', service.evaluateToBoolean(availableConditions, \"Condition.code.coding.code = '363346000' and Condition.clinicalStatus.coding.code = 'active'\", false))"]
},
]
  1. add your params at LAUNCH_QUESTIONNAIRE section of adult_profile_config.json

Sample JSON

    { "trigger": "ON_CLICK",
"workflow": "LAUNCH_QUESTIONNAIRE",
"questionnaire": {
"id": "54497",
"title": "Record Comorbidity",
"resourceIdentifier": "@{patientId}",
"params": [
{
"key": "familyLogicalId",
"value": "@{familyLogicalId}",
"paramType": "UPDATE_DATE_ON_EDIT"
}
]
},
"params": [
{
"paramType": "PREPOPULATE",
"linkId": "has-cancer",
"dataType": "BOOLEAN",
"key": "hasCancer",
"value": "@{hasCancer}"
}],
}

On the underlying_conditions questionnaire side, we will then call the has-cancer linkId that was declared in the adult_profile_config, as shown in the example below.

{
"extension": [ {
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden",
"valueBoolean": true
} ],
"linkId": "has-cancer",
"definition": "http://hl7.org/fhir/StructureDefinition/Resource#Resource.id",
"type": "boolean"
}, {
"linkId": "e4b02bd1-faa3-415e-84e7-378b8cc84d92",
"text": "Cancer",
"type": "choice",
"enableWhen": [ {
"question": "9f320854-7677-4ecb-9886-d323b7161a2e",
"operator": "=",
"answerCoding": {
"system": "urn:uuid:5fddcabd-9ae1-412a-e591-8fb6089a4f26",
"code": "yes"
}
}, {
"question": "has-cancer",
"operator": "=",
"answerBoolean": false
} ],
"enableBehavior": "all",
"required": false,
"answerOption": [ {
"valueCoding": {
"id": "727795dd-2870-4bc2-e057-4aa8518405dd",
"system": "urn:uuid:5fddcabd-9ae1-412a-e591-8fb6089a4f26",
"code": "yes",
"display": "Yes"
}
}, {
"valueCoding": {
"id": "944752c4-b116-4bca-8bc0-7e2889219565",
"system": "urn:uuid:5fddcabd-9ae1-412a-e591-8fb6089a4f26",
"code": "no",
"display": "No"
}
} ]
}

Sample questionnaire with an event workflow

Suppose you wanted to close above mentioned generated resources. For example, when moving a patient from ANC to PNC you would like to close exsiting Tasks and Careplans generated for ANC, the below configs shows how to do this.

Sample questionnaire with an event workflow

{
"title": "Pregnancy Outcome",
"titleColor": "@{patientTextColor}",
"visible": "@{isPregnant}",
"enabled": "@{patientActive}",
"actions": [
{
"trigger": "ON_CLICK",
"workflow": "LAUNCH_QUESTIONNAIRE",
"questionnaire": {
"id": "questionnaire-uuid",
"title": "Pregnancy outcome",
"resourceIdentifier": "@{patientId}",
"planDefinitions": [
"planDefinitions-uuid"
],
"eventWorkflows": [
{
"eventType": "RESOURCE_CLOSURE",
"triggerConditions": [
{
"eventResourceId": "carePlanToBeClosed",
"matchAll": false,
"conditionalFhirPathExpressions": [
"condition-to-check"
]
}
],
"eventResources": [
{
"id": "carePlanToBeClosed",
"resource": "CarePlan",
"configRules": [
{
"name": "patientId",
"condition": "true",
"actions": [
"data.put('patientId', fhirPath.extractValue(Patient, 'Patient.id'))"
]
}
],
"dataQueries": [
{
"paramName": "instantiates-canonical",
"filterCriteria": [
{
"dataType": "REFERENCE",
"value": "PlanDefinition/planDefinition-uuid-used-to-generate-the-resources"
}
]
},
{
"paramName": "subject",
"filterCriteria": [
{
"dataType": "REFERENCE",
"computedRule": "patientId"
}
]
}
],
"relatedResources": [
{
"resource": "Task",
"searchParameter": "based-on"
}
]
}
]
}
]
},
"params": [
{
"paramType": "PREPOPULATE",
"linkId": "linkId-uuid",
"dataType": "STRING",
"key": "key",
"value": "@{value-before-interpolation}"
}
]
}
]
}

Extra eventWorkflows properties

PropertyDescriptionRequiredDefault
eventTypeThe intention of the eventWorkflow. E.g close resourcesyesRESOURCE_CLOSURE is supported for now
triggerConditionsThis defines an array of condition for to be met for the event to runnonull
eventResourceIduniqueId of resource id to be closedyes
eventResourcesA list of resources to close(Type of ResourceConfig)yes

Unique ID assignment

Unique IDs are unique identifier values assigned to a resource (e.g. Patient) and are associated with a single entity.

Unique ID assignment configs determine how pre-generated unique IDs are retrieved from a Group FHIR resource and subsequently populated in a Questionnaire field.

Here is a sample configuration for the unique identifier assignment:

{
"uniqueIdAssignment": {
"linkId": "phn",
"idFhirPathExpression": "Group.characteristic.where(exclude=false and code.text='phn').first().value.text",
"readOnly": false,
"resource": "Group",
"sortConfigs": [
{
"paramName": "_lastUpdated",
"dataType": "DATE",
"order": "DESCENDING"
}
],
"resourceFilterExpression": {
"conditionalFhirPathExpressions": [
"Group.active = true and Group.type = 'device' and Group.name = 'Unique IDs'"
],
"matchAll": true
}
}
}

The configuration contains the following properties:

linkId - The linkId for the targeted Questionnaire item idFhirPathExpression - The FHIR path expression used to extract ID from a resource readOnly - Enable or disable editing of the field. Defaults to true resource - FHIR resource used to store generated unique IDs sortConfigs - For ordering resources. It is important to ensure the resources are ordered by last updated resourceFilterExpression - Extra configurations to apply filter via code on the declared Resource

NOTE: If the readOnly configuration is set to false, the ID field in the Questionnaire becomes editable. If the prepopulated ID is modified and a different ID is submitted with the Questionnaire, the prepopulated ID will not be marked as used. This means that it will still be prepopulated the next time the Questionnaire is launched.

Characteristic-based Group resource for unique IDs

IDs are stored as text in a valueCodeableConcept in the characteristic field. The batch of IDs is assigned to a Practitioner using the managingEntity.

When an ID is used, the characteristic entry with that ID is updated to be excluded by setting "exclude": true. Once all IDs in the Group are used, the group is set to inactive.

Sample Group resource with unique IDs

{
"resourceType": "Group",
"id": "37312ad4-538e-4535-82d2-ea14f40deeb9",
"meta": {
"versionId": "9",
"lastUpdated": "2023-12-22T06:43:35.986+00:00",
"source": "#04a1c85fb6adf0cc",
"tag": [
{
"system": "https://smartregister.org/care-team-tag-id",
"code": "3e005baf-854b-40a7-bdd5-9b73f63aa9a3",
"display": "Practitioner CareTeam"
},
{
"system": "https://smartregister.org/organisation-tag-id",
"code": "41eae946-bdc4-4179-b404-6503ff12f59c",
"display": "Practitioner Organization"
},
{
"system": "https://smartregister.org/location-tag-id",
"code": "3816",
"display": "Practitioner Location"
},
{
"system": "https://smartregister.org/practitioner-tag-id",
"code": "49b72a3d-44cd-4a74-9459-4dc9f6b543fa",
"display": "Practitioner"
},
{
"system": "https://smartregister.org/app-version",
"code": "Not defined",
"display": "Application Version"
}
]
},
"identifier": [
{
"system": "http://smartregister.org",
"value": "37312ad4-538e-4535-82d2-ea14f40deeb9"
}
],
"active": true,
"type": "device",
"actual": true,
"name": "Unique IDs",
"managingEntity": {
"reference": "Practitioner/49b72a3d-44cd-4a74-9459-4dc9f6b543fa"
},
"characteristic": [
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000010001"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000020002"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000030003"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000040004"
},
"exclude": false
},
{
"code": {
"text": "phn"
},
"valueCodeableConcept": {
"text": "1000050005"
},
"exclude": false
}
]
}

Hiding characters in a questionnaire

Sensitive information typed on a questionnaire can be hidden through adding a linkId extension. A sample linkId with password-widget extension looks like

{
"extension": [
{
"url": "https://github.com/google/android-fhir/StructureDefinition/questionnaire-itemControl",
"valueString": "password-widget"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/minLength",
"valueInteger": 16
},
{
"url": "http://ehelse.no/fhir/StructureDefinition/validationtext",
"valueString": "NIK number should be of 16 digits"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/regex",
"valueString": "^\\+?(?:[()\\h-]*\\d[()\\h-]*){16}$"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory",
"valueCodeableConcept": {
"coding": [
{
"system": "http://hl7.org/fhir/questionnaire-display-category",
"code": "instructions"
}
]
}
}
],
"linkId": "82a80049-8d2d-4008-81aa-55356b9d6628",
"text": "NIK number",
"_text": {
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/translation",
"extension": [
{
"url": "lang",
"valueCode": "id"
},
{
"url": "content",
"valueString": "Nomor Induk Kependudukan (NIK)"
}
]
}
]
},
"type": "string",
"required": false,
"maxLength": 16
}

Below is the specific extension for this. The extension is validated in this class PasswordViewHolderFactory

  {
"url": "https://github.com/google/android-fhir/StructureDefinition/questionnaire-itemControl",
"valueString": "password-widget"
}