This page is part of the Structured Data Capture FHIR IG (v2.5.0: STU 3 Ballot 1) based on FHIR v3.5.0. The current version which supercedes this version is 3.0.0. For a full list of available versions, see the Directory of published versions
Questionnaires are excellent tools for data capture. They allow tight control over what data is gathered and ensure information is gathered consistently across multiple users. However, data gathered using different questionnaires - or even different versions of the same questionnaire - is often not comparible. It is also not very searchable or easily integrated with discrete data sources. Because of this, the general recommendation in FHIR is to use questionnaires for raw data capture but then to convert the resulting QuestionnaireResponse instances into other FHIR resources - Observations, MedicationStatements, FamilyMemberHistories, etc. This allows the data gathered to then be easily combined with other data into FHIR documents and messages and exposed over FHIR REST interfaces.
Such conversion can be done with custom code written on a Questionnaire by Questionnaire basis. However, it makes the process much easier if it's possible to write generic software that can convert any arbitrary QuestionnaireResponse into appropriate FHIR resources leveraging metadata embedded in the Questionnaire. This portion of the SDC guide defines mechanisms for doing so.
entity
reference of type 'source' that points back to the original QuestionnaireResponse. If Observations are generated, they can also
have an explicit derivedFrom
link pointing back to the QuestionnaireRsponseid
of the
resource must be retained through the round-trip process to allow for the update. This can be supported by storing the id as a
hidden readOnly question not shown to the user.Questionnaire.derivedFrom
relationship to the same canonical URL the QuestionnaireResponse refers to. The derived Questionnaire would contain the same content as the
base Questionnaire, but would have additional extensions inserted to support data extraction.Like Questionnaire population, extracting data from a QuestionnaireResponse is a complex process involving querying existing FHIR data and using more advanced technologies such as FHIRPath and StructureMap. It's therefore a function that systems may also wish to offload to a separate system. The Questionnaire extract has been created for this purpose. It takes in a completed QuestionnaireResponse and returns either an individual FHIR resource or a Bundle of resources, depending on the type of Questionnaire. The operation doesn't actually post the created resources to a server. It's up to the client system to determine what action(s) to take with the created content.
NOTE: It's the responsibility of the client system to ensure that any generated resources are valid against necessary profiles, etc. before using content produced by this operation.
This specification defines three different mechanisms to embed information in Questionnaires to support subsequent resource extraction:
Systems are free to experiment with other extraction mechanisms but cannot expect support for those from other SDC-conformant systems.
The sdc-questionnaire-extract profile includes the data elements and extensions relevant for supporting all of these mechanisms. None of the additional resource elements or extensions are marked as "mustSupport" because there is no expectation that systems will support all of (or any of) the different extraction mechanisms. Instead, each system should choose which approach(es) it wishes to use and support the elements described in that section of the implementation guide.
This is the simplest of the extraction mechanisms. It leverages the same data elements as are used for the Observation-based population
mechanism. It takes advantage of the fact that the vast majority of questions in the healthcare space typically correspond to the
value
element of an Observation. It also takes advantage of the Questionnaire.item.code
element that identifies what a concept each question or
group corresponds to.
To use this method:
item.code
element on each question to be populated. Typically this will be a LOINC code, but in some jurisdictions/environments, SNOMED CT or
other codes may be relevantitem.code
present - this might represent the code of the a panel or the Observation.code of an Observation with no value but with
multiple Observation.component
elements. Child question items can then assert the item.code
of the "member-of" Observations or the
Observation.component.code
valuesitem.code
elements might be present. If so, each are considered to be one of the Observation.code codings in the resulting extracted Observation.For example:
<item>
<extension url="http://hl7.org/fhir/StructureDefinition/questionnaire-observationLinkPeriod">
<valueDuration>
<value value="3"/>
<system value="http://unitsofmeasure.org"/>
<code value="mo"/>
</valueDuration>
</extension>
<linkId value="code-pop-demo"/>
<code>
<system value="http://loinc.org"/>
<code value="29463-7"/>
<display value="Body weight"/>
</code>
<code>
<system value="http://loinc.org"/>
<code value="3141-9"/>
<display value="Body weight Measured"/>
</code>
<code>
<system value="http://loinc.org"/>
<code value="8341-0"/>
<display value="Dry body weight Measured"/>
</code>
<text value="What is your current weight?"/>
<type value="quantity"/>
<answerOption>
<valueCoding>
<system value="http://unitsofmeasure.org"/>
<code value="kg"/>
</valueCoding>
</answerOption>
<answerOption>
<valueCoding>
<system value="http://unitsofmeasure.org"/>
<code value="[lb_av]"/>
</valueCoding>
</answerOption>
</item>
When performing the extraction process, the system will create a batch that will contain creates or updates of Observation instances. It will go through the QuestionnaireResponse and identify all answers for which the corresponding Questionnaire item has a questionnaire-observationLinkPeriod extension. For each of those it will then determine whether to create a new observation, update an existing observation or do nothing. Guidelines for making this decision are as follows:
Update if: |
|
---|---|
Take no action if: |
|
Create a new Observation if: | the conditions in the preceding two rows are not met |
If updating, simply adjust the original Observation to have the new value and change the status to "amended", then PUT it to the source system. If creating, data elements should be populated as follows:
Observation.basedOn
and Observation.partOf
- copy from QuestionnaireResponse elements of the same nameObservation.status
- set to 'final'Observation.category
- if this can be inferred from any of the Questionnaire.item.code
values or from known context of the Questionnaire itself, then fill it in,
otherwise ommit.Observation.code
- add all of the Questionnaire.item.code
values as Observation.code.coding
instancesObservation.subject
- set to QuestionnaireResponse.subject
Observation.encounter
- set to QuestionnaireResponse.context
(if an Encounter)Observation.effectiveDateTime
- set to QuestionnaireResponse.authored
.Observation.issued
- set to QuestionnaireResponse.authored
Observation.performer
- set to QuestionnaireResponse.author
Observation.value[x]
- set to QuestionnaireResponse.item.answer.value[x]
Observation.derivedFrom
- set to a reference to the QuestionnaireResponseObservation.interpretation
and Observation.referenceRange
- if these can be inferred from the QuestionnaireResponse.item.code
(and for interpretation the
answer value too), they can be populated, otherwise omit
If the Questionnaire.item that is linked to an Observation contains child items that are also linked to Observations, then things get more complex as a determination will need to be made on whether
to link the parent to child as Observation.component
or as Observation.hasMember
. In the ideal situation, the system will recognize the Observation.item.code and know which
approach is correct for that type of Observation. If not, then the system could query for other records of the child type and see if they appear as components anywhere. If unsure, systems
should use "hasMember".
Considerations when using this approach:
Observation.focus
is relevant or for capturing Observation.dataAbsentReason
Patient.birthDate
), systems MAY take advantage of this knowledge to
update the value of resources other than Observations, however such use is discouraged - using one of the other extraction techniques is likely better and safer.This approach to population is more generic. It supports extracting data into any type of FHIR resource rather than being limited to only Observation. It also supports more Observation data elements than can be gathered using the Observation element - for example, explicit effective time ranges, interpretations, comments, etc.
To use this method:
Questionnaire.definition
to point to the resource or profile element that Questionnaire item corresponds to. (Profiles may be relevant for
data that is sliced or has fixed values for some properties.). The definition SHALL have the full canonical URL of the resource (or profile) followed by '#' followed by the snapshot.path of the element
the Questionnaire item corresponds to.Questionnaire.initialValue[x]
or that use the
questionnaire-initialExpression extension to define their content to use to populate resource elements that the
user will not be filling in. (The initialExpressions might in turn depend on variable and
questionnaire-context extensions, used as described in the Path-based population
section.To perform the population process, first determine whether the context resource should be updated or created:
If updating, the answer values from the questionnaire (including hidden, calculated answers) will be propagated to the context resource. If creating, then for each occurrence of each item asserting a questionnaire-context, a resource of the context type will be defined. It will be populated with answers to questions (hidden or not) that have a definition that matches the resource type of the context. As well, if the definitions refer to a profile, any child elements of the profile that have fixed values or patterns declared will also be included in the instance with values matching the fixed or pattern values.
An example of a questionnaire using this approach can be found here.
Considerations when using this approach:
The StructureMap approach is the most sophisticated approach of the three - and the most powerful. It allows significant transformation of data, including code translations when generating output resources. It also allows the conversion process between data and Questionnaire to be maintained independently and to draw on shared sources across Questionnaires. This can be an advantage in certain environments where the content of the questionnaire may need tight control but the data environment can be more dynamic. This comes at the cost of requiring expertise in the FHIR mapping language, which is not (yet?) a common skill.
To use this method:
To extract data from the completed QuestionnaireResponse, simply invoke execute the StructureMap on it. A sample Questionnaire and associated StructureMap that shows this approach can be found here.
Considerations when using this approach: