DiagnosticReport
→
measurement
documented
When a DiagnosticReport's LOINC code resolves to an OMOP concept with domain_id = Measurement, the report itself produces one or more measurement rows. Each conclusionCode entry generates a separate row. This is independent of the Observation results referenced by DiagnosticReport.result[], which are mapped by the Observation mapper. Lab panel codes (e.g. 24323-8 Comprehensive metabolic panel) are the primary Measurement-domain codes.
Conversion profile
omop-diagnosticreport-measurement
A FHIR instance converts to measurement iff it validates against this profile.
| Path | Card | Type | Binding / Fixed | Comment |
|---|---|---|---|---|
| DiagnosticReport.status | fhir/diagnostic-report-statusrequired | OMOP measurement requires a finalized report. | ||
| DiagnosticReport.code | 1..*MS | omop-measurement-codesrequired | ||
| DiagnosticReport.subject | 1..* | Reference | Required for measurement.person_id. | |
| DiagnosticReport.effective[x] | 1..*MS | Required for measurement.measurement_date / measurement_datetime. |
ViewDefinition (Stage 1 flattener)
omop-diagnosticreport-measurement
11 columns · resource DiagnosticReport
| column name | FHIRPath | type |
|---|---|---|
| id | DiagnosticReport.id | id |
| code_loinc | DiagnosticReport.code.coding.where(system='http://loinc.org').first().code | code |
| code_snomed | DiagnosticReport.code.coding.where(system='http://snomed.info/sct').first().code | code |
| code_text | DiagnosticReport.code.text | string |
| subject_id | DiagnosticReport.subject | Reference(Patient) |
| measurement_date | DiagnosticReport.effective[x] | dateTime|Period |
| measurement_datetime | DiagnosticReport.effective[x] | dateTime|Period |
| value_as | DiagnosticReport.conclusionCode | CodeableConcept |
| performer_id | DiagnosticReport.performer[0] | Reference(Practitioner) |
| encounter_id | DiagnosticReport.encounter | Reference(Encounter) |
| value_text | DiagnosticReport.conclusionCode.coding[0].code | code |
Condition: DiagnosticReport.code (LOINC) resolves to OMOP concept with domain_id = Measurement
Fields (23)
-
measurement_id← — integer · idPKrequiredSurrogate key. Hash/sequence of DiagnosticReport.id + conclusionCode index. Must be unique across all measurement sources. -
person_id←DiagnosticReport.subjectinteger · Reference(Patient)FK→PERSONrequiredResolve Patient/{id} reference to integer person_id. Skip row if unresolvable. -
measurement_concept_id←DiagnosticReport.codeinteger · CodeableConceptFK→CONCEPTrequiredLOINC code looked up in OMOP vocabulary. Must have domain_id = Measurement. Use first LOINC coding found. 0 if concept not found. -
measurement_date←DiagnosticReport.effective[x]date · dateTime|PeriodrequiredDate component of effectiveDateTime, or effectivePeriod.start. Skip row if absent (ETL-German behavior).2 sources ▾
-
Skip resource if effective[x] absent; accepts effectiveDateTime or effectivePeriod.start
-
Uses effectivePeriod.start (parser extracts but does not map to OMOP)
-
-
measurement_datetime←DiagnosticReport.effective[x]datetime · dateTime|PeriodFull timestamp from effectiveDateTime or effectivePeriod.start. -
measurement_time← — varchar(10)Legacy field. Leave null. -
measurement_type_concept_id←DiagnosticReport.categoryinteger · CodeableConceptFK→CONCEPTrequiredmap:diagnostic_report_categoryETL-German maps category via SOURCE_VOCABULARY_ID_DIAGNOSTIC_REPORT_CATEGORY custom concept map. Common defaults: 32817 (EHR) or 32856 (Lab result).3 sources ▾
-
Custom source_to_concept_map keyed on 'DiagnosticReport_Category'; LAB→32856, others→32817
-
Field listed as required but left null in skeleton
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/009-DiagnosticReport-measurement.sh
-
Conceptual guidance: map category code to type concept
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575
-
-
operator_concept_id← — integerFK→CONCEPTETL-German maps SNOMED conclusion interpretation codes to operator. Typically null for DiagnosticReport. -
value_as_number← — floatDiagnosticReport conclusions are coded, not numeric. Leave null. -
value_as_concept_id←DiagnosticReport.conclusionCodeinteger · CodeableConceptFK→CONCEPTSNOMED conclusion code resolved to OMOP concept. ETL-German currently stores this in measurement_source_concept_id instead.2 sources ▾
-
Not used; SNOMED conclusionCode concept stored in measurement_source_concept_id instead (line 339)
-
Conceptual guidance: conclusionCode should populate value_as_concept_id
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575
-
-
unit_concept_id← — integerFK→CONCEPTNo units on DiagnosticReport-level measurements. Leave null. -
range_low← — floatNo reference ranges on DiagnosticReport. Leave null. -
range_high← — floatNo reference ranges on DiagnosticReport. Leave null. -
provider_id←DiagnosticReport.performer[0]integer · Reference(Practitioner)FK→PROVIDERFirst performer reference resolved to provider_id. Also consider resultsInterpreter[0]. -
visit_occurrence_id←DiagnosticReport.encounterinteger · Reference(Encounter)FK→VISIT_OCCURRENCEResolve Encounter/{id} to visit_occurrence_id. -
visit_detail_id← — integerFK→VISIT_DETAILLeave null unless visit details are modeled. -
measurement_source_value←DiagnosticReport.conclusionCode.coding[0].codevarchar(50) · codeVerbatim SNOMED code from conclusionCode. ETL-German uses this field. -
measurement_source_concept_id←DiagnosticReport.conclusionCodeinteger · CodeableConceptFK→CONCEPTSNOMED conclusionCode resolved to OMOP concept ID. ETL-German stores the SNOMED concept here (line 339).2 sources ▾
-
SNOMED conclusionCode concept stored here instead of value_as_concept_id (line 339); splits composite SNOMED expressions
-
Conceptual: conclusionCode maps to measurement_source_concept_id
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575
-
-
unit_source_value← — varchar(50)No units. Leave null. -
unit_source_concept_id← — integerFK→CONCEPTNo units. Leave null. -
value_source_value←DiagnosticReport.conclusionCode.coding[0].codevarchar(50) · codeVerbatim SNOMED code. ETL-German stores this (line 344). -
measurement_event_id← — integerCan link back to a related OMOP record if needed. Leave null. -
meas_event_field_concept_id← — integerFK→CONCEPTLeave null.
Vocabularies
report_code_loinc
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| 24323-8 | Comprehensive metabolic panel | 3004410 | Comprehensive metabolic panel |
| 24357-6 | Urinalysis macro panel | 3002385 | Urinalysis macro panel |
| 58410-2 | CBC panel (blood) | 3016502 | CBC panel (blood) |
diagnostic_report_category
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| LAB | Laboratory studies | 32856 | Lab result |
| RAD | Radiology | 32817 | EHR |
| PAT | Pathology | 32817 | EHR |
| MB | Microbiology | 32817 | EHR |
| (absent) | No category | 32817 | EHR |
Edge Cases
No effectiveDateTime or effectivePeriod
ETL-German skips the resource entirely. Alternative: fall back to issued timestamp.
Status is registered, preliminary, cancelled, entered-in-error, or unknown
Reject. Accept only final, amended, corrected, appended. Preliminary results may change and should not be committed to the CDM.
No conclusionCode
ETL-German skips the resource. Without a conclusion, there is no value to store. Only the referenced Observations produce rows.
Multiple conclusionCode entries
Each code produces a separate measurement row. The measurement_concept_id (report LOINC) is the same across all rows; only measurement_source_concept_id and value_source_value differ.
Composite SNOMED in conclusionCode (e.g. 118247008:{363713009=373068000})
ETL-German splits composite expressions: base code before : maps to measurement_source_concept_id; attributes after :{ are parsed for interpretation codes mapped to operator_concept_id.
SNOMED codes joined with + (conjunction)
ETL-German splits on + and creates one row per component code.
No category
ETL-German skips the resource. Alternative: default to measurement_type_concept_id = 32817 (EHR).
LOINC code resolves to Observation or Procedure domain
Route to observation or procedure_occurrence table instead.
Report with result[] but no code or conclusionCode
No report-level row produced. The Observations in result[] are still mapped independently.
subject references Group (not Patient)
Not supported. OMOP requires a single person_id. Skip the resource.
Incremental updates (same DiagnosticReport reprocessed)
ETL-German deletes existing OMOP rows by fhirLogicalId or fhirIdentifier before re-inserting.
Reference Implementations
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/DiagnosticReportMapper.java:303-365 — Primary reference. createDiagnosticReportMeasurement() with full domain routing, LOINC lookup, SNOMED conclusionCode, composite SNOMED splitting.
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/009-DiagnosticReport-measurement.sh — Skeleton mapping with all 23 measurement fields listed but all set to null.
- NACHC-fhir-to-omop(java) refs/refs/NACHC-fhir-to-omop/src/main/java/org/nachc/tools/fhirtoomop/fhir/parser/r4/diagnosticreport/DiagnosticReportParser.java:1-120 — Parser only -- extracts code, status, effectivePeriod. No OMOP output mapping.
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575 — Conceptual mapping guidance. Documents domain-based routing and field correspondences.