DiagnosticReport note documented

When a DiagnosticReport carries free-text content -- either a conclusion string or one or more presentedForm attachments -- a note row is created to preserve the unstructured clinical narrative. This is independent of the domain-routed rows (measurement, observation, procedure_occurrence) that are produced from the report's LOINC code and conclusionCode. A single DiagnosticReport may produce both a domain-routed row and a note row. No reference implementation currently maps DiagnosticReport.conclusion/presentedForm to the OMOP note table; omoponfhir's DocumentReference mapper is the closest reference.

Conversion profile omop-diagnosticreport-note
A FHIR instance converts to note iff it validates against this profile.
Path Card Type Binding / Fixed Comment
DiagnosticReport.status fhir/diagnostic-report-statusrequired
DiagnosticReport.code 1..*MS Required for note_title and traceability. No domain binding — notes are unstructured.
DiagnosticReport.subject 1..* Reference Required for note.person_id.
DiagnosticReport.effective[x] 1..*MS Required for note.note_date / note_datetime.
DiagnosticReport.conclusion MS Either conclusion or presentedForm[] must be present — enforced by the conclusion-or-presentedForm constraint.
DiagnosticReport.presentedForm MS Either conclusion or presentedForm[] must be present.
ViewDefinition (Stage 1 flattener) omop-diagnosticreport-note
13 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_text DiagnosticReport.code.text string
subject_id DiagnosticReport.subject Reference(Patient)
note_date DiagnosticReport.effective[x] dateTime|Period
note_datetime DiagnosticReport.effective[x] dateTime|Period
note_class DiagnosticReport.category CodeableConcept
note_title DiagnosticReport.code.coding[0].display string
note_text DiagnosticReport.conclusion string|base64Binary
encoding DiagnosticReport.presentedForm[].contentType code
language DiagnosticReport.presentedForm[].language code
performer_id DiagnosticReport.performer[0] Reference(Practitioner)
encounter_id DiagnosticReport.encounter Reference(Encounter)
Condition: DiagnosticReport.conclusion is non-empty or DiagnosticReport.presentedForm contains text-extractable attachments

Fields (16)

  • note_id integer · id
    PKrequired
    Surrogate key. Hash/sequence of DiagnosticReport.id + content source indicator (conclusion vs presentedForm index). Must be unique across all note sources.
  • person_id DiagnosticReport.subject integer · Reference(Patient)
    FK→PERSONrequired
    Resolve Patient/{id} reference to integer person_id. Skip row if unresolvable.
  • note_date DiagnosticReport.effective[x] date · dateTime|Period
    required
    Date component of effectiveDateTime, or effectivePeriod.start. If absent, fall back to DiagnosticReport.issued (instant). Skip resource if no date available.
  • note_datetime DiagnosticReport.effective[x] datetime · dateTime|Period
    Full timestamp from effectiveDateTime, effectivePeriod.start, or issued. Set time to midnight if not given.
  • note_type_concept_id DiagnosticReport.category integer · CodeableConcept
    FK→CONCEPTrequiredmap:note_type
    Maps the report's provenance/origin. Use 32817 (EHR) as the default. This field captures where the note came from (the source system), not what kind of note it is (that is note_class_concept_id).
    2 sources ▾
  • note_class_concept_id DiagnosticReport.category integer · CodeableConcept
    FK→CONCEPTrequiredmap:note_class
    Maps the report category to a LOINC Document Type concept per OMOP CDM specification. Map LAB -> 44814645 (Note), RAD -> 44814641 (Radiology report), PAT -> 44814642 (Pathology report). 0 if unmappable.
    2 sources ▾
  • note_title DiagnosticReport.code.coding[0].display varchar(250) · string
    Display text of the report's primary code (typically LOINC). E.g. 'History and physical note'. Truncate to 250 characters. Fall back to DiagnosticReport.code.text or the LOINC code itself.
  • note_text DiagnosticReport.conclusion | DiagnosticReport.presentedForm[].data varchar(MAX) · string|base64Binary
    required
    For conclusion: use the string directly. For presentedForm: base64-decode the data field. Only text/plain content types stored directly; other types require extraction or conversion. If both exist, create separate note rows or concatenate.
    3 sources ▾
  • encoding_concept_id DiagnosticReport.presentedForm[].contentType integer · code
    FK→CONCEPTrequired=32678
    32678 = UTF-8. FHIR strings are always UTF-8. For presentedForm, check contentType and encoding; default to 32678 for text/plain.
  • language_concept_id DiagnosticReport.presentedForm[].language | DiagnosticReport.language integer · code
    FK→CONCEPTrequiredmap:language
    Map BCP-47/ISO 639 language codes to OMOP concepts descended from 4182347 (World Languages). en/en-US -> 4180186, de -> 4182948, fr -> 4181536, es -> 4182511. Fall back to DiagnosticReport.language. 0 if absent.
  • provider_id DiagnosticReport.performer[0] integer · Reference(Practitioner)
    FK→PROVIDER
    First performer reference resolved to provider_id. Filter to Practitioner-typed references. resultsInterpreter[0] is an alternative source.
    2 sources ▾
  • visit_occurrence_id DiagnosticReport.encounter integer · Reference(Encounter)
    FK→VISIT_OCCURRENCE
    Resolve Encounter/{id} to visit_occurrence_id. If unresolvable, log warning but still create the note row with null.
    2 sources ▾
  • visit_detail_id integer
    FK→VISIT_DETAIL
    Leave null unless visit details are modeled.
  • note_source_value DiagnosticReport.category.coding[0].code varchar(50) · code
    The source value mapped to note_class_concept_id. Store the raw category code (e.g. 'LAB', 'RAD', 'PAT').
  • note_event_id integer
    If the note should be linked to a corresponding domain-routed row (e.g. the measurement_id or procedure_occurrence_id generated from the same DiagnosticReport), store that row's primary key here.
  • note_event_field_concept_id integer
    FK→CONCEPT
    The CONCEPT_ID identifying which table note_event_id references. Use: 1147138 (measurement.measurement_id), 1147127 (observation.observation_id), 1147082 (procedure_occurrence.procedure_occurrence_id). Leave null if note_event_id is null.

