Clinical care
Observation
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 and value holds the result.…
3.1 · versioned documentation · 9 min read
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:
care/emr/models/observation.py - Resource spec:
care/emr/resources/observation/spec.py - Value sets:
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 MANSOQ 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 (MANSOQ_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 |
CASCADE. Spec field is UUID4 | 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 {}) |
Spec: optional Performer { type: PerformerType (related_person|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 |
Spec: str | None. Free-text note |
Clinical qualifiers
| Field | Storage type | Spec shape / notes |
|---|---|---|
body_site |
JSONField (default {}) |
Spec: optional Coding bound to MANSOQ_BODY_SITE_VALUESET (slug system-body-site-observation, SNOMED CT). FHIR Observation.bodySite |
method |
JSONField (default {}) |
Spec: optional Coding bound to MANSOQ_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 |
Nullable. Spec: UUID4 | 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 |
CASCADE, nullable. Spec field is UUID4 | 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 MANSOQ value sets. A body_site or method coding outside its value set is rejected during deserialization.
| Field | Value set | Slug | System(s) |
|---|---|---|---|
main_code |
MANSOQ_OBSERVATION_VALUSET |
system-observation |
http://loinc.org |
body_site |
MANSOQ_BODY_SITE_VALUESET |
system-body-site-observation |
http://snomed.info/sct (curated concept list + descendants of 442083009) |
method |
MANSOQ_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 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 MANSOQ'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 viaMANSOQ_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
- Reference: Observation Definition
- Reference: Diagnostic Report
- Reference: Questionnaire
- Reference: Encounter
- Reference: Patient
- Reference: Base Model
- Source: observation.py on GitHub
- Source: observation/spec.py on GitHub
- Source: observation/valueset.py on GitHub
- Source: resources/base.py on GitHub
Source evidence
References are pinned to the private MANSOQ source repository. Repository access is required to open these links.