References/Observation: Difference between revisions
More languages
More actions
OHC identity seed |
Automated edit (via update-page on MediaWiki MCP Server) |
||
| Line 444: | Line 444: | ||
* On read, the list and detail specs blank out <code>encounter</code>, <code>patient</code>, and <code>questionnaire_response</code>, so don't rely on those being populated in observation payloads. |
* On read, the list and detail specs blank out <code>encounter</code>, <code>patient</code>, and <code>questionnaire_response</code>, so don't rely on those being populated in observation payloads. |
||
* Prefer structured observations. Allergy and medication checks read coded values, not free text, so unstructured data leaves them blind. |
* Prefer structured observations. Allergy and medication checks read coded values, not free text, so unstructured data leaves them blind. |
||
{{Navbox clinical}} |
|||
{{Related}} |
{{Related}} |
||
Wiki: ohcnwiki.tellmey.fyi |
|||
Latest revision as of 09:33, 6 July 2026
An Observation is one named, coded data point about a subject — almost always a patient: a blood pressure reading, a temperature, a coded answer captured through a questionnaire. main_code records what was observed (usually bound to LOINC) and value holds the result. You rarely create one by hand; most observations are materialized when a questionnaire is submitted.
Source:
- Model: [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/observation.py
care/emr/models/observation.py] - Resource spec: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/observation/spec.py
care/emr/resources/observation/spec.py] - Value sets: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/observation/valueset.py
care/emr/resources/observation/valueset.py]
The model is just storage; several fields are opaque JSONFields. Their real structure lives in the Pydantic resource specs under care/emr/resources/observation/, which own the enums, nested JSON shapes, validation, and the read/write schema split. Each field row below gives the storage column first, then the shape the spec enforces on top of it.
Models
| Model | Purpose |
|---|---|
Observation
|
A single coded measurement or finding about a patient, captured within an encounter |
Observation extends EMRBaseModel, the shared Care EMR base that supplies external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.
Observation fields
Classification & coding
| Field | Storage type | Spec shape / notes |
|---|---|---|
status
|
CharField(255)
|
Enum ObservationStatus — final, amended, entered_in_error. Required in specs. Questionnaire-sourced observations land as final
|
is_group
|
BooleanField
|
Default False. True marks a grouping/panel observation that holds component/child observations instead of a single value. Not exposed in the resource specs
|
category
|
JSONField (default {})
|
Spec: optional Coding { system?, version?, code, display? }. FHIR observation category, derived from the questionnaire
|
main_code
|
JSONField (default {})
|
Spec: optional Coding. What is being observed, typically LOINC (CARE_OBSERVATION_VALUSET, system http://loinc.org)
|
alternate_coding
|
JSONField (default [])
|
Spec: optional CodeableConcept { id?, coding?: Coding[], text }. Same concept expressed in other code systems
|
Subject & context
| Field | Storage type | Spec shape / notes |
|---|---|---|
subject_type
|
CharField(255)
|
Enum SubjectType — patient, encounter. Spec defaults to encounter
|
subject_id
|
UUIDField
|
The subject entity's id. Set server-side; not in the spec body |
patient
|
FK → Patient
|
CASCADE. Set server-side, not in the spec body
|
encounter
|
FK → Encounter
|
None. Where the observation was recorded |
effective_datetime
|
DateTimeField
|
When the observation was effective. Nullable in storage, but the spec requires a tz-aware datetime on create; optional on update
|
Author & performer
| Field | Storage type | Spec shape / notes |
|---|---|---|
data_entered_by
|
FK → users.User
|
CASCADE, nullable. related_name="observations_entered". Write specs accept data_entered_by_id: int; read specs serialize a nested UserSpec
|
performer
|
JSONField (default {})
|
user), id: str }. Use it when the performer differs from the data-entry user |
Value
| Field | Storage type | Spec shape / notes |
|---|---|---|
value_type
|
CharField(255)
|
Enum QuestionType (from the questionnaire module). Tells the client how to read value — see the table below. Required in specs
|
value
|
JSONField
|
Spec: QuestionnaireSubmitResultValue { value?: str, unit?: Coding, coding?: Coding }. Required on create, optional on update. value holds the raw/string result, unit the quantity unit, coding the coded answer
|
note
|
TextField
|
None. Free-text note |
Clinical qualifiers
| Field | Storage type | Spec shape / notes |
|---|---|---|
body_site
|
JSONField (default {})
|
Spec: optional Coding bound to CARE_BODY_SITE_VALUESET (slug system-body-site-observation, SNOMED CT). FHIR Observation.bodySite
|
method
|
JSONField (default {})
|
Spec: optional Coding bound to CARE_OBSERVATION_COLLECTION_METHOD (slug system-collection-method, SNOMED CT). FHIR Observation.method
|
reference_range
|
JSONField (default [])
|
Spec: ReferenceRange[] (shape below), default []. Reference ranges for the value, usually carried over from the observation definition
|
interpretation
|
JSONField (default {})
|
Spec: free dict, default {}. Coded interpretation against the reference range (high/low/normal). Normal/abnormal/critical value sets exist (below) but are not enforced on this dict
|
interpretation_old
|
CharField(255)
|
Nullable. Legacy free-text interpretation, kept for backward compatibility. Not in the specs |
Structure & relationships
| Field | Storage type | Spec shape / notes |
|---|---|---|
parent
|
UUIDField
|
None — the external_id of a parent observation, for nesting related observations
|
component
|
JSONField (default [])
|
Spec: Component[] (shape below), default []. Sub-observations sharing this context but carrying their own codes and values (FHIR Observation.component)
|
questionnaire_response
|
FK → QuestionnaireResponse
|
None. The submission that produced this observation |
diagnostic_report
|
FK → DiagnosticReport
|
CASCADE, nullable. The report this observation belongs to, if any. Not in the write specs
|
observation_definition
|
FK → ObservationDefinition
|
CASCADE, nullable. The definition this observation templates from. Serialized as BaseObservationDefinitionSpec only in ObservationRetrieveSpec
|
Enum & value-set reference
ObservationStatus values
| Value | Meaning |
|---|---|
final
|
Recorded, complete and verified. The default for questionnaire-sourced observations |
amended
|
Was final, then corrected
|
entered_in_error
|
Recorded in error; disregard it |
PerformerType values (performer.type)
| Value |
|---|
related_person
|
user
|
SubjectType values (subject_type)
| Value |
|---|
patient
|
encounter (spec default)
|
QuestionType values (value_type)
From the questionnaire module. The value tells the client which key of value to read.
| Value | Value sourced from QuestionnaireSubmitResultValue
|
|---|---|
group
|
grouping node (no direct value) |
boolean
|
value ("true"/"false")
|
decimal
|
value
|
integer
|
value
|
string
|
value
|
text
|
value
|
display
|
display-only |
date
|
value
|
dateTime
|
value
|
time
|
value
|
choice
|
coding
|
url
|
value
|
quantity
|
value + unit
|
structured
|
structured payload |
Bound value sets
Coded fields validate against registered Care value sets. A body_site or method coding outside its value set is rejected during deserialization.
| Field | Value set | Slug | System(s) |
|---|---|---|---|
main_code
|
CARE_OBSERVATION_VALUSET
|
system-observation
|
http://loinc.org
|
body_site
|
CARE_BODY_SITE_VALUESET
|
system-body-site-observation
|
http://snomed.info/sct (curated concept list + descendants of 442083009)
|
method
|
CARE_OBSERVATION_COLLECTION_METHOD
|
system-collection-method
|
http://snomed.info/sct (descendants of 272394005, 129264002, 386053000)
|
These interpretation value sets are registered for the frontend and observation definitions, but are not enforced on the interpretation dict:
| Value set | Slug | Concepts (system http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation)
|
|---|---|---|
| Normal | system-normal-coded-valueset
|
N Normal, ND Not detected, NEG Negative
|
| Abnormal | system-abnormal-coded-valueset
|
A Abnormal, AA Critically abnormal, H High, HH Critically high, L Low, LL Critically low, POS Positive, DET Detected
|
| Critical | system-critical-coded-valueset
|
C Critical, CC Critical high, CL Critical low, CV Critical value
|
| UCUM units | system-ucum-units
|
http://unitsofmeasure.org (all UCUM units)
|
Nested JSON shapes (from the spec)
The structures behind the model's JSONFields.
Coding
Coding {
system: str | null # e.g. http://loinc.org
version: str | null
code: str # required
display: str | null
} # extra keys forbidden
CodeableConcept (alternate_coding)
CodeableConcept {
id: str | null
coding: Coding[] | null
text: str | null # field present, required key
} # extra keys forbidden
QuestionnaireSubmitResultValue (value)
QuestionnaireSubmitResultValue {
value: str | null # raw/string value
unit: Coding | null # for quantity values
coding: Coding | null # for coded values
}
Performer (performer)
Performer {
type: PerformerType # related_person | user
id: str
}
ReferenceRange (reference_range[])
ReferenceRange {
min: float | null
max: float | null
unit: str | null
interpretation: str # required
value: str | null
}
Component (component[])
Component {
value: QuestionnaireSubmitResultValue # required
interpretation: str | dict = {}
reference_range: ReferenceRange[] = []
code: Coding | null = null
note: str = ""
}
Resource specs (API schema)
The Pydantic specs build on [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/base.py EMRResource], which provides serialize / de_serialize and the perform_extra_serialization / perform_extra_deserialization hooks. Every concrete spec inherits from BaseObservationSpec.
| Spec | Role | Notes |
|---|---|---|
BaseObservationSpec
|
shared base | Defines the common fields: id, status, category, main_code, alternate_coding, subject_type, encounter, effective_datetime, performer, value_type, value, note, body_site (bound), method (bound), reference_range, interpretation, parent, questionnaire_response, component
|
ObservationSpec
|
write · create | Adds data_entered_by_id, created_by_id, updated_by_id (all int). Used internally when materializing observations from a questionnaire submission
|
ObservationUpdateSpec
|
write · update | The base, but effective_datetime and value relax to optional (None) for partial updates
|
ObservationReadSpec
|
read · list | Swaps the user IDs for nested created_by / updated_by / data_entered_by UserSpec objects
|
ObservationRetrieveSpec
|
read · detail | Extends ObservationReadSpec and additionally serializes observation_definition via BaseObservationDefinitionSpec when it is set
|
Server-side behaviour
ObservationSpec.perform_extra_deserialization(create/update): setsobj.external_id = self.id, copiesdata_entered_by_id,created_by_id,updated_by_idonto the model, dropsdata_entered_by_idfrommeta, and on create (is_updateis false) clearsobj.idso a new row is inserted.ObservationReadSpec.perform_extra_serialization: mapsidfromexternal_id; deliberately blanksencounter,patient, andquestionnaire_responsetoNonein the output to avoid extra DB queries; resolves the audit users (created_by,updated_by) anddata_entered_byfrom cache (model_from_cache).ObservationRetrieveSpec.perform_extra_serialization: calls the parent, then inlines the fullobservation_definitionwhen the FK is set.body_site/methodareValueSetBoundCoding[...]: the suppliedCodingis validated against its bound value set during deserialization and rejected if the code is not a member.de_serializeonly writes fields present in the model's column mapping. Non-default values are persisted (model_dump(exclude_defaults=True)), and unknown spec-only fields would land inmetawhen__store_metadata__is set — which, by default, it is not for observations.
Related models
Observation is self-contained; it expresses relationships through foreign keys rather than secondary tables.
patient → FK Patient (CASCADE)
encounter → FK Encounter (CASCADE)
data_entered_by → FK users.User (CASCADE, nullable)
questionnaire_response → FK QuestionnaireResponse (CASCADE, nullable)
diagnostic_report → FK DiagnosticReport (CASCADE, nullable)
observation_definition → FK ObservationDefinition (CASCADE, nullable)
parent is the exception: a soft, UUID-based self-reference to another observation's external_id, not a Django FK. It lets observations nest without a database constraint.
API integration notes
- Observations are exposed through Care's REST API and align with the FHIR
Observationresource. FHIR field names (effectiveDateTime,bodySite, and so on) differ from the Django model names; the resource specs above are the authoritative request/response schemas. - Most observations are created as a side effect of submitting a questionnaire.
valueandvalue_typemirror the submission types (QuestionnaireSubmitResultValue/QuestionType), andquestionnaire_responserecords provenance. effective_datetimemust be timezone-aware. Required on create, optional on update.statusis one offinal/amended/entered_in_error; questionnaire-sourced observations arefinal.body_siteandmethodare validated against their bound value sets, andmain_codeis expected to bind to LOINC viaCARE_OBSERVATION_VALUSET.- On read, the list and detail specs blank out
encounter,patient, andquestionnaire_response, so don't rely on those being populated in observation payloads. - Prefer structured observations. Allergy and medication checks read coded values, not free text, so unstructured data leaves them blind.
Related
Pages that link here:
- Reference: References/Base models & conventions
Wiki: ohcnwiki.tellmey.fyi