From d3fd1a8d37bc454b53283675ab4abcb7c2685ede Mon Sep 17 00:00:00 2001 From: Sean Fong Date: Tue, 23 Jul 2024 17:13:29 +0930 Subject: [PATCH 1/3] Fix initial calculatedExpression pre-fill --- .../assets/questionnaires/QPrePopTester.ts | 337 ++++++++++++++---- .../src/utils/calculatedExpression.ts | 2 +- .../src/utils/fhirpath.ts | 5 - 3 files changed, 267 insertions(+), 77 deletions(-) diff --git a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QPrePopTester.ts b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QPrePopTester.ts index 8d83be3f..0a8b6378 100644 --- a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QPrePopTester.ts +++ b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QPrePopTester.ts @@ -183,108 +183,246 @@ export const qSelectivePrePopTester: Questionnaire = { valueString: 'The patient that is to be used to pre-populate the form' } ] + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/variable', + valueExpression: { + name: 'ObsTobaccoSmokingStatus', + language: 'application/x-fhir-query', + expression: 'Observation?code=72166-2&_count=1&_sort=-date&patient={{%patient.id}}' + } } ], item: [ { - linkId: 'display-description', + linkId: 'container', extension: [ { - url: 'http://hl7.org/fhir/StructureDefinition/rendering-xhtml', - valueString: - '
\r\n
This questionnaire is used by Playwright to do regression testing of the
@aehrc/sdc-populate
library\'s pre-population logic. Items will be incrementally added as needed.

' - } - ], - text: "This questionnaire is used by Playwright to do regression testing of the @aehrc/sdc-populate library's pre-population logic. Items will be incrementally added as needed.", - type: 'display', - repeats: false - }, - { - extension: [ - { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + url: 'http://hl7.org/fhir/StructureDefinition/variable', valueExpression: { + name: 'sex', language: 'text/fhirpath', - expression: '%patient.gender' + expression: "item.where(linkId='sex-at-birth-initial-expression').answer.value" } - } - ], - linkId: 'gender-avs-url', - text: 'Administrative gender (answerValueSet url)', - type: 'choice', - repeats: false, - answerValueSet: 'http://hl7.org/fhir/ValueSet/administrative-gender' - }, - { - extension: [ + }, { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + url: 'http://hl7.org/fhir/StructureDefinition/variable', valueExpression: { + name: 'smoker', language: 'text/fhirpath', - expression: '%patient.gender' + expression: "item.where(linkId='smoking-status-initial-expression').answer.value" } } ], - linkId: 'gender-avs-contained', - text: 'Administrative gender (answerValueSet contained)', - type: 'choice', - repeats: false, - answerValueSet: 'http://hl7.org/fhir/ValueSet/administrative-gender' - }, - { - extension: [ + text: '', + type: 'group', + item: [ { - url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', - valueCodeableConcept: { - coding: [ - { - system: 'http://hl7.org/fhir/questionnaire-item-control', - code: 'gtable' + linkId: 'display-description', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/rendering-xhtml', + valueString: + '
\r\n
This questionnaire is used by Playwright to do regression testing of the
@aehrc/sdc-populate
library\'s pre-population logic. Items will be incrementally added as needed.

