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).

A FHIR instance converts to observation iff it validates against this profile.
Routing key DiagnosticReport.code ∈ omop-observation-codes (OMOP domain Observation)
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 · id
    PKrequired
    Surrogate key. Hash/sequence of DiagnosticReport.id + conclusionCode index. Must be unique across all observation sources.
  • person_id DiagnosticReport.subject integer · Reference(Patient)
    FK→PERSONrequired
    Resolve Patient/{id} reference to integer person_id. Skip row if unresolvable.
  • observation_concept_id DiagnosticReport.code integer · CodeableConcept
    FK→CONCEPTrequired
    LOINC 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|Period
    required
    Date component of effectiveDateTime, or effectivePeriod.start. Skip row if absent (ETL-German behavior).
  • observation_datetime DiagnosticReport.effective[x] datetime · dateTime|Period
    Full timestamp from effectiveDateTime or effectivePeriod.start.
  • observation_type_concept_id DiagnosticReport.category integer · CodeableConcept
    FK→CONCEPTrequiredmap:diagnostic_report_category
    ETL-German maps category via SOURCE_VOCABULARY_ID_DIAGNOSTIC_REPORT_CATEGORY custom concept map. Common defaults: 32817 (EHR) or 32856 (Lab result).
    2 sources ▾
  • value_as_number float
    DiagnosticReport conclusions are coded, not numeric. Leave null.
  • value_as_string DiagnosticReport.conclusionCode.coding[0].display varchar(60) · string
    ETL-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 ▾
  • value_as_concept_id DiagnosticReport.conclusionCode integer · CodeableConcept
    FK→CONCEPT
    SNOMED 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 ▾
  • qualifier_concept_id integer
    FK→CONCEPT
    ETL-German maps SNOMED composite expression interpretation attributes (from post-coordinated conclusionCode) to this field (line 425). Null if no interpretation is present.
    1 source ▾
  • unit_concept_id integer
    FK→CONCEPT
    No units on DiagnosticReport-level observations. Leave null.
  • provider_id DiagnosticReport.performer[0] integer · Reference(Practitioner)
    FK→PROVIDER
    First performer reference resolved to provider_id. Also consider resultsInterpreter[0].
  • visit_occurrence_id DiagnosticReport.encounter integer · Reference(Encounter)
    FK→VISIT_OCCURRENCE
    Resolve Encounter/{id} to visit_occurrence_id.
  • visit_detail_id integer
    FK→VISIT_DETAIL
    Leave null unless visit details are modeled.
  • observation_source_value DiagnosticReport.conclusionCode.coding[0].code varchar(50) · code
    Verbatim SNOMED code from conclusionCode. ETL-German stores this (line 405).
  • observation_source_concept_id DiagnosticReport.conclusionCode integer · CodeableConcept
    FK→CONCEPT
    SNOMED conclusionCode resolved to OMOP concept ID. ETL-German stores the SNOMED concept here (line 404).
    2 sources ▾
  • 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].code varchar(50) · code
    Verbatim SNOMED code. ETL-German does not explicitly set this field. Recommended: store the raw conclusionCode string.
  • observation_event_id integer
    Can link back to a related OMOP record if needed. Leave null.
  • obs_event_field_concept_id integer
    FK→CONCEPT
    Leave 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