From 3a86890585c3e50896fbab47bcd892158e247687 Mon Sep 17 00:00:00 2001 From: LaurenD Date: Fri, 16 Aug 2024 12:00:26 -0400 Subject: [PATCH 1/5] Flexporter mapping for 130 and initial README --- synthea/README.md | 75 ++++++++++++++++++++++++++ synthea/qicore_130.yaml | 117 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 synthea/README.md create mode 100644 synthea/qicore_130.yaml diff --git a/synthea/README.md b/synthea/README.md new file mode 100644 index 0000000..d165849 --- /dev/null +++ b/synthea/README.md @@ -0,0 +1,75 @@ +# Bulk Data Generation with Synthea + +The [Synthea](#https://github.com/synthetichealth/synthea) project can be used to generate realistic (but not real) patient data for use in a testing context. Synthea patient data can be exported according to the FHIR specification but may not be conformant to certain IGs and profiles. The Synthea [Flexporter](#https://github.com/synthetichealth/synthea/wiki/Flexporter) can be used to implement mappings during the export process that can add, remove, or update the patient data to be conformant to FHIR profiles. + +## Usage + +- Download the Synthea codebase from https://github.com/synthetichealth/synthea +- In the Synthea project, use `./run_synthea -fm {mapping file location}` to run synthea with the flexporter +- You may also use the flexporter standalone to map an existing exported file `./run_flexporter -fm {mapping_file_location} -s {source_fhir_file}` +- See the [flexporter documentation](#https://github.com/synthetichealth/synthea/wiki/Flexporter) for additional information on the flexporter, mapping file, and limitations. + +## Building the Mapping File +Quality Measurement calculation requires data conformant with qicore, and expansive IG, which would require expansive effort to fully map. As such, we can piecemeal address the IG requirements by supporting requirements for individual measures. The recommended process for creating a mapping that addresses a set of measures is: + +1. Use [elm-parser-for-ecqms fhir_review branch](#https://github.com/projecttacoma/elm-parser-for-ecqms/tree/fhir_review) and get data requirements to build a combined list of mustSupports for all resources across the set of measures. +2. Use [fqm-execution](#https://github.com/projecttacoma/fqm-execution) and get data requirements to build a full list of profiles used. +3. For all resource types, ensure resource is exported as a top level resource by Synthea or already supported by the flexporter mapping. If not, use the flexporter `create_resource` action to export the resource based on a logical existing exported resource or based on a logical Synthea module state. Resources that are only used for SDEs may be initially ignored. +3. Make sure all profiles are applied to the correct exported resource. +4. For each must support: + - Check the resource's [qicore 4.1.1](#https://hl7.org/fhir/us/qicore/STU4.1.1/) profile Snapshot Table to check if the mustSupport is also required by the profile (minimum cardinality 1). If not, it may be initially ignored. + - If required, check if synthea already exports it by looking in the [FhirR4.java](#https://github.com/synthetichealth/synthea/blob/master/src/main/java/org/mitre/synthea/export/FhirR4.java) file. You can look for a string like `Condition()` for an example of how the Fhir object is created and what fields are set on it. + - If Synthea doesn't export it, check if the existing mapping file already adds it through a mapping. + - If not, fill from something logical that synthea does export (if something logical exists). Understanding what values are logical may involve looking at the FHIR specification and/or the measure's CQL for how the must support field is used in the measure logic. + - If a logical field doesn't exist, fill from a chosen pre-set value or choose randomly from a limited set of values + +## CMS130 Mapping Example + +Below is the example list of resources and must support elements collated from elm-parser-for-ecqms data requirement calculation. Unexported values are marked with a `*` with notes about how their export was resolved in the `qicore_130.yaml` mapping file. + +- Condition: + - "code", + - "clinicalStatus", + - "onset", + - "abatement" +- Coverage: * exported as a contained resource only -> ignored since Coverage is only used for SDEs + - "type", + - "period" +- DeviceRequest: * not exported -> created based on existing exported Device resources + - "code", + - "authoredOn", + - "status", + - "intent", + - "modifierExtension", * not exported -> ignored since modifierExtension is only used for doNotPerform + - "modifierExtension.url", + - "modifierExtension.value" +- Encounter: + - "status", + - "type", + - "period", + - "diagnosis",* + - "diagnosis.condition", * not exported -> set using a js function that finds conditions that reference this encounter and creates a reference to that condition + - "hospitalization" +- MedicationRequest: + - "medication", + - "doNotPerform", * not exported -> set to false because all resources exported from Synthea are logically performed + - "status", + - "intent", + - "dosageInstruction", + - "dispenseRequest", * not exported -> set from the `authoredOn` field because the measure logic coalesces `dispenseRequest.validityPeriod.start` with `authoredOn` + - "authoredOn" +- Observation: + - "code", + - "value", + - "effective", + - "status", + - "category" +- Procedure: + - "code", + - "performed", + - "status" +- ServiceRequest: * exported as a contained resource only -> created based on existing exported Procedure resources + - "code", + - "authoredOn", + - "status", + - "intent" \ No newline at end of file diff --git a/synthea/qicore_130.yaml b/synthea/qicore_130.yaml new file mode 100644 index 0000000..97e6c60 --- /dev/null +++ b/synthea/qicore_130.yaml @@ -0,0 +1,117 @@ +--- +name: QI Core - 130 minimal +applicability: true + +actions: + - name: Apply Profiles + profiles: + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient + applicability: Patient + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-encounter + applicability: Encounter + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition + applicability: Condition + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation + applicability: Observation + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-procedure + applicability: Procedure + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest + applicability: MedicationRequest + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-immunization + applicability: Immunization + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-careplan + applicability: CarePlan + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-imagingstudy + applicability: ImagingStudy + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-device + applicability: Device + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-practitioner + applicability: Practitioner + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-allergyintolerance + applicability: AllergyIntolerance + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-claim + applicability: Claim + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-coverage + applicability: Coverage + - profile: http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest + applicability: ServiceRequest + + + - name: Set Missing Values + set_values: + - applicability: MedicationRequest + fields: + - location: MedicationRequest.doNotPerform + value: "false" + - location: MedicationRequest.dispenseRequest.validityPeriod.start + value: $getField([MedicationRequest.authoredOn]) + - name: Apply Script​ + execute_script: + - apply_to: resource + function_name: addDiagnosis + resource_type: Encounter + function: | + function addDiagnosis(resource, bundle) { + const conditionEntry = bundle.entry.find(e => e.resource?.resourceType === 'Condition' && e.resource?.encounter?.reference === `urn:uuid:${resource.id}`); + if (conditionEntry){ + resource.diagnosis = [{ + condition:{ + reference: `Condition/${conditionEntry.resource.id}` + } + }]; + } + } + - name: Create Resources + create_resource: + - resourceType: ServiceRequest + based_on: + resource: Procedure.performed.ofType(Period) # handle value setting for period choice type + fields: + - location: ServiceRequest.intent + value: order + - location: ServiceRequest.encounter.reference + value: $getField([Procedure.encounter.reference]) + - location: ServiceRequest.subject.reference + value: $findRef([Patient]) + - location: ServiceRequest.status + value: completed # all procedures are exported as completed + - location: ServiceRequest.authoredOn + value: $getField([Procedure.performed.start]) # period choice type + - location: ServiceRequest.code + value: $getField([Procedure.code]) + writeback: + - location: Procedure.basedOn.reference + value: $setRef([ServiceRequest]) + - resourceType: ServiceRequest + based_on: + resource: Procedure.performed.ofType(dateTime) # handle value setting for datetime choice type + fields: + - location: ServiceRequest.intent + value: order + - location: ServiceRequest.encounter.reference + value: $getField([Procedure.encounter.reference]) + - location: ServiceRequest.subject.reference + value: $findRef([Patient]) + - location: ServiceRequest.status + value: completed # all procedures are exported as completed + - location: ServiceRequest.authoredOn + value: $getField([Procedure.performed]) # datetime choice type + - location: ServiceRequest.code + value: $getField([Procedure.code]) + writeback: + - location: Procedure.basedOn.reference + value: $setRef([ServiceRequest]) + - resourceType: DeviceRequest + based_on: + resource: Device + profiles: + - http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-devicerequest + fields: + - location: DeviceRequest.code.reference + value: $findRef([Device]) + - location: DeviceRequest.authoredOn + value: $getField([Device.manufactureDate]) # manufacture time is set 3 weeks before device model's start, so this is close enough + - location: DeviceRequest.status + value: completed + - location: DeviceRequest.intent + value: order \ No newline at end of file From e11f4fdeaa076fb27ad4c526b5cb6d9d9f100b80 Mon Sep 17 00:00:00 2001 From: LaurenD Date: Tue, 20 Aug 2024 11:54:28 -0400 Subject: [PATCH 2/5] Fix links --- synthea/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/synthea/README.md b/synthea/README.md index d165849..0b27f32 100644 --- a/synthea/README.md +++ b/synthea/README.md @@ -1,24 +1,24 @@ # Bulk Data Generation with Synthea -The [Synthea](#https://github.com/synthetichealth/synthea) project can be used to generate realistic (but not real) patient data for use in a testing context. Synthea patient data can be exported according to the FHIR specification but may not be conformant to certain IGs and profiles. The Synthea [Flexporter](#https://github.com/synthetichealth/synthea/wiki/Flexporter) can be used to implement mappings during the export process that can add, remove, or update the patient data to be conformant to FHIR profiles. +The [Synthea](https://github.com/synthetichealth/synthea) project can be used to generate realistic (but not real) patient data for use in a testing context. Synthea patient data can be exported according to the FHIR specification but may not be conformant to certain IGs and profiles. The Synthea [Flexporter](https://github.com/synthetichealth/synthea/wiki/Flexporter) can be used to implement mappings during the export process that can add, remove, or update the patient data to be conformant to FHIR profiles. ## Usage - Download the Synthea codebase from https://github.com/synthetichealth/synthea - In the Synthea project, use `./run_synthea -fm {mapping file location}` to run synthea with the flexporter - You may also use the flexporter standalone to map an existing exported file `./run_flexporter -fm {mapping_file_location} -s {source_fhir_file}` -- See the [flexporter documentation](#https://github.com/synthetichealth/synthea/wiki/Flexporter) for additional information on the flexporter, mapping file, and limitations. +- See the [flexporter documentation](https://github.com/synthetichealth/synthea/wiki/Flexporter) for additional information on the flexporter, mapping file, and limitations. ## Building the Mapping File Quality Measurement calculation requires data conformant with qicore, and expansive IG, which would require expansive effort to fully map. As such, we can piecemeal address the IG requirements by supporting requirements for individual measures. The recommended process for creating a mapping that addresses a set of measures is: -1. Use [elm-parser-for-ecqms fhir_review branch](#https://github.com/projecttacoma/elm-parser-for-ecqms/tree/fhir_review) and get data requirements to build a combined list of mustSupports for all resources across the set of measures. -2. Use [fqm-execution](#https://github.com/projecttacoma/fqm-execution) and get data requirements to build a full list of profiles used. +1. Use [elm-parser-for-ecqms fhir_review branch](https://github.com/projecttacoma/elm-parser-for-ecqms/tree/fhir_review) and get data requirements to build a combined list of mustSupports for all resources across the set of measures. +2. Use [fqm-execution](https://github.com/projecttacoma/fqm-execution) and get data requirements to build a full list of profiles used. 3. For all resource types, ensure resource is exported as a top level resource by Synthea or already supported by the flexporter mapping. If not, use the flexporter `create_resource` action to export the resource based on a logical existing exported resource or based on a logical Synthea module state. Resources that are only used for SDEs may be initially ignored. 3. Make sure all profiles are applied to the correct exported resource. 4. For each must support: - - Check the resource's [qicore 4.1.1](#https://hl7.org/fhir/us/qicore/STU4.1.1/) profile Snapshot Table to check if the mustSupport is also required by the profile (minimum cardinality 1). If not, it may be initially ignored. - - If required, check if synthea already exports it by looking in the [FhirR4.java](#https://github.com/synthetichealth/synthea/blob/master/src/main/java/org/mitre/synthea/export/FhirR4.java) file. You can look for a string like `Condition()` for an example of how the Fhir object is created and what fields are set on it. + - Check the resource's [qicore 4.1.1](https://hl7.org/fhir/us/qicore/STU4.1.1/) profile Snapshot Table to check if the mustSupport is also required by the profile (minimum cardinality 1). If not, it may be initially ignored. + - If required, check if synthea already exports it by looking in the [FhirR4.java](https://github.com/synthetichealth/synthea/blob/master/src/main/java/org/mitre/synthea/export/FhirR4.java) file. You can look for a string like `Condition()` for an example of how the Fhir object is created and what fields are set on it. - If Synthea doesn't export it, check if the existing mapping file already adds it through a mapping. - If not, fill from something logical that synthea does export (if something logical exists). Understanding what values are logical may involve looking at the FHIR specification and/or the measure's CQL for how the must support field is used in the measure logic. - If a logical field doesn't exist, fill from a chosen pre-set value or choose randomly from a limited set of values From fde40539e1942644faba8e5ec9b1ea0fea0c6b0c Mon Sep 17 00:00:00 2001 From: lmd59 Date: Wed, 4 Sep 2024 12:23:14 -0400 Subject: [PATCH 3/5] README typo updates Co-authored-by: Elsa Perelli --- synthea/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/synthea/README.md b/synthea/README.md index 0b27f32..a493b88 100644 --- a/synthea/README.md +++ b/synthea/README.md @@ -6,22 +6,22 @@ The [Synthea](https://github.com/synthetichealth/synthea) project can be used to - Download the Synthea codebase from https://github.com/synthetichealth/synthea - In the Synthea project, use `./run_synthea -fm {mapping file location}` to run synthea with the flexporter -- You may also use the flexporter standalone to map an existing exported file `./run_flexporter -fm {mapping_file_location} -s {source_fhir_file}` +- You may also use the flexporter standalone to map an existing exported file `./run_flexporter -fm {mapping file location} -s {source fhir file}` - See the [flexporter documentation](https://github.com/synthetichealth/synthea/wiki/Flexporter) for additional information on the flexporter, mapping file, and limitations. ## Building the Mapping File -Quality Measurement calculation requires data conformant with qicore, and expansive IG, which would require expansive effort to fully map. As such, we can piecemeal address the IG requirements by supporting requirements for individual measures. The recommended process for creating a mapping that addresses a set of measures is: +Quality Measurement calculation requires data conformant with qicore, an expansive IG, which would require expansive effort to fully map. As such, we can piecemeal address the IG requirements by supporting requirements for individual measures. The recommended process for creating a mapping that addresses a set of measures is: 1. Use [elm-parser-for-ecqms fhir_review branch](https://github.com/projecttacoma/elm-parser-for-ecqms/tree/fhir_review) and get data requirements to build a combined list of mustSupports for all resources across the set of measures. 2. Use [fqm-execution](https://github.com/projecttacoma/fqm-execution) and get data requirements to build a full list of profiles used. 3. For all resource types, ensure resource is exported as a top level resource by Synthea or already supported by the flexporter mapping. If not, use the flexporter `create_resource` action to export the resource based on a logical existing exported resource or based on a logical Synthea module state. Resources that are only used for SDEs may be initially ignored. -3. Make sure all profiles are applied to the correct exported resource. +4. Make sure all profiles are applied to the correct exported resource. 4. For each must support: - Check the resource's [qicore 4.1.1](https://hl7.org/fhir/us/qicore/STU4.1.1/) profile Snapshot Table to check if the mustSupport is also required by the profile (minimum cardinality 1). If not, it may be initially ignored. - If required, check if synthea already exports it by looking in the [FhirR4.java](https://github.com/synthetichealth/synthea/blob/master/src/main/java/org/mitre/synthea/export/FhirR4.java) file. You can look for a string like `Condition()` for an example of how the Fhir object is created and what fields are set on it. - If Synthea doesn't export it, check if the existing mapping file already adds it through a mapping. - If not, fill from something logical that synthea does export (if something logical exists). Understanding what values are logical may involve looking at the FHIR specification and/or the measure's CQL for how the must support field is used in the measure logic. - - If a logical field doesn't exist, fill from a chosen pre-set value or choose randomly from a limited set of values + - If a logical field doesn't exist, fill from a chosen pre-set value or choose randomly from a limited set of values. ## CMS130 Mapping Example From 5f05784480838b32b77e75fc979f1ec8746f6e0d Mon Sep 17 00:00:00 2001 From: LaurenD Date: Wed, 4 Sep 2024 13:17:51 -0400 Subject: [PATCH 4/5] Clarify synthea branch and fix numbering --- synthea/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/synthea/README.md b/synthea/README.md index a493b88..3bf071a 100644 --- a/synthea/README.md +++ b/synthea/README.md @@ -4,7 +4,7 @@ The [Synthea](https://github.com/synthetichealth/synthea) project can be used to ## Usage -- Download the Synthea codebase from https://github.com/synthetichealth/synthea +- Download the [Synthea](https://github.com/synthetichealth/synthea) codebase: If [synthetichealth/synthea#1501](https://github.com/synthetichealth/synthea/pull/1501) has been merged, you can use the `master` branch, otherwise use the branch listed in that PR. - In the Synthea project, use `./run_synthea -fm {mapping file location}` to run synthea with the flexporter - You may also use the flexporter standalone to map an existing exported file `./run_flexporter -fm {mapping file location} -s {source fhir file}` - See the [flexporter documentation](https://github.com/synthetichealth/synthea/wiki/Flexporter) for additional information on the flexporter, mapping file, and limitations. @@ -14,8 +14,7 @@ Quality Measurement calculation requires data conformant with qicore, an expansi 1. Use [elm-parser-for-ecqms fhir_review branch](https://github.com/projecttacoma/elm-parser-for-ecqms/tree/fhir_review) and get data requirements to build a combined list of mustSupports for all resources across the set of measures. 2. Use [fqm-execution](https://github.com/projecttacoma/fqm-execution) and get data requirements to build a full list of profiles used. -3. For all resource types, ensure resource is exported as a top level resource by Synthea or already supported by the flexporter mapping. If not, use the flexporter `create_resource` action to export the resource based on a logical existing exported resource or based on a logical Synthea module state. Resources that are only used for SDEs may be initially ignored. -4. Make sure all profiles are applied to the correct exported resource. +3. For all resource types, ensure resource is exported as a top level resource by Synthea or already supported by the flexporter mapping. If not, use the flexporter `create_resource` action to export the resource based on a logical existing exported resource or based on a logical Synthea module state. Resources that are only used for SDEs may be initially ignored. Make sure all profiles are applied to the correct exported resource. 4. For each must support: - Check the resource's [qicore 4.1.1](https://hl7.org/fhir/us/qicore/STU4.1.1/) profile Snapshot Table to check if the mustSupport is also required by the profile (minimum cardinality 1). If not, it may be initially ignored. - If required, check if synthea already exports it by looking in the [FhirR4.java](https://github.com/synthetichealth/synthea/blob/master/src/main/java/org/mitre/synthea/export/FhirR4.java) file. You can look for a string like `Condition()` for an example of how the Fhir object is created and what fields are set on it. From 8c41dccaf8d83876c2ddad1e47bcf5a588dd327b Mon Sep 17 00:00:00 2001 From: LaurenD Date: Thu, 5 Sep 2024 13:26:46 -0400 Subject: [PATCH 5/5] Update usage description --- synthea/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/synthea/README.md b/synthea/README.md index 3bf071a..9a1ecc9 100644 --- a/synthea/README.md +++ b/synthea/README.md @@ -4,9 +4,9 @@ The [Synthea](https://github.com/synthetichealth/synthea) project can be used to ## Usage -- Download the [Synthea](https://github.com/synthetichealth/synthea) codebase: If [synthetichealth/synthea#1501](https://github.com/synthetichealth/synthea/pull/1501) has been merged, you can use the `master` branch, otherwise use the branch listed in that PR. -- In the Synthea project, use `./run_synthea -fm {mapping file location}` to run synthea with the flexporter -- You may also use the flexporter standalone to map an existing exported file `./run_flexporter -fm {mapping file location} -s {source fhir file}` +- Download the [Synthea](https://github.com/synthetichealth/synthea) codebase. +- In the Synthea project, use `./run_synthea -fm {mapping file location}` to run synthea with the flexporter. +- You may also use the flexporter standalone to map an existing exported file: `./run_flexporter -fm {mapping file location} -s {source fhir file}`. - See the [flexporter documentation](https://github.com/synthetichealth/synthea/wiki/Flexporter) for additional information on the flexporter, mapping file, and limitations. ## Building the Mapping File