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
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
Property | Description | Required | Default |
---|---|---|---|
id | Questionnaire Unique String | no | null |
title | Display text shown as on the button | yes | |
titleColor | Display text color | ||
visible | A string to show if the questionnaire button should be shown | no | true |
enabled | A string to show if the questionnaire button should be clickable | no | true |
actions | A list of actions that you would like to be executed | yes if you are using a questionnaire | |
actions (trigger) | Defines when you want to launch the questions | yes | |
actions(workflow) | Import to execute logic related to the questionnaire | yes | |
linkIds | Contains 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 | |
type | The 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 logic | no | null |
repeats | Boolean that controls whether an item is repeated or not, used to control a single item or group item that creates a repeated group | no | false |
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
Property | Description | Required | Default |
---|---|---|---|
id | Unique uuid that determines what questionnaire i.e form to launch | yes | |
title | label text of the questionnaire | yes | |
resourceIdentifier | FHIR resource to pull/add or update | yes |
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
Property | Description | Required | Default |
---|---|---|---|
planDefinitions | A list of questionnaire planDefinition uuids | no | null |
cqlInputResources | A list of CQL Library id 's. The referenced Libraries are executed after questionnaire submission | no | null |
barcodeLinkId | The link ID for barcode widget used in the Questionnaire | no | null |
saveQuestionnaireResponse | Indicate whether to save QuestionnaireResponse or not | yes | true |
onSubmitActions | Configurations for actions invoked post Questionnaire submission | no | null |
extractedResourceUniquePropertyExpressions | Configurations for unique properties used to identify resources during Questionnaire edit | no | null |
uniqueIdAssignment | Configuration for unique identifier assignment | no | null |
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
- 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))"]
},
]
- 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
Property | Description | Required | Default |
---|---|---|---|
eventType | The intention of the eventWorkflow. E.g close resources | yes | RESOURCE_CLOSURE is supported for now |
triggerConditions | This defines an array of condition for to be met for the event to run | no | null |
eventResourceId | uniqueId of resource id to be closed | yes | |
eventResources | A 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"
}