Skip to main content
Version: 3.0

Condition

Technical reference for the Condition module in Care EMR. For the plain-language view, read the Condition concept.

Source:

Condition has two layers, and the split matters when you read the code:

  • The Django model is storage. Its coded and timing fields (code, body_site, onset, abatement) are opaque JSONFields. The model says nothing about their shape.
  • The Pydantic resource specs are the API. They define the enums, the structure inside each JSON field, the validation, the value-set binding for code, and the separate read and write schemas.

One model backs two API surfaces. SymptomViewSet serves symptom, and DiagnosisViewSet serves diagnosis. The category field separates them.

Models

ModelPurpose
ConditionA clinical problem, symptom, or diagnosis recorded for a patient

Condition extends EMRBaseModel, which provides external_id, created_date/modified_date, created_by/updated_by, soft delete through deleted, and the history/meta JSON fields.

Condition fields

Status and classification

FieldTypeNotes
clinical_statusCharField(100), nullableCourse of the condition. Bound to ClinicalStatusChoices by the specs; optional on write.
verification_statusCharField(100), nullableCertainty. Bound to VerificationStatusChoices; required on ConditionSpec and ConditionUpdateSpec.
categoryCharField(100), nullableBound to CategoryChoices; required on create. SymptomViewSet overwrites it with problem_list_item in perform_create.
severityCharField(100), nullableBound to SeverityChoices; optional on write.

Coded concepts

FieldTypeNotes
codeJSONField (default=dict, not null/blank)The condition itself. On write, a single Coding bound to the condition code value set. On read, a plain Coding.
body_siteJSONField (default=dict, not null/blank)Anatomical site. No current spec exposes it, so no client reads or writes it.

Timing

FieldTypeNotes
onsetJSONField (default=dict)Shaped by ConditionOnSetSpec.
abatementJSONField (default=dict)Shaped by ConditionAbatementSpec.
recorded_dateDateTimeField, nullableNot exposed by the current specs.

Context and notes

FieldTypeNotes
patientFK → Patient, on_delete=CASCADEDerived server-side from the encounter on create. Listed in __exclude__, so no client sets it.
encounterFK → Encounter, nullable, on_delete=CASCADESet server-side on create from the UUID in the write spec. Listed in __exclude__.
noteTextField, nullableFree-text note.

Enums

Every enum is a str, Enum in care/emr/resources/condition/spec.py. The stored and serialized value is the string in the table.

ClinicalStatusChoices values

Value
active
recurrence
relapse
inactive
remission
resolved
unknown

The frontend offers every value except unknown.

VerificationStatusChoices values

Value
unconfirmed
provisional
differential
confirmed
refuted
entered_in_error

CategoryChoices values

ValueUsed by
problem_list_itemSymptomViewSet, which forces this value on create and filters its queryset by it
encounter_diagnosisDiagnosisViewSet, the value the diagnosis form sends
chronic_conditionDiagnosisViewSet, for long-term diagnoses

SeverityChoices values

Value
mild
moderate
severe

Nested JSON shapes

These spec classes extend EMRResource and define the real structure behind the JSON fields.

ConditionOnSetSpec (onset shape)

FieldTypeDefaultNotes
onset_datetimedatetime | NoneNoneMade timezone-aware when naive. A value after care_now() is rejected.
onset_ageint | NoneNoneAge at onset.
onset_stringstr | NoneNoneFree-text onset.
notestr | NoneNoneNote about the onset.

ConditionAbatementSpec (abatement shape)

FieldTypeDefaultNotes
abatement_datetimedatetime | NoneNoneNo future-date check.
abatement_ageint | NoneNoneAge at abatement.
abatement_stringstr | NoneNoneFree-text abatement.
notestr | NoneNoneNote about the abatement.

Coding shape

code is a single Coding, not a CodeableConcept.

FieldTypeNotes
systemstr | NoneCode system URI, such as http://snomed.info/sct.
versionstr | NoneCode system version.
codestrRequired. The code value.
displaystr | NoneLabel for the code.

code value-set binding

On write, code is typed ValueSetBoundCoding[CARE_CODITION_CODE_VALUESET.slug]. The value set has the slug system-condition-code and includes the SNOMED CT concepts that are is-a 404684003 (Clinical finding). Care rejects codes outside the value set on ConditionSpec, ConditionUpdateSpec, and ChronicConditionUpdateSpec. The read spec uses a plain Coding and skips the check, which keeps reads cheap.

Resource specs (API schema)

Every spec extends BaseConditionSpecEMRResource. BaseConditionSpec sets __model__ = Condition, sets __exclude__ = ["patient", "encounter"], and exposes id: UUID4.

Spec classRoleExposes / behaviour
BaseConditionSpecshared baseid; excludes patient and encounter from direct mapping.
ConditionSpecwrite · createclinical_status?, verification_status (required), severity?, code (required, value-set bound), encounter (UUID4, required), onset, abatement, note?, category (required). Validates that the encounter exists; on create sets obj.encounter and obj.patient = encounter.patient.
ConditionUpdateSpecwrite · updateclinical_status?, verification_status (required), severity?, code (required, value-set bound), onset, abatement, note?. Accepts neither encounter nor category.
ChronicConditionUpdateSpecwrite · updateExtends ConditionUpdateSpec and adds encounter (UUID4). On deserialize, resolves the encounter with get_object_or_404 and assigns it.
ConditionReadSpecread · list/detailclinical_status, verification_status, category, severity (plain str), code (plain Coding), encounter (UUID4), onset, abatement, created_by?, updated_by?, note?, created_date, modified_date.

Validation and server-side behaviour

  • ConditionSpec.validate_encounter_exists rejects an unknown encounter UUID. perform_extra_deserialization runs on create only, loads the encounter, and derives patient from it.
  • verification_status is mandatory on both write specs. category is mandatory on create only.
  • code must belong to the bound value set on every write spec.
  • onset_datetime cannot be in the future, and Care makes it timezone-aware. abatement_datetime has no such rule.
  • ConditionReadSpec.perform_extra_serialization maps id to external_id, replaces encounter with its external_id, and expands created_by/updated_by.
  • body_site and recorded_date are storage only. No spec reads or writes them.

Viewsets

Both viewsets extend EMRModelViewSet, EncounterBasedAuthorizationBase, EMRQuestionnaireResponseMixin, and the local ValidateEncounterMixin. They are registered under the patient-nested router.

ViewsetRouteQuerysetNotes
SymptomViewSetpatient/<patient_id>/symptom/Conditions of the patient with category = problem_list_itemperform_create forces category to problem_list_item. Registered as the symptom system questionnaire.
DiagnosisViewSetpatient/<patient_id>/diagnosis/Every condition of the patientRegistered as the diagnosis system questionnaire. Overrides authorize_update for chronic conditions.

ValidateEncounterMixin.validate_data rejects the request when the encounter belongs to a different patient than the one in the URL.

Both viewsets expose the shared upsert action, which the frontend uses to send several conditions in one atomic request.

Filters

ConditionFilters applies to both viewsets: encounter, clinical_status, exclude_clinical_status, verification_status, exclude_verification_status, severity (case-insensitive exact), name (matches code__display), and category. The status and category filters accept several comma-separated values.

Authorization

ActionCheckPermission
List, retrieveauthorize_read_encounter in get_querysetcan_view_clinical_data on the patient, or can_read_encounter_clinical_data on the encounter in the encounter query parameter
Create, update, destroycan_update_encounter_clinical_datacan_write_encounter_clinical_data on the encounter
Update a chronic conditionDiagnosisViewSet.authorize_updatecan_view_clinical_data on the patient

can_update_encounter_clinical_data returns False when the encounter status is Completed, Cancelled, Entered in Error, or Discontinued. No user writes a condition to a closed encounter.

patient → FK Patient (CASCADE, derived from the encounter)
encounter → FK Encounter (CASCADE, nullable in the column, required on create)

Deletion of a Patient or an Encounter cascades to its Condition rows.

API integration notes

  • Send code as a Coding from the condition code value set, never as free text. Always send verification_status. Send category and encounter on create.
  • onset and abatement are structured objects, not arbitrary JSON.
  • Do not send patient, external_id, the audit fields, or deleted. The server owns them.
  • To remove a saved condition from the lists, set verification_status to entered_in_error. The frontend lists exclude that value.