Skip to main content
Version: 3.1

Diagnostic Report

A DiagnosticReport records the results and interpretation of a diagnostic test, tied to the ServiceRequest that ordered it. You create one to report on an investigation, then read it back with its observations, encounter, and requester embedded.

The Django model (care/emr/models/diagnostic_report.py) imposes no shape: it persists category and code as opaque JSONFields and status as a free CharField. Shape and validation come from the Pydantic resource specs (care/emr/resources/diagnostic_report/) — the status enum, the coded-concept structure, the bound value sets, and the separate read/write API schemas. The specs build on EMRResource and serialize between the API and the model.

Source:

Models

ModelPurpose
DiagnosticReportResults and interpretation of a diagnostic test or investigation, tied to a ServiceRequest

DiagnosticReport extends EMRBaseModel, the shared Care EMR base that provides external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.

DiagnosticReport fields

Classification

FieldModel typeSpec shapeNotes
statusCharField(255)DiagnosticReportStatusChoices (enum, required)Lifecycle status. Stored as free text but constrained to the enum below on write. Optional on update.
categoryJSONField (null)Coding { system, version?, code, display? }, requiredService section the report belongs to. code is validated against the Diagnostic Service Sections value set.
codeJSONField (null)Coding { system, version?, code, display? }, optionalThe test or panel performed. code is validated against the Observation value set (LOINC). Nullable.

The Coding shape (from care/emr/resources/common/coding.py, extra="forbid"):

FieldTypeRequiredNotes
systemstroptionalCode system URI (e.g. http://terminology.hl7.org/CodeSystem/v2-0074, http://loinc.org)
versionstroptionalCode system version
codestrrequiredThe code; validated against the bound value set
displaystroptionalHuman-readable label

Narrative

FieldModel typeSpec shapeNotes
noteTextField (null)str | NoneFree-text comments. Not parsed by the platform.
conclusionTextField (null)str | NoneClinical interpretation/summary of the results. Not parsed by the platform.

Relationships

FieldTypeNotes
facilityFK → facility.FacilityPROTECT, nullable. Facility the report is scoped to. Set server-side; not a writable spec field.
patientFK → emr.PatientCASCADE. Subject of the report. Set server-side; not a writable spec field.
encounterFK → emr.EncounterCASCADE. Encounter the report was produced under. Set server-side; not a writable spec field.
service_requestFK → emr.ServiceRequestCASCADE. The order this report fulfils. Write-only on create (resolved from external_id); excluded from base serialization via __exclude__.

Enum values

DiagnosticReportStatusChoices

From spec.py. (modified is defined in source but commented out and is not an active value.)

ValueMeaning
registeredReport exists / has been registered
partialSome results available, report incomplete
preliminaryProvisional results pending verification
finalVerified, complete report

Bound value sets

Coded fields validate their code against a registered Care value set (ValueSetBoundCoding). A code outside the bound set is rejected at deserialization.

FieldValue setSlugIncluded system(s)
categoryDiagnostic Service Sectionssystem-diagnostic-service-sections-codehttp://terminology.hl7.org/CodeSystem/v2-0074
codeObservationsystem-observationhttp://loinc.org

Resource specs (API schema)

Every spec extends EMRResource (care/emr/resources/base.py) through DiagnosticReportSpecBase. serialize builds the read payload from the model (mapping external_idid); de_serialize writes spec fields back to the model. The perform_extra_serialization/perform_extra_deserialization hooks handle server-side behaviour — resolving foreign keys on write, embedding related resources on read.

Spec classRoleFields / behaviour
DiagnosticReportSpecBaseshared baseid?, status (enum, required), category (bound Coding, required), code? (bound Coding), note?, conclusion?. __model__ = DiagnosticReport, __exclude__ = ["service_request"].
DiagnosticReportCreateSpecwrite · createBase + service_request: UUID4 (required, the request's external_id). perform_extra_deserialization resolves it via get_object_or_404(ServiceRequest, external_id=...) and sets obj.service_request.
DiagnosticReportUpdateSpecwrite · updateBase, but status is optional (DiagnosticReportStatusChoices | None). service_request is not writable on update.
DiagnosticReportListSpecread · listBase + created_date, modified_date, service_request: dict | None. perform_extra_serialization sets id = external_id and embeds the linked request via BaseServiceRequestSpec.serialize(...).
DiagnosticReportRetrieveSpecread · detailExtends ListSpec + observations: list[dict], encounter: dict, created_by?, updated_by?, requester?. See serialization below.

DiagnosticReportRetrieveSpec.perform_extra_serialization (server-side, read)

The detail read enriches the list payload so a viewer gets the report, its observations, and its context in one call:

  • Calls the ListSpec hook (sets id, embeds service_request).
  • serialize_audit_users → populates created_by / updated_by from cache (UserSpec).
  • observations → all Observation rows where diagnostic_report == obj, each via ObservationRetrieveSpec.serialize(...).
  • requester → if service_request.requester_id is set, loaded from cache via UserSpec.
  • encounterEncounterListSpec.serialize(obj.encounter), with encounter["patient"] set to PatientRetrieveSpec.serialize(obj.encounter.patient, facility=obj.facility).

These embedded fields (service_request, observations, encounter, requester, audit users) are read-only and are not accepted on create/update.

DiagnosticReport is the only model defined in this file. It sits downstream of a ServiceRequest and is anchored to a patient and encounter:

diagnostic_report
facility → FK facility.Facility (PROTECT, nullable)
patient → FK emr.Patient (CASCADE)
encounter → FK emr.Encounter (CASCADE)
service_request → FK emr.ServiceRequest (CASCADE)

Deleting a Patient, Encounter, or ServiceRequest cascades to its diagnostic reports; the facility link is PROTECTed. Routine deletes are soft, via EMRBaseModel. On retrieve, related Observation records are pulled in through the reverse FK (Observation.diagnostic_report).

Methods & save behaviour

  • Create (DiagnosticReportCreateSpec.de_serialize): writes status, category, code, note, conclusion onto the model; perform_extra_deserialization resolves service_request from its external_id (404 if missing). facility, patient, and encounter come from the request context in the view layer, not the payload.
  • Update (DiagnosticReportUpdateSpec): same base fields, but status is optional, so a partial status change is allowed. service_request cannot be reassigned.
  • Read (List/Retrieve): no model writes; all enrichment happens in perform_extra_serialization (see above). User and service-request lookups go through the serializer cache (model_from_cache).
  • Coded fields are validated at deserialization: category.code must be in the Diagnostic Service Sections value set; code.code (when present) must be in the Observation value set. status must be a DiagnosticReportStatusChoices value.

API integration notes

  • Diagnostic reports are exposed through Care's REST API and align with the FHIR DiagnosticReport resource; payload field names follow the spec classes above.
  • category and code are coded concepts (Coding: system/code/display), not free strings. Populate code from the bound value set — anything outside it is rejected, and Coding forbids extra keys.
  • status is stored as a free CharField, but the spec restricts writes to the DiagnosticReportStatusChoices enum (registered, partial, preliminary, final).
  • A report fulfils exactly one service_request. Create the Service Request first, then pass its external_id as service_request on create.
  • note and conclusion carry human-readable narrative and are not parsed by the platform.
  • The detail (Retrieve) payload embeds the linked service request, its requester, the encounter (with patient), audit users, and all child observations — none of which are writable.