DiagnosticReport
→
observation
documented
When a DiagnosticReport's LOINC code resolves to an OMOP concept with domain_id = Observation, the report produces one or more observation rows. This applies primarily to clinical document types -- history and physical notes, discharge summaries, evaluation and plan notes, consultation notes -- where the LOINC code represents a document class rather than a quantitative measurement or procedure. Each conclusionCode entry generates a separate row. This is the most frequently hit domain for DiagnosticReport resources in real-world EHR data (clinical notes are far more common than lab panel-level reports).
Conversion profile
omop-diagnosticreport-observation
A FHIR instance converts to observation iff it validates against this profile.
| Path | Card | Type | Binding / Fixed | Comment |
|---|---|---|---|---|
| DiagnosticReport.status | fhir/diagnostic-report-statusrequired | |||
| DiagnosticReport.code | 1..*MS | omop-observation-codesrequired | ||
| DiagnosticReport.subject | 1..* | Reference | Required for observation.person_id. | |
| DiagnosticReport.effective[x] | 1..*MS | Required for observation.observation_date / observation_datetime. |
ViewDefinition (Stage 1 flattener)
omop-diagnosticreport-observation
12 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) |
| observation_date | DiagnosticReport.effective[x] | dateTime|Period |
| observation_datetime | DiagnosticReport.effective[x] | dateTime|Period |
| value_as_string | DiagnosticReport.conclusionCode.coding[0].display | string |
| 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 = Observation
Fields (21)
-
observation_id← — integer · idPKrequiredSurrogate key. Hash/sequence of DiagnosticReport.id + conclusionCode index. Must be unique across all observation sources. -
person_id←DiagnosticReport.subjectinteger · Reference(Patient)FK→PERSONrequiredResolve Patient/{id} reference to integer person_id. Skip row if unresolvable. -
observation_concept_id←DiagnosticReport.codeinteger · CodeableConceptFK→CONCEPTrequiredLOINC code looked up in OMOP vocabulary. Must have domain_id = Observation. Use first LOINC coding found. 0 if concept not found. -
observation_date←DiagnosticReport.effective[x]date · dateTime|PeriodrequiredDate component of effectiveDateTime, or effectivePeriod.start. Skip row if absent (ETL-German behavior). -
observation_datetime←DiagnosticReport.effective[x]datetime · dateTime|PeriodFull timestamp from effectiveDateTime or effectivePeriod.start. -
observation_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).2 sources ▾
-
Custom source_to_concept_map keyed on 'DiagnosticReport_Category'; category required — resource skipped if absent
-
Conceptual: category code maps to type concept
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575
-
-
value_as_number← — floatDiagnosticReport conclusions are coded, not numeric. Leave null. -
value_as_string←DiagnosticReport.conclusionCode.coding[0].displayvarchar(60) · stringETL-German stores the SNOMED code string here (line 409). Recommended: use conclusionCode display text, or fall back to conclusion free text (truncated to 60 chars).2 sources ▾
-
Stores the raw SNOMED code string (not display text) from conclusionCode at line 409
-
Not specified — field not addressed
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575
-
-
value_as_concept_id←DiagnosticReport.conclusionCodeinteger · CodeableConceptFK→CONCEPTSNOMED conclusion code resolved to OMOP concept. ETL-German currently stores this in observation_source_concept_id instead. Recommended: store the resolved SNOMED concept here as the clinical value of the report.2 sources ▾
-
Commented out at line 408 — ETL-German does not populate this field; SNOMED concept goes to observation_source_concept_id instead
-
Conceptual: conclusionCode should populate value_as_concept_id
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575
-
-
qualifier_concept_id← — integerFK→CONCEPTETL-German maps SNOMED composite expression interpretation attributes (from post-coordinated conclusionCode) to this field (line 425). Null if no interpretation is present.1 source ▾
-
Interpretation code from composite SNOMED expression (value after = in :{...}) resolved to OMOP concept at line 425; not set without composite expression
-
-
unit_concept_id← — integerFK→CONCEPTNo units on DiagnosticReport-level observations. 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. -
observation_source_value←DiagnosticReport.conclusionCode.coding[0].codevarchar(50) · codeVerbatim SNOMED code from conclusionCode. ETL-German stores this (line 405). -
observation_source_concept_id←DiagnosticReport.conclusionCodeinteger · CodeableConceptFK→CONCEPTSNOMED conclusionCode resolved to OMOP concept ID. ETL-German stores the SNOMED concept here (line 404).2 sources ▾
-
SNOMED conclusionCode base concept stored here (line 404); doubles as semantic value since value_as_concept_id is unused
-
Conceptual: conclusionCode maps here
- fhir2omop-cookbook(guidance) refs/fhir2omop-cookbook.md:528-575
-
-
unit_source_value← — varchar(50)No units. Leave null. -
qualifier_source_value← — varchar(50)ETL-German stores the SNOMED interpretation concept code from composite SNOMED expressions (line 426). Null if no composite expression. -
value_source_value←DiagnosticReport.conclusionCode.coding[0].codevarchar(50) · codeVerbatim SNOMED code. ETL-German does not explicitly set this field. Recommended: store the raw conclusionCode string. -
observation_event_id← — integerCan link back to a related OMOP record if needed. Leave null. -
obs_event_field_concept_id← — integerFK→CONCEPTLeave null.
Vocabularies
report_code_loinc
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| 34117-2 | History and physical note | 3040820 | History and physical note |
| 51847-2 | Evaluation+Plan note | 3045440 | Evaluation+Plan note |
| 18842-5 | Discharge summary | 3002340 | Discharge summary |
| 47039-3 | Hospital Admission history and physical note | 3046280 | Hospital Admission history and physical note |
| 47042-7 | Counseling note | 3046283 | Counseling note |
| 57133-1 | Referral note | 3048099 | Referral note |
| 28570-0 | Procedure note | 3022227 | Procedure note |
| 11506-3 | Progress note | 3001832 | Progress note |
| 34109-9 | Note | 3040812 | Note |
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 |
| LP29684-5 | Radiology (LOINC category, US Core) | 32817 | EHR |
| LP29708-2 | Cardiology (LOINC category) | 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.
No conclusionCode
ETL-German skips the resource. Without a conclusion, there is no value to store. Alternative: create a row with null value fields to record the report's existence.
Multiple conclusionCode entries
Each code produces a separate observation row. The observation_concept_id (report LOINC) is the same across all rows; only observation_source_concept_id, observation_source_value, and value_as_string differ.
Composite SNOMED in conclusionCode (e.g. 118247008:{363713009=373068000})
ETL-German splits composite expressions: base code before : maps to observation_source_concept_id; the interpretation attribute after = maps to qualifier_concept_id and qualifier_source_value.
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 observation_type_concept_id = 32817 (EHR).
LOINC code resolves to Measurement or Procedure domain
Route to measurement 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.
conclusion text present but no conclusionCode
ETL-German skips (requires conclusionCode). Alternative: store conclusion text in value_as_string and create a row with the LOINC report code. The conclusion text can also be mapped to the note table instead.
Reference Implementations
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/DiagnosticReportMapper.java:367-431 — Primary reference. createDiagnosticReportObservation() with domain routing, LOINC lookup, SNOMED conclusionCode, composite SNOMED splitting, qualifier from interpretation.
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/009-DiagnosticReport-measurement.sh — Skeleton only -- no observation-specific mapping file exists. Example resource uses LOINC 34117-2 (History and physical note) which resolves to Observation domain.
- 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 domain routing at lines 563-567.