References/Condition
More languages
More actions
A Condition records a clinical problem, diagnosis, or symptom against a patient. It maps to the FHIR Condition resource and shows up in the Care product UI as Symptoms.
Source:
- Model: [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/condition.py
care/emr/models/condition.py] - Resource spec: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/condition/spec.py
care/emr/resources/condition/spec.py] - Value set: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/condition/valueset.py
care/emr/resources/condition/valueset.py]
A Condition lives across two layers, and the split matters when you read the code:
- The Django model (
care/emr/models/condition.py) is storage. Its coded and timing fields (code,body_site,onset,abatement) are opaqueJSONFields — the model says nothing about their shape. - The Pydantic resource specs (
care/emr/resources/condition/spec.py) are the API. They define the enums, the structure inside each JSON field, field validation, the value-set binding forcode, and the separate read and write schemas.
Models
| Model | Purpose |
|---|---|
Condition
|
A clinical condition, problem, diagnosis, or symptom recorded for a patient |
Condition 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.
Condition fields
Status & classification
| Field | Type | Notes
{{Field|model=Condition|section=Status & classification|name=clinical_status|type=CharField(100), nullable|notes=Clinical state. Spec binds to [[#clinicalstatuschoices-values}} {{Field|model=Condition|section=Status & classification|name=verification_status|type=CharField(100), nullable|notes=Level of certainty. Spec binds to [[#verificationstatuschoices-values}} {{Field|model=Condition|section=Status & classification|name=category|type=CharField(100), nullable|notes=Classification. Spec binds to [[#categorychoices-values}} {{Field|model=Condition|section=Status & classification|name=severity|type=CharField(100), nullable|notes=Subjective severity. Spec binds to [[#severitychoices-values}} |
|---|
Coded concepts
| Field | Type | Notes
{{Field|model=Condition|section=Coded concepts|name=code|type=JSONField (default=dict, not null/blank)|notes=The condition itself. On write, a single [[#coding-shape}} |
|---|---|---|
body_site |
JSONField (default=dict, not null/blank) |
Anatomical location(s). Exists on the model but no current spec exposes it — never written or read through the standard Condition API.
|
Timing
| Field | Type | Notes
{{Field|model=Condition|section=Timing|name=onset|type=JSONField (default=dict)|notes=Choice-of-type onset, shaped by [[#conditiononsetspec-onset-shape}} {{Field|model=Condition|section=Timing|name=abatement|type=JSONField (default=dict)|notes=When the condition resolved or went into remission. Shaped by [[#conditionabatementspec-abatement-shape}} |
|---|---|---|
recorded_date |
DateTimeField, nullable |
When the condition was first recorded. Not exposed by the current specs. |
Context & notes
| Field | Type | Notes |
|---|---|---|
patient |
FK → Patient, on_delete=CASCADE |
Subject of the condition. Derived server-side from the encounter on create, never read from the client (__exclude__).
|
encounter |
FK → Encounter, nullable, on_delete=CASCADE |
Encounter the condition was recorded in. Set server-side on create (__exclude__); the client supplies a UUID in the spec, which is validated to exist.
|
note |
TextField, nullable |
Free-text clinical note. |
Enums
Every enum is a str, Enum in care/emr/resources/condition/spec.py. The value stored and serialized is the string in the table.
ClinicalStatusChoices values
| Value |
|---|
active
|
recurrence
|
relapse
|
inactive
|
remission
|
resolved
|
unknown
|
VerificationStatusChoices values
| Value |
|---|
unconfirmed
|
provisional
|
differential
|
confirmed
|
refuted
|
entered_in_error
|
CategoryChoices values
| Value |
|---|
problem_list_item
|
encounter_diagnosis
|
chronic_condition
|
SeverityChoices values
| Value |
|---|
mild
|
moderate
|
severe
|
Nested JSON shapes
These spec classes (all extend EMRResource) are the real structure behind the model's JSON fields.
ConditionOnSetSpec (onset shape)
| Field | Type | Default | Notes |
|---|---|---|---|
onset_datetime |
datetime |
None
| |
onset_age |
int |
None
| |
onset_string |
str |
None
| |
note |
str |
None
|
ConditionAbatementSpec (abatement shape)
| Field | Type | Default | Notes |
|---|---|---|---|
abatement_datetime |
datetime |
None
| |
abatement_age |
int |
None
| |
abatement_string |
str |
None
| |
note |
str |
None
|
Coding shape
code is a single [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/common/coding.py Coding], not a CodeableConcept. extra="forbid".
| Field | Type | Notes |
|---|---|---|
system |
str |
Code system URI (e.g. http://snomed.info/sct).
|
version |
str |
Code system version. |
code |
str |
Required. The code value. |
display |
str |
Human-readable label. |
code value-set binding
On write, code is typed ValueSetBoundCoding[CARE_CODITION_CODE_VALUESET.slug] — a Coding checked against the Condition code value set (care/emr/resources/condition/valueset.py, slug system-condition-code), which covers SNOMED CT concepts that are is-a 404684003 (Clinical finding). Codes outside it are rejected on ConditionSpec, ConditionUpdateSpec, and ChronicConditionUpdateSpec. The read spec falls back to a plain Coding and skips value-set validation, so reads stay cheap.
Resource specs (API schema)
Every spec extends BaseConditionSpec → EMRResource and round-trips through serialize (DB → Pydantic) and de_serialize (Pydantic → DB). BaseConditionSpec sets __model__ = Condition, sets __exclude__ = ["patient", "encounter"] (both server-maintained, never trusted from the client), and exposes id: UUID4.
| Spec class | Role | Exposes / behaviour |
|---|---|---|
BaseConditionSpec
|
shared base | id; excludes patient and encounter from direct mapping.
|
ConditionSpec
|
write · create | clinical_status?, verification_status (required), severity?, code (value-set bound, required), encounter (UUID4, required), onset (={}), abatement (={}), note?, category (required). Validates that the encounter exists; on create sets obj.encounter and obj.patient = encounter.patient.
|
ConditionUpdateSpec
|
write · update | clinical_status?, verification_status (required), severity?, code (value-set bound, required), onset (={}), abatement (={}), note?. Does not accept encounter, category, or patient.
|
ChronicConditionUpdateSpec
|
write · update (chronic) | Extends ConditionUpdateSpec and adds encounter (UUID4). On deserialize, if encounter is set, resolves it (get_object_or_404) and assigns obj.encounter.
|
ConditionReadSpec
|
read · detail/list | clinical_status, verification_status, category, criticality, severity (all plain str), code (plain Coding), encounter (UUID4), onset, abatement, created_by?, updated_by?, note?, created_date, modified_date.
|
Validation & server-side behaviour
- Encounter is required and verified on create.
ConditionSpec.validate_encounter_existsrejects unknown encounter UUIDs;perform_extra_deserialization(create only) loads the encounter and derivespatientfrom it. Clients never setpatientorencounterdirectly. verification_statusis mandatory on bothConditionSpecandConditionUpdateSpec;categoryis mandatory only on create (ConditionSpec).codemust belong to the bound value set (SNOMED CT clinical findings) on every write spec.onset_datetimecannot be in the future and is forced timezone-aware;abatement_datetimecarries no such constraint.ConditionReadSpecexposescriticality, a string in the read schema with no backing column on theConditionmodel. The standard serializer never populates it, so it comes back unset.- Read serialization (
perform_extra_serialization) mapsid = external_id, replacesencounterwith itsexternal_id, and expandscreated_by/updated_byviaserialize_audit_users(cachedUserSpec). body_siteandrecorded_dateexist on the model but no current spec reads or writes them — storage-only, outside the standardConditionAPI surface.
Related models
Condition links to two clinical records:
patient → FK Patient (CASCADE, server-derived from encounter)
encounter → FK Encounter (CASCADE, nullable; required on create)
Deleting a Patient or Encounter cascades to its Condition rows. The column allows a null encounter, but the standard create flow always supplies one and derives patient from it.
Methods & save behaviour
serialize(obj)andde_serialize(obj)(fromEMRResource) convert between theConditionmodel and the spec. Field mapping uses the model's non-FK column names;__exclude__(patient,encounter) andid/external_idare skipped during deserialization.ConditionSpec.perform_extra_deserialization(is_update=False, obj)— create path: resolves the encounter and setsobj.patient = obj.encounter.patient.ChronicConditionUpdateSpec.perform_extra_deserialization— resolves and assignsencounterwhen provided.ConditionReadSpec.perform_extra_serialization— setsid/encounterexternal ids and serializes audit users.external_id, audit fields,meta/history, and soft-delete (deleted) are platform-maintained viaEMRBaseModel.
API integration notes
- Send
codeas a structuredCoding({system, code, display}) drawn from the SNOMED CT clinical-finding value set, not as free text. Always includeverification_status; addcategoryandencounterwhen creating. onsetandabatementare structured choice-of-type objects (*_datetime,*_age,*_string,note), not arbitrary JSON.onset_datetimemust be timezone-aware and not in the future.- Don't send
patient,external_id, audit fields, ordeletedfrom the client — the server owns them.
Related
Pages that link here:
- Reference: References/Base models & conventions
Wiki: ohcnwiki.tellmey.fyi