' + } + ], + text: "This questionnaire is used by Playwright to do regression testing of the @aehrc/sdc-populate library's pre-population logic. Items will be incrementally added as needed.", + type: 'display', + repeats: false + }, + { + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: '%patient.gender' } - ] - } + } + ], + linkId: 'gender-avs-url', + text: 'Administrative gender (answerValueSet url)', + type: 'choice', + repeats: false, + answerValueSet: 'http://hl7.org/fhir/ValueSet/administrative-gender' }, { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext', - valueExpression: { - name: 'ConditionRepeat', - language: 'text/fhirpath', - expression: - "%Condition.entry.resource.where(category.coding.exists(code='problem-list-item'))" - } - } - ], - linkId: 'medical-history', - text: 'Medical history and current problems list', - type: 'group', - repeats: true, - item: [ + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: '%patient.gender' + } + } + ], + linkId: 'gender-avs-contained', + text: 'Administrative gender (answerValueSet contained)', + type: 'choice', + repeats: false, + answerValueSet: 'http://hl7.org/fhir/ValueSet/administrative-gender' + }, { extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: + "%patient.extension.where(exists(url='http://hl7.org/fhir/StructureDefinition/individual-recordedSexOrGender' and extension.where(exists(url='type' and value.coding.code='1515311000168102')) and extension.where(url='effectivePeriod').value.end.empty())).extension.where(url='value').value.coding" + } + }, { url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', valueCodeableConcept: { coding: [ { system: 'http://hl7.org/fhir/questionnaire-item-control', - code: 'autocomplete' + code: 'drop-down' } ] } - }, + } + ], + linkId: 'sex-at-birth-initial-expression', + text: 'Sex assigned at birth (initialExpression)', + type: 'choice', + repeats: false, + answerValueSet: 'https://healthterminologies.gov.au/fhir/ValueSet/biological-sex-1' + }, + { + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', + valueExpression: { + description: 'Calculated Sex At Birth', + language: 'text/fhirpath', + expression: '%sex' + } + } + ], + linkId: 'sex-at-birth-calculated', + text: 'Sex assigned at birth (calculatedExpression)', + type: 'choice', + repeats: false, + readOnly: true, + answerValueSet: 'https://healthterminologies.gov.au/fhir/ValueSet/biological-sex-1' + }, + { + extension: [ { url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', valueExpression: { language: 'text/fhirpath', expression: - "%ConditionRepeat.code.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first())" + "%ObsTobaccoSmokingStatus.entry.resource.value.coding.where(system='http://snomed.info/sct')" + } + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'radio-button' + } + ] } } ], - linkId: 'medical-history-condition', - text: 'Condition', - type: 'open-choice', - answerValueSet: '#MedicalHistory' + linkId: 'smoking-status-initial-expression', + text: 'Smoking status (initialExpression)', + type: 'choice', + repeats: false, + answerOption: [ + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '266919005', + display: 'Never smoked' + } + }, + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '77176002', + display: 'Smoker' + } + }, + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '8517006', + display: 'Ex-smoker' + } + }, + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '16090371000119103', + display: 'Exposure to second hand tobacco smoke' + } + }, + { + valueString: 'Wants to quit' + }, + { + valueString: 'Other tobacco use' + } + ] + }, + { + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', + valueExpression: { + description: 'Calculated Smoking Status', + language: 'text/fhirpath', + expression: '%smoker' + } + } + ], + linkId: 'smoking-status-calculated', + text: 'Smoking status (calculatedExpression)', + type: 'choice', + repeats: false, + readOnly: true, + answerOption: [ + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '266919005', + display: 'Never smoked' + } + }, + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '77176002', + display: 'Smoker' + } + }, + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '8517006', + display: 'Ex-smoker' + } + }, + { + valueCoding: { + system: 'http://snomed.info/sct', + code: '16090371000119103', + display: 'Exposure to second hand tobacco smoke' + } + }, + { + valueString: 'Wants to quit' + }, + { + valueString: 'Other tobacco use' + } + ] }, { extension: [ @@ -294,23 +432,80 @@ export const qSelectivePrePopTester: Questionnaire = { coding: [ { system: 'http://hl7.org/fhir/questionnaire-item-control', - code: 'drop-down' + code: 'gtable' } ] } }, { - url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext', valueExpression: { + name: 'ConditionRepeat', language: 'text/fhirpath', - expression: '%ConditionRepeat.clinicalStatus.coding' + expression: + "%Condition.entry.resource.where(category.coding.exists(code='problem-list-item'))" } } ], - linkId: 'medical-history-status', - text: 'Clinical Status', - type: 'choice', - answerValueSet: 'http://hl7.org/fhir/ValueSet/condition-clinical' + linkId: 'medical-history', + text: 'Medical history and current problems list', + type: 'group', + repeats: true, + item: [ + { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'autocomplete' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: + "%ConditionRepeat.code.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first())" + } + } + ], + linkId: 'medical-history-condition', + text: 'Condition', + type: 'open-choice', + answerValueSet: '#MedicalHistory' + }, + { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'drop-down' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression', + valueExpression: { + language: 'text/fhirpath', + expression: '%ConditionRepeat.clinicalStatus.coding' + } + } + ], + linkId: 'medical-history-status', + text: 'Clinical Status', + type: 'choice', + answerValueSet: 'http://hl7.org/fhir/ValueSet/condition-clinical' + } + ] } ] } diff --git a/packages/smart-forms-renderer/src/utils/calculatedExpression.ts b/packages/smart-forms-renderer/src/utils/calculatedExpression.ts index 43a5aaf5..2686a608 100644 --- a/packages/smart-forms-renderer/src/utils/calculatedExpression.ts +++ b/packages/smart-forms-renderer/src/utils/calculatedExpression.ts @@ -224,7 +224,7 @@ function initialiseItemCalculatedExpressionValueRecursive( updateQrItemsInGroup( updatedChildQRItem, null, - updatedChildQRItem ?? { linkId: qItem.linkId, text: qItem.text, item: [] }, + qrItem ?? { linkId: qItem.linkId, text: qItem.text, item: [] }, indexMap ); } diff --git a/packages/smart-forms-renderer/src/utils/fhirpath.ts b/packages/smart-forms-renderer/src/utils/fhirpath.ts index a27e7f65..18454fcc 100644 --- a/packages/smart-forms-renderer/src/utils/fhirpath.ts +++ b/packages/smart-forms-renderer/src/utils/fhirpath.ts @@ -101,11 +101,6 @@ export function createFhirPathContext( rootResource: questionnaireResponse }; - // Exit early if there are no QR items - if (!questionnaireResponse.item) { - return fhirPathContext; - } - // Evaluate resource-level variables fhirPathContext = evaluateQuestionnaireLevelVariables( questionnaireResponse, From 8ccdd2d847826a170afcf5b7424fa903b87cc13e Mon Sep 17 00:00:00 2001 From: Sean Fong Date: Tue, 23 Jul 2024 17:14:21 +0930 Subject: [PATCH 2/3] Add support for Coding object in calculatedExpressions for choice and open-choice items --- .../src/hooks/useCodingCalculatedExpression.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/smart-forms-renderer/src/hooks/useCodingCalculatedExpression.ts b/packages/smart-forms-renderer/src/hooks/useCodingCalculatedExpression.ts index 565d7f10..f8cce519 100644 --- a/packages/smart-forms-renderer/src/hooks/useCodingCalculatedExpression.ts +++ b/packages/smart-forms-renderer/src/hooks/useCodingCalculatedExpression.ts @@ -16,7 +16,7 @@ */ import { useEffect, useState } from 'react'; -import type { QuestionnaireItem } from 'fhir/r4'; +import type { Coding, QuestionnaireItem } from 'fhir/r4'; import { useQuestionnaireStore } from '../stores'; interface UseCodingCalculatedExpression { @@ -56,6 +56,7 @@ function useCodingCalculatedExpression( calcExpression.value !== valueInString && (typeof calcExpression.value === 'string' || typeof calcExpression.value === 'number' || + typeof calcExpression.value === 'object' || calcExpression.value === null) ) { // update ui to show calculated value changes @@ -70,6 +71,14 @@ function useCodingCalculatedExpression( return; } + // calculatedExpression value is object, check if it is a Coding object + if (typeof calcExpression.value === 'object' && objectIsCoding(calcExpression.value)) { + if (calcExpression.value.code) { + onChangeByCalcExpressionString(calcExpression.value.code); + return; + } + } + // calculatedExpression value is a string or number const newValueString = typeof calcExpression.value === 'string' @@ -87,4 +96,8 @@ function useCodingCalculatedExpression( return { calcExpUpdated: calcExpUpdated }; } +function objectIsCoding(obj: any): obj is Coding { + return obj && obj.code && typeof obj.code === 'string'; +} + export default useCodingCalculatedExpression; From c80e4cdd6d685b10913f1d60759c4c6432fbd618 Mon Sep 17 00:00:00 2001 From: Sean Fong Date: Tue, 23 Jul 2024 17:17:01 +0930 Subject: [PATCH 3/3] Update package versions --- apps/smart-forms-app/package.json | 2 +- documentation/package.json | 2 +- package-lock.json | 6 +++--- packages/smart-forms-renderer/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json index babc5d30..20c02cd7 100644 --- a/apps/smart-forms-app/package.json +++ b/apps/smart-forms-app/package.json @@ -28,7 +28,7 @@ "dependencies": { "@aehrc/sdc-assemble": "^1.3.1", "@aehrc/sdc-populate": "^2.3.0", - "@aehrc/smart-forms-renderer": "^0.36.0", + "@aehrc/smart-forms-renderer": "^0.36.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/material-icons": "^5.0.18", diff --git a/documentation/package.json b/documentation/package.json index 28de0adb..6c25e08f 100644 --- a/documentation/package.json +++ b/documentation/package.json @@ -15,7 +15,7 @@ "typecheck": "tsc" }, "dependencies": { - "@aehrc/smart-forms-renderer": "^0.36.0", + "@aehrc/smart-forms-renderer": "^0.36.1", "@docusaurus/core": "^3.4.0", "@docusaurus/preset-classic": "^3.4.0", "@docusaurus/theme-live-codeblock": "^3.4.0", diff --git a/package-lock.json b/package-lock.json index ac17ca36..07475c40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "dependencies": { "@aehrc/sdc-assemble": "^1.3.1", "@aehrc/sdc-populate": "^2.3.0", - "@aehrc/smart-forms-renderer": "^0.36.0", + "@aehrc/smart-forms-renderer": "^0.36.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/material-icons": "^5.0.18", @@ -440,7 +440,7 @@ "name": "@aehrc/smart-forms-documentation", "version": "0.0.0", "dependencies": { - "@aehrc/smart-forms-renderer": "^0.36.0", + "@aehrc/smart-forms-renderer": "^0.36.1", "@docusaurus/core": "^3.4.0", "@docusaurus/preset-classic": "^3.4.0", "@docusaurus/theme-live-codeblock": "^3.4.0", @@ -41666,7 +41666,7 @@ }, "packages/smart-forms-renderer": { "name": "@aehrc/smart-forms-renderer", - "version": "0.36.0", + "version": "0.36.1", "license": "Apache-2.0", "dependencies": { "@aehrc/sdc-populate": "^2.3.0", diff --git a/packages/smart-forms-renderer/package.json b/packages/smart-forms-renderer/package.json index 4509addd..a1aab1c4 100644 --- a/packages/smart-forms-renderer/package.json +++ b/packages/smart-forms-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@aehrc/smart-forms-renderer", - "version": "0.36.0", + "version": "0.36.1", "description": "FHIR Structured Data Captured (SDC) rendering engine for Smart Forms", "main": "lib/index.js", "scripts": {