Vocabularies

note_type

Source Display Concept ID Concept Name
EHR EHR (default) 32817 EHR
EHR encounter EHR encounter record 32827 EHR encounter record
EHR admin EHR administration 32821 EHR administration

note_class

Source Display Concept ID Concept Name
LAB Laboratory 44814645 Note
RAD Radiology 44814641 Radiology report
PAT Pathology 44814642 Pathology report
MB Microbiology 44814645 Note
OTH Other 44814645 Note
(absent) No category 0 No matching concept

encoding

Source Display Concept ID Concept Name
UTF-8 UTF-8 32678 UTF-8
ASCII ASCII 0 No standard OMOP concept
(other) Other encoding 0 No standard OMOP concept

language

Source Display Concept ID Concept Name
en English 4180186 English language
de German 4182948 German language
fr French 4181536 French language
es Spanish 4182511 Spanish language
pt Portuguese 4181898 Portuguese language
zh Chinese 4181721 Chinese language
(absent) No language specified 0 No matching concept

note_event_field

Source Display Concept ID Concept Name
measurement measurement.measurement_id 1147138 measurement.measurement_id
observation observation.observation_id 1147127 observation.observation_id
procedure_occurrence procedure_occurrence.procedure_occurrence_id 1147082 procedure_occurrence.procedure_occurrence_id

Edge Cases

Empty conclusion (empty string or whitespace only)
Do not create a note row. note_text is required and must be non-empty. Check conclusion.trim().length > 0 before creating the row.
Binary attachment (application/pdf, image/png, application/dicom)
Skip the attachment for note creation. Log a warning. OMOP note_text is designed for free text, not binary content. Consider extracting text from PDFs with a dedicated parser.
Multiple presentedForm entries
Create one note row per text-extractable attachment, each with a unique note_id. Differentiate via note_id generation strategy (hash of DiagnosticReport.id + attachment index).
Large text (presentedForm > 1MB)
OMOP note_text is varchar(MAX). However, downstream NLP tools may have size limits. Consider truncating or splitting at a configurable maximum with a log warning.
No contentType on presentedForm
Assume text/plain if the data field is present and decodable as valid UTF-8 text. Otherwise skip the attachment.
presentedForm.url without data
The attachment references an external document. Either fetch the content at ETL time and inline it, or skip the attachment with a log warning.
Base64 decoding failure
Skip the attachment and log an error. The data field may be corrupted or not valid base64.
Encoding mismatch (non-UTF-8 text)
Attempt to detect encoding and transcode to UTF-8 before storing. Set encoding_concept_id = 32678 after successful transcoding. If transcoding fails, use 0.
presentedForm.language differs across attachments
Each note row should reflect the language of its specific attachment. Do not assume all attachments share the same language.
Status is preliminary, registered, or entered-in-error
ETL-German rejects non-final statuses. For notes, preliminary reports may still contain valuable narrative. Consider accepting preliminary notes but tagging them. Reject entered-in-error.
subject references Group (not Patient)
Not supported. OMOP requires a single person_id. Skip the resource.
No effective[x] and no issued
No date available. OMOP note_date is required. Skip the resource and log a warning.
DiagnosticReport has conclusion but no conclusionCode
Still create a note row. The conclusion text is independent of the structured conclusionCode. A report can have a textual interpretation without coded findings.
HTML content in presentedForm (text/html)
Strip HTML tags to produce plain text for note_text, or store HTML as-is for NLP pipelines that can process it. Document the approach in ETL conventions.
Both conclusion and presentedForm text exist
Option A (recommended): Create two separate note rows. Option B: Concatenate with delimiter. Option C: Prefer presentedForm as primary note, store conclusion in note_title.

Reference Implementations