Stage 3: FHIR resource extraction¶
Stage 3 reads FHIR R4 data into the same record tables as C-CDA documents, so a person's records from every health system and format sit side by side.
File shapes¶
A FHIR file can be a Bundle, a single resource, or NDJSON with one resource per line. Single resources and NDJSON are read as if they were a Bundle.
Resources read¶
| Resource | Bundle table | Notes |
|---|---|---|
| Patient | patients |
Name, birth date, sex, race and ethnicity (US Core extensions), MRN, the health system's patient ID as source_patient_id, and deceasedDateTime |
| Condition | problems |
Every coding; onset and recorded dates; clinical status |
| Observation | labs, vitals (and labs_vitals) |
Numeric, integer, text, coded and true or false values; units; category. Vital-sign observations go to vitals |
| MedicationRequest, MedicationDispense, MedicationAdministration, MedicationStatement | medications |
fhir_resource_type is kept, so an order can be told from a dispense, an administration or reported use |
| Procedure | procedures |
Performed date |
| Encounter | encounters |
Class and type |
| DiagnosticReport | diagnostic_reports |
Code, date and conclusion; attached report text becomes a document |
| DocumentReference | document_references |
Attached text becomes a document |
| Binary | documents |
Decoded content becomes a document |
| AllergyIntolerance, Immunization, CarePlan, Goal | their own tables |
Resources that do not belong to one patient, such as Practitioner, Organization and Location, are not read.
Choosing a code from several codings¶
A FHIR CodeableConcept can hold several codings for one thing, for example RxNorm and NDC for a medicine, or SNOMED CT and ICD-10-CM for a problem. fhir_prioritize_coding picks the code shown in code by domain:
| Domain | Preferred code systems, in order |
|---|---|
| Medication | RxNorm |
| Problem | SNOMED CT, then ICD-10-CM |
| Laboratory result and vital sign | LOINC |
| Procedure | CPT, then SNOMED CT |
| Allergy | RxNorm, then SNOMED CT |
| Immunization | CVX |
| Encounter | CPT, then SNOMED CT |
Every coding is kept in all_codings, and the add-on modules search all of them. C-CDA <code> elements are read the same way through extract_all_codings, which includes every <translation>, so an NDC code with an RxNorm translation is shown as RxNorm.
References across files¶
Exports often put a Medication resource in a different file from the MedicationRequest that points to it. Before the main pass, the pipeline reads every file once to index each Medication and each DocumentReference subject, so a medicationReference resolves wherever the Medication sits. On the warehouse route, a document listed in uuid_mapping.csv keeps the patient that file names; the DocumentReference index fills in only documents the mapping does not list.
Text attached to resources¶
Neurology notes, pulmonary function reports, genetic test reports and other documents are often delivered inside DiagnosticReport (presentedForm), DocumentReference (content) or Binary resources. Each attachment is decoded and converted by format (PDF, RTF, HTML or text) into a document with its own ID, title and date, and note extraction reads it like any other document. An attachment given only as a URL is not fetched; it is listed with the format external_attachment_not_retrieved so the gap is visible.
Deaths¶
Patient.deceasedDateTime is kept as deceased_date_time. The drug repurposing, cohort summary, OMOP and Phenopacket modules use it, together with any deaths in vital_status.csv.
Malformed resources¶
Exports sometimes hold malformed entries: strings where objects are expected, missing fields or encoding damage. Each file is parsed inside its own error handler, so one bad file is logged with its name and the error type and the run continues. Resources without a patient are skipped rather than written without one. Check the log for parse errors after each run.