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 · idPKrequiredSurrogate key. Hash/sequence of DiagnosticReport.id + content source indicator (conclusion vs presentedForm index). Must be unique across all note sources. -
person_id←DiagnosticReport.subjectinteger · Reference(Patient)FK→PERSONrequiredResolve Patient/{id} reference to integer person_id. Skip row if unresolvable. -
note_date←DiagnosticReport.effective[x]date · dateTime|PeriodrequiredDate 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|PeriodFull timestamp from effectiveDateTime, effectivePeriod.start, or issued. Set time to midnight if not given. -
note_type_concept_id←DiagnosticReport.categoryinteger · CodeableConceptFK→CONCEPTrequiredmap:note_typeMaps 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 ▾
-
Set from DocumentReference.type via LOINC→NoteType enum mapping (OmopNoteTypeMapping); defaults to concept from type lookup
-
Field listed as required but set to null in skeleton mapping
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/010-DiagnosticReport-note.sh:14-29
-
-
note_class_concept_id←DiagnosticReport.categoryinteger · CodeableConceptFK→CONCEPTrequiredmap:note_classMaps 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 ▾
-
Defaults to Concept(0); note_class_concept_id is technically non-compliant when 0 (required field)
-
Field listed as required but set to null in skeleton mapping
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/010-DiagnosticReport-note.sh:14-29
-
-
note_title←DiagnosticReport.code.coding[0].displayvarchar(250) · stringDisplay 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[].datavarchar(MAX) · string|base64BinaryrequiredFor 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 ▾
-
Maps DocumentReference.content.attachment.data (text/plain only); rejects non-text/plain; concatenates multiple attachments
-
Field listed but set to null; example shows Synthea presentedForm text/plain base64-encoded content
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/010-DiagnosticReport-note.sh:119-124
-
BuildNote method exists but note creation is commented out (//TMP: NOTE)
- FhirToCdm(csharp) refs/refs/FhirToCdm/CdmPersonBuilder.cs:700-710 — line 358 comment: //TMP: NOTE
-
-
encoding_concept_id←DiagnosticReport.presentedForm[].contentTypeinteger · codeFK→CONCEPTrequired=3267832678 = 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.languageinteger · codeFK→CONCEPTrequiredmap:languageMap 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→PROVIDERFirst performer reference resolved to provider_id. Filter to Practitioner-typed references. resultsInterpreter[0] is an alternative source.2 sources ▾
-
Resolved from DocumentReference.author[0] (Practitioner reference only)
-
Field listed but set to null; no performer resolution
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/010-DiagnosticReport-note.sh:14-29
-
-
visit_occurrence_id←DiagnosticReport.encounterinteger · Reference(Encounter)FK→VISIT_OCCURRENCEResolve Encounter/{id} to visit_occurrence_id. If unresolvable, log warning but still create the note row with null.2 sources ▾
-
Resolved from DocumentReference.context.encounter[0]
-
Field listed but set to null; no encounter resolution
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/010-DiagnosticReport-note.sh:14-29
-
-
visit_detail_id← — integerFK→VISIT_DETAILLeave null unless visit details are modeled. -
note_source_value←DiagnosticReport.category.coding[0].codevarchar(50) · codeThe source value mapped to note_class_concept_id. Store the raw category code (e.g. 'LAB', 'RAD', 'PAT'). -
note_event_id← — integerIf 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← — integerFK→CONCEPTThe 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
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/DiagnosticReportMapper.java — Primary reference for DiagnosticReport mapping, but does not produce note rows. Only handles conclusionCode for domain routing.
- fhir-to-omop-demo(bash) refs/refs/fhir-to-omop-demo/data/convert/mapping/010-DiagnosticReport-note.sh — Skeleton with all 16 note fields listed but all set to null. Contains example DiagnosticReport with presentedForm base64-encoded text/plain content.
- omoponfhir-v54-r4(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopDocumentReference.java:225-389 — Closest reference for FHIR narrative to OMOP note mapping. Maps DocumentReference (not DiagnosticReport) with subject, author, encounter, attachment data (text/plain only).
- omoponfhir-v54-r4(java) refs/refs/omoponfhir-v54-r4/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopNoteTypeMapping.java:19-28 — 10 Note Type to LOINC concept mappings.
- FhirToCdm(csharp) refs/refs/FhirToCdm/CdmPersonBuilder.cs — Note entity exists, BuildNote method at line 700, but note creation is commented out (//TMP: NOTE at line 358).
- fhir-omop-ig(fsh) refs/refs/fhir-omop-ig/input/fsh/Note.fsh — HL7 official Note logical model with all 16 fields. No FML transformation rules for DiagnosticReport->note.