Clinical care
Condition
A Condition records a clinical problem, diagnosis, or symptom against a patient. It maps to the FHIR Condition resource and shows up in the MANSOQ product UI as Symptoms. Source: - Model: care/emr/models/condition.py - Resource spec:…
3.1 · versioned documentation · 7 min read
A Condition records a clinical problem, diagnosis, or symptom against a patient. It maps to the FHIR Condition resource and shows up in the MANSOQ product UI as Symptoms.
Source:
- Model:
care/emr/models/condition.py - Resource spec:
care/emr/resources/condition/spec.py - Value set:
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 MANSOQ 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 |
|---|---|---|
clinical_status |
CharField(100), nullable |
Clinical state. Spec binds to ClinicalStatusChoices; optional on write. |
verification_status |
CharField(100), nullable |
Level of certainty. Spec binds to VerificationStatusChoices; required on write (ConditionSpec/ConditionUpdateSpec). |
category |
CharField(100), nullable |
Classification. Spec binds to CategoryChoices; required on create (ConditionSpec). |
severity |
CharField(100), nullable |
Subjective severity. Spec binds to SeverityChoices; optional on write. |
Coded concepts
| Field | Type | Notes |
|---|---|---|
code |
JSONField (default=dict, not null/blank) |
The condition itself. On write, a single Coding bound to the MANSOQ_CODITION_CODE_VALUESET value set (SNOMED CT clinical findings). On read, serialized as a plain Coding. |
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 |
|---|---|---|
onset |
JSONField (default=dict) |
Choice-of-type onset, shaped by ConditionOnSetSpec. Defaults to {}. |
abatement |
JSONField (default=dict) |
When the condition resolved or went into remission. Shaped by ConditionAbatementSpec. Defaults to {}. |
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 |
None |
Coerced to timezone-aware (make_aware) if naive. Cannot be in the future — a value > mansoq_now() is rejected. |
onset_age |
int | None |
None |
Age at onset. |
onset_string |
str | None |
None |
Free-text onset description. |
note |
str | None |
None |
Note about the onset. |
ConditionAbatementSpec (abatement shape)
| Field | Type | Default | Notes |
|---|---|---|---|
abatement_datetime |
datetime | None |
None |
Unlike onset_datetime, no future-date check. |
abatement_age |
int | None |
None |
Age at abatement. |
abatement_string |
str | None |
None |
Free-text abatement description. |
note |
str | None |
None |
Note about the abatement. |
Coding shape
code is a single Coding, not a CodeableConcept. extra="forbid".
| Field | Type | Notes |
|---|---|---|
system |
str | None |
Code system URI (e.g. http://snomed.info/sct). |
version |
str | None |
Code system version. |
code |
str |
Required. The code value. |
display |
str | None |
Human-readable label. |
code value-set binding
On write, code is typed ValueSetBoundCoding[MANSOQ_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
- Reference: Patient
- Reference: Encounter
- Reference: Base model
- Source: condition.py · spec.py · valueset.py
Source evidence
References are pinned to the private MANSOQ source repository. Repository access is required to open these links.