Discussion area to go over the suggested implementation of the FHIR SDC Template based extraction logic
https://jira.hl7.org/browse/FHIR-39498
## Change Request Summary
I'd like to propose putting templates of resources into a Questionnaire.contained list that can be completed by items in the questionnaire.
For instance, if you're taking a history from a patient and answer that they have high blood pressure. Or an SDOH survey and the patient answers they have food insecurity. There would be a condition for high blood pressure or food insecurity predefined. And then the only fields that would need to be completed by the Questionnaire would be the date, and the patient reference.
Note that unlike the other approaches, this approach cannot be used to update an existing instance.
## Initial Proposal Discussed *(in tracker)*
Define the following extensions:
### extractToTemplate
| Property | Value |
| - | - |
| Extension URL | http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractToTemplate |
| Context | Questionnaire \| Questionnaire.item |
| Cardinality | 0..* |
| Value Type | id |
| Definition | Indicates the contained resource that will act as a template for extraction from this point |
| Comment | Each occurrence within the QuestionnaireResponse that links to this level in the QuestionnaireResponse will produce an instantiation of the referenced template |
> Brian: The value type should be a **Reference**, as this needs to actually link to the contained resource in order for the resource to be valid dom-3.
> *Logged tracker http://jira.hl7.org/browse/FHIR-43690*
> It will then also permit the template to be shared under some conditions.
> **Note:** If the resource in the template needs to have contained resources, these will also need to be in the questionnaire too, and be moved during the extraction.
### extractContext
| Property | Value |
| - | - |
| Extension URL | http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractContext |
| Context | Questionnaire.item |
| Cardinality | 0..* |
| Value Type | string |
| Definition | linkId within the Questionnaire to establish as a local context for this element. This element will appear based on the number of repetitions of the linkId within the parent context. |
| Comment | |
> Brian: Expression datatype? - this would then support other languages than just fhirpath
> Brian: The extractContext's context should be **element** as noted in the process description
### extractValue
| Property | Value |
| - | - |
| Extension URL | http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractValue |
| Context | element |
| Cardinality | 0..* |
| Value Type | string |
| Definition | fhirPath expression that determines the content of this element within the template |
| Comment | |
> Brian: The cardinality should be 0..1, or provide what it would mean to have more than one expression providing data.
> Brian: Expression datatype? - this would then support other languages than just fhirpath
## Process:
While performing a QuestionnaireResponse $extract (using the template approach) any instances of the extractToTemplate found in the Questionnaire (at the root or on any item) will result in the creation of a new instance of that template resource.
scanning a QuestionnaireResponse to perform a template based extraction each item in the QR is scanned to locate any templates defined in the Questionnaire at the root or on any item (defined by the extractToTemplate extension).
* The location of the extractToTemplate extension in the Questionnaire/item establishes a 'context' of the node within the QuestionnaireResponse. All variables available at that point and all FHIRPath navigation to child elements from the containing node of the extension would be available when evaluating FHIRPath expressions inside the 'template' contained resource
* Within a template, an extractContext extension can appear on an element. If so, then that element will repeat once for each occurrence of the specified path and will change the context node in terms of relative paths for descendant expressions as well as what variables are available
* Element values that are to be taken from QuestionnaireResponse content will use the valueExpression extension within the element to be extracted. This could be a simple type (e.g. string) or a complex type (e.g. Address)
* Instantiate all of the templates and put them into a transaction, then execute the transaction. (The transaction will allow cross-references between the created resources)
---
## Test Resource
*(very much in progress)*
``` json
{
"resourceType": "Questionnaire",
"id": "extract-using-template",
"contained": [
{
"resourceType": "Observation",
"id": "obs-template",
"status": "final",
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "9269-2",
"display": "Glasgow coma score total"
}
],
"text": "Glasgow Coma Scale, (GCS)"
},
"subject": {
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractValue",
"valueString": "QuestionnaireResponse.subject"
}
],
"reference": "REPLACE WITH TEMPLATED CONTENT",
"display": "REPLACE WITH TEMPLATED CONTENT"
},
"_effectiveDateTime": {
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractValue",
"valueString": "QuestionnaireResponse.authored"
}
]
},
"effectiveDateTime": "REPLACED",
"valueQuantity": {
"_value": {
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractValue",
"valueString": "QuestionnaireResponse.item.where(linkId = 'g1').item.where(linkId = 'q1').answer.value)"
}
]
},
"value": 13,
"system": "http://unitsofmeasure.org",
"code": "{score}"
},
"referenceRange": [
{
"high": {
"value": 8,
"system": "http://unitsofmeasure.org",
"code": "{score}"
},
"type": {
"text": "Severe TBI"
}
},
{
"low": {
"value": 9,
"system": "http://unitsofmeasure.org",
"code": "{score}"
},
"high": {
"value": 12,
"system": "http://unitsofmeasure.org",
"code": "{score}"
},
"type": {
"text": "Moderate TBI"
}
},
{
"low": {
"value": 13,
"system": "http://unitsofmeasure.org",
"code": "{score}"
},
"type": {
"text": "Mild TBI"
}
}
],
"component": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "9268-4",
"display": "Glasgow coma score motor"
}
],
"text": "GCS Motor"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://acme.ec/codes",
"code": "5",
"display": "Localizes painful stimuli"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/ordinalValue",
"valueDecimal": 5
}
],
"system": "http://loinc.org",
"code": "LA6566-9",
"display": "Localizing pain"
}
],
"text": "5 (Localizes painful stimuli)"
}
},
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "9270-0",
"display": "Glasgow coma score verbal"
}
],
"text": "GSC Verbal"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://acme.ec/codes",
"code": "4",
"display": "Confused, disoriented"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/ordinalValue",
"valueDecimal": 4
}
],
"system": "http://loinc.org",
"code": "LA6560-2",
"display": "Confused"
}
],
"text": "4 (Confused, disoriented)"
}
},
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "9267-6",
"display": "Glasgow coma score eye opening"
}
],
"text": "Eyes"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://acme.ec/codes",
"code": "4",
"display": "Opens eyes spontaneously"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/ordinalValue",
"valueDecimal": 4
}
],
"system": "http://loinc.org",
"code": "LA6556-0",
"display": "Eyes open spontaneously"
}
],
"text": "4 (Opens eyes spontaneously)"
}
}
]
}
],
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractToTemplate",
"valueReference": {
"reference": "#obs-template"
}
},
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext",
"extension": [
{
"url": "name",
"valueCoding": {
"code": "ips"
}
},
{
"url": "type",
"valueCode": "Bundle"
}
]
},
{
"url": "http://hl7.org/fhir/StructureDefinition/variable",
"valueExpression": {
"name": "pat",
"language": "text/fhirpath",
"expression": "%ips.entry.resource.ofType(Patient)"
}
}
],
"url": "http://fhir.forms-lab.com/Questionnaire/extract-using-template",
"title": "Template based extraction Demo",
"status": "draft",
"publisher": "Brian Postlethwaite",
"item": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/variable",
"valueExpression": {
"name": "patname",
"language": "text/fhirpath",
"expression": "%pat.name.first()"
}
}
],
"linkId": "pat-details",
"text": "Patient Details",
"type": "group",
"item": [
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%patname.select(text | (family | (given | prefix).join(' ')).join(', ')).first()"
}
}
],
"linkId": "name",
"text": "Full Name",
"type": "string",
"item": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
"valueCodeableConcept": {
"coding": [
{
"system": "http://hl7.org/fhir/questionnaire-item-control",
"code": "flyover"
}
]
}
}
],
"linkId": "display-help",
"text": "tooltip stuff",
"type": "display"
}
]
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%patname.family"
}
}
],
"linkId": "family",
"text": "Family Name",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%patname.given.first()"
}
}
],
"linkId": "given",
"text": "Given Name",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%patname.given.skip(1).join(' ')"
}
}
],
"linkId": "middle-name",
"text": "Middle Name(s)",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%patname.prefix.join(' ')"
}
}
],
"linkId": "title",
"text": "Title",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%pat.name.where(use = 'usual').first().select(text | (family | (given | prefix).join(' ')).join(', ')).first()"
}
}
],
"linkId": "prefName",
"text": "Preferred Name",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%pat.birthDate"
}
}
],
"linkId": "DOB",
"text": "Birth Date",
"type": "date"
}
]
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext",
"valueExpression": {
"name": "medication",
"language": "text/fhirpath",
"expression": "%ips.entry.resource.ofType(MedicationStatement) | %ips.entry.resource.ofType(MedicationRequest)"
}
},
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
"valueCodeableConcept": {
"coding": [
{
"system": "http://hl7.org/fhir/questionnaire-item-control",
"code": "gtable"
}
]
}
}
],
"linkId": "meds",
"text": "Medications",
"type": "group",
"repeats": true,
"item": [
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "(%medication.medication.text | %medication.medication.display | %medication.medication.coding.display).first()"
}
}
],
"linkId": "med-name",
"text": "Medication",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%medication.status.toString()"
}
}
],
"linkId": "med-status",
"text": "Status",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%medication.dosageInstruction.text"
}
}
],
"linkId": "med-dose",
"text": "Dose",
"type": "string"
}
]
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext",
"valueExpression": {
"name": "allergy",
"language": "text/fhirpath",
"expression": "%ips.entry.resource.ofType(AllergyIntolerance)"
}
},
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
"valueCodeableConcept": {
"coding": [
{
"system": "http://hl7.org/fhir/questionnaire-item-control",
"code": "gtable"
}
]
}
}
],
"linkId": "allergies",
"text": "Allergies / Adverse Reactions / Food Intolerances",
"type": "group",
"repeats": true,
"item": [
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%allergy.code.select((text | coding.display).first()) & ' (' & %allergy.category.join(' ') & ')'"
}
}
],
"linkId": "allergy-name",
"text": "Name (of food/medication)",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%allergy.clinicalStatus.coding.code.toString()"
}
}
],
"linkId": "allergy-status",
"text": "Status",
"type": "string"
},
{
"extension": [
{
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
"valueExpression": {
"language": "text/fhirpath",
"expression": "%allergy.reaction.manifestation.select((text | coding.display).first()).join('\\r\\n')"
}
}
],
"linkId": "allergy-reaction",
"text": "Reaction (provide details of what happened and when)",
"type": "text"
}
]
}
]
}
```