From d50cfd92b8f34dd921dca818b1b6639183102a2f Mon Sep 17 00:00:00 2001 From: Alex Lipovka Date: Wed, 17 Jun 2026 15:08:13 +0400 Subject: [PATCH] add support for printable header and footer extensions --- src/converter/__tests__/fce.test.ts | 4 +++ .../questionnaire_fce/printable_header.json | 19 +++++++++++ .../questionnaire_fhir/printable_header.json | 24 ++++++++++++++ .../questionnaire/processExtensions.ts | 16 ++++++++++ .../questionnaire/processExtensions.ts | 32 ++++++++++++++++++- src/fce.types.ts | 4 +++ 6 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/converter/__tests__/resources/questionnaire_fce/printable_header.json create mode 100644 src/converter/__tests__/resources/questionnaire_fhir/printable_header.json diff --git a/src/converter/__tests__/fce.test.ts b/src/converter/__tests__/fce.test.ts index 280f67d..6a3cba4 100644 --- a/src/converter/__tests__/fce.test.ts +++ b/src/converter/__tests__/fce.test.ts @@ -36,6 +36,7 @@ import fce_practitioner_create from './resources/questionnaire_fce/practitioner_ import fce_practitioner_create_structure_map from './resources/questionnaire_fce/practitioner_create_structure_map.json'; import fce_practitioner_edit from './resources/questionnaire_fce/practitioner_edit.json'; import fce_practitioner_role_create from './resources/questionnaire_fce/practitioner_role_create.json'; +import fce_printable_header from './resources/questionnaire_fce/printable_header.json'; import fce_public_appointment from './resources/questionnaire_fce/public_appointment.json'; import fce_questionnaire_variable from './resources/questionnaire_fce/questionnaire_variable.json'; import fce_review_of_systems from './resources/questionnaire_fce/review_of_systems.json'; @@ -81,6 +82,7 @@ import fhir_practitioner_create from './resources/questionnaire_fhir/practitione import fhir_practitioner_create_structure_map from './resources/questionnaire_fhir/practitioner_create_structure_map.json'; import fhir_practitioner_edit from './resources/questionnaire_fhir/practitioner_edit.json'; import fhir_practitioner_role_create from './resources/questionnaire_fhir/practitioner_role_create.json'; +import fhir_printable_header from './resources/questionnaire_fhir/printable_header.json'; import fhir_public_appointment from './resources/questionnaire_fhir/public_appointment.json'; import fhir_questionnaire_variable from './resources/questionnaire_fhir/questionnaire_variable.json'; import fhir_review_of_systems from './resources/questionnaire_fhir/review_of_systems.json'; @@ -147,6 +149,7 @@ describe('Questionanire and QuestionnaireResponses transformation', () => { ['chart-highlight', fhir_chart_highlight, fce_chart_highlight], ['column-width', fhir_column_width, fce_column_width], ['mapping-inline', fhir_mapping_inline, fce_mapping_inline], + ['printable-header', fhir_printable_header, fce_printable_header], ])('Each FHIR Questionnaire should convert to FCE %s', async (_, fhir_questionnaire, fce_questionnaire) => { expect(toFirstClassExtension(fhir_questionnaire as FHIRQuestionnaire)).toStrictEqual(fce_questionnaire); }); @@ -199,6 +202,7 @@ describe('Questionanire and QuestionnaireResponses transformation', () => { ['chart-highlight', fce_chart_highlight, fhir_chart_highlight], ['column-width', fce_column_width, fhir_column_width], ['mapping-inline', fce_mapping_inline, fhir_mapping_inline], + ['printable-header', fce_printable_header, fhir_printable_header], ])('Each FCE Questionnaire should convert to FHIR %s', async (_, fce_questionnaire, fhir_questionnaire) => { expect(sortExtensionsList(fromFirstClassExtension(fce_questionnaire as FCEQuestionnaire))).toStrictEqual( sortExtensionsList(fhir_questionnaire), diff --git a/src/converter/__tests__/resources/questionnaire_fce/printable_header.json b/src/converter/__tests__/resources/questionnaire_fce/printable_header.json new file mode 100644 index 0000000..111acaf --- /dev/null +++ b/src/converter/__tests__/resources/questionnaire_fce/printable_header.json @@ -0,0 +1,19 @@ +{ + "resourceType": "Questionnaire", + "status": "active", + "meta": { + "profile": ["https://emr-core.beda.software/StructureDefinition/fhir-emr-questionnaire"] + }, + "printableHeader": { + "contentType": "application/pdf", + "data": "base64-encoded-pdf-data", + "url": "https://example.com/printable-header.pdf" + }, + "item": [ + { + "linkId": "title", + "type": "string", + "text": "Title" + } + ] +} diff --git a/src/converter/__tests__/resources/questionnaire_fhir/printable_header.json b/src/converter/__tests__/resources/questionnaire_fhir/printable_header.json new file mode 100644 index 0000000..8f53b3f --- /dev/null +++ b/src/converter/__tests__/resources/questionnaire_fhir/printable_header.json @@ -0,0 +1,24 @@ +{ + "resourceType": "Questionnaire", + "status": "active", + "meta": { + "profile": ["https://emr-core.beda.software/StructureDefinition/fhir-emr-questionnaire"] + }, + "extension": [ + { + "url": "https://emr-core.beda.software/StructureDefinition/printable-header", + "valueAttachment": { + "contentType": "application/pdf", + "data": "base64-encoded-pdf-data", + "url": "https://example.com/printable-header.pdf" + } + } + ], + "item": [ + { + "linkId": "title", + "type": "string", + "text": "Title" + } + ] +} diff --git a/src/converter/fceToFhir/questionnaire/processExtensions.ts b/src/converter/fceToFhir/questionnaire/processExtensions.ts index 3fd0e3e..a7d7579 100644 --- a/src/converter/fceToFhir/questionnaire/processExtensions.ts +++ b/src/converter/fceToFhir/questionnaire/processExtensions.ts @@ -11,6 +11,8 @@ export function processExtensions(questionnaire: FCEQuestionnaire): FHIRQuestion assembleContext, itemPopulationContext, variable, + printableHeader, + printableFooter, ...fhirQuestionnaire } = questionnaire; @@ -117,6 +119,20 @@ export function processExtensions(questionnaire: FCEQuestionnaire): FHIRQuestion ); } + if (printableHeader) { + extensions.push({ + url: 'https://emr-core.beda.software/StructureDefinition/printable-header', + valueAttachment: printableHeader, + }); + } + + if (printableFooter) { + extensions.push({ + url: 'https://emr-core.beda.software/StructureDefinition/printable-footer', + valueAttachment: printableFooter, + }); + } + if (extensions.length) { fhirQuestionnaire.extension = (fhirQuestionnaire.extension ?? []).concat(extensions); } diff --git a/src/converter/fhirToFce/questionnaire/processExtensions.ts b/src/converter/fhirToFce/questionnaire/processExtensions.ts index 9572b94..2a4b9aa 100644 --- a/src/converter/fhirToFce/questionnaire/processExtensions.ts +++ b/src/converter/fhirToFce/questionnaire/processExtensions.ts @@ -1,4 +1,4 @@ -import { Questionnaire as FHIRQuestionnaire, Expression as FHIRExpression } from 'fhir/r4b'; +import { Questionnaire as FHIRQuestionnaire, Expression as FHIRExpression, Attachment } from 'fhir/r4b'; export function processExtensions(fhirQuestionnaire: FHIRQuestionnaire): { launchContext?: any[]; @@ -9,6 +9,8 @@ export function processExtensions(fhirQuestionnaire: FHIRQuestionnaire): { assembleContext?: string[]; itemPopulationContext?: FHIRExpression; variable?: FHIRExpression[]; + printableHeader?: Attachment; + printableFooter?: Attachment; } { const launchContext = processLaunchContext(fhirQuestionnaire); const mapping = processMapping(fhirQuestionnaire); @@ -18,6 +20,8 @@ export function processExtensions(fhirQuestionnaire: FHIRQuestionnaire): { const assembleContext = processAssembleContext(fhirQuestionnaire); const itemPopulationContext = processItemPopulationContext(fhirQuestionnaire); const variable = processVariable(fhirQuestionnaire); + const printableHeader = processPrintableHeader(fhirQuestionnaire); + const printableFooter = processPrintableFooter(fhirQuestionnaire); return { launchContext: launchContext?.length ? launchContext : undefined, @@ -28,6 +32,8 @@ export function processExtensions(fhirQuestionnaire: FHIRQuestionnaire): { assembleContext: assembleContext.length ? assembleContext : undefined, itemPopulationContext: itemPopulationContext ? itemPopulationContext : undefined, variable: variable?.length ? variable : undefined, + printableHeader: printableHeader ? printableHeader : undefined, + printableFooter: printableFooter ? printableFooter : undefined, }; } @@ -156,3 +162,27 @@ function processVariable(fhirQuestionnaire: FHIRQuestionnaire): FHIRExpression[] return extensions.map((ext) => ext.valueExpression!); } + +function processPrintableHeader(fhirQuestionnaire: FHIRQuestionnaire): Attachment | undefined { + const extension = fhirQuestionnaire.extension?.find( + (ext) => ext.url === 'https://emr-core.beda.software/StructureDefinition/printable-header', + ); + + if (!extension) { + return undefined; + } + + return extension.valueAttachment; +} + +function processPrintableFooter(fhirQuestionnaire: FHIRQuestionnaire): Attachment | undefined { + const extension = fhirQuestionnaire.extension?.find( + (ext) => ext.url === 'https://emr-core.beda.software/StructureDefinition/printable-footer', + ); + + if (!extension) { + return undefined; + } + + return extension.valueAttachment; +} diff --git a/src/fce.types.ts b/src/fce.types.ts index 99a9ea0..09d667c 100644 --- a/src/fce.types.ts +++ b/src/fce.types.ts @@ -32,6 +32,10 @@ export interface FCEQuestionnaire extends Questionnaire { /** NOTE: from extension http://hl7.org/fhir/StructureDefinition/variable */ /** Variable specifying a logic to generate a variable for use in subsequent logic. The name of the variable will be added to FHIRPath's context when processing descendants of the element that contains this extension. */ variable?: Expression[]; + /** NOTE: from extension https://emr-core.beda.software/StructureDefinition/printable-header */ + printableHeader?: Attachment; + /** NOTE: from extension https://emr-core.beda.software/StructureDefinition/printable-footer */ + printableFooter?: Attachment; /** NOTE: from extension http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-targetStructureMap */ targetStructureMap?: string[]; }