Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flexporter mapping for 130 and initial README #50

Merged
merged 5 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions synthea/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# 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](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
lmd59 marked this conversation as resolved.
Show resolved Hide resolved
- 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, 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. 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

lmd59 marked this conversation as resolved.
Show resolved Hide resolved
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"
elsaperelli marked this conversation as resolved.
Show resolved Hide resolved
- 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",
elsaperelli marked this conversation as resolved.
Show resolved Hide resolved
- "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",
elsaperelli marked this conversation as resolved.
Show resolved Hide resolved
- "status",
- "category"
- Procedure:
- "code",
- "performed",
- "status"
- ServiceRequest: * exported as a contained resource only -> created based on existing exported Procedure resources
elsaperelli marked this conversation as resolved.
Show resolved Hide resolved
- "code",
- "authoredOn",
- "status",
- "intent"
117 changes: 117 additions & 0 deletions synthea/qicore_130.yaml
Original file line number Diff line number Diff line change
@@ -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
Loading