Encounter
An Encounter records a single interaction between a patient and a facility — a visit, admission, or consultation — and maps to the FHIR Encounter resource. You create one when a patient presents, drive it through its status lifecycle, and read it back to render the clinical timeline.
The Django model is only the storage layer: several fields are opaque JSONFields whose real shape lives in the Pydantic resource specs, so the model alone won't tell you what a write must look like. The spec tables below carry that detail.
Source:
- Model:
care/emr/models/encounter.py - Specs:
care/emr/resources/encounter/spec.py·constants.py·valueset.py·enum_display_names.py
Models
| Model | Purpose |
|---|---|
Encounter | A single interaction between a patient and a facility (visit, admission, consultation) |
EncounterOrganization | Links an Encounter to a FacilityOrganization for access control |
Both extend EMRBaseModel, the shared Care EMR base that provides external_id, audit fields, and soft-delete semantics.
Encounter fields
Classification & status
| Field | Type | Notes |
|---|---|---|
status | CharField(100) | Nullable in DB; required in API. One of StatusChoices (see below) |
status_history | JSONField | Default {}. Server-maintained. Shape: { "history": [{ "status": str, "moved_at": str (ISO datetime) }] } |
encounter_class | CharField(100) | Nullable in DB; required in API. One of ClassChoices (see below) |
encounter_class_history | JSONField | Default {}. Server-maintained. Shape: { "history": [{ "status": str, "moved_at": str (ISO datetime) }] } |
priority | CharField(100) | Nullable in DB; required in API. One of EncounterPriorityChoices (see below) |
external_identifier | CharField(100) | Nullable. Identifier from an external/source system |
Subject & facility
| Field | Type | Notes |
|---|---|---|
patient | FK → Patient | CASCADE. The patient this encounter is for. Set on create only |
facility | FK → Facility | PROTECT. The facility where the encounter takes place. Set on create only |
appointment | FK → TokenBooking | SET_NULL, nullable. Originating scheduling booking, if any |
current_location | FK → FacilityLocation | SET_NULL, nullable. Cached current location for easier querying |
Timing & disposition
| Field | Type | Notes |
|---|---|---|
period | JSONField | Default {}. PeriodSpec { start: datetime (tz-aware, optional), end: datetime (tz-aware, optional) }. If both set, start ≤ end |
hospitalization | JSONField | Default {}. HospitalizationSpec (see shape below). Nullable |
discharge_summary_advice | TextField | Nullable. Free-text discharge advice. Explicitly cleared to None on update when omitted |
Care team
| Field | Type | Notes |
|---|---|---|
care_team | JSONField | Default {}. Stored as a list of { "user_id": int, "role": Coding } entries. Not writable on create/update (excluded); managed via the dedicated care-team write spec |
care_team_users | ArrayField[int] | Platform-maintained denormalized cache of user IDs derived from care_team via sync_care_team_users_cache() on every save |
Organization cache, tags & extensions
| Field | Type | Notes |
|---|---|---|
facility_organization_cache | ArrayField[int] | Platform-maintained cache of facility-organization IDs (incl. parent chains). Rebuilt by sync_organization_cache() |
tags | ArrayField[int] | Tag IDs applied to the encounter. Serialized via SingleFacilityTagManager().render_tags() |
extensions | JSONField | Default {}. Open extension bag for deployment-specific metadata. Validated by ExtensionValidator against ExtensionResource.encounter |
Nested JSON shapes
PeriodSpec (period)
Defined in resources/base.py.
| Field | Type | Notes |
|---|---|---|
start | datetime | None | Must be timezone-aware if provided |
end | datetime | None | Must be timezone-aware if provided |
validate_period rejects naive datetimes, and when both start and end are set it requires start not to exceed end.
HospitalizationSpec (hospitalization)
Defined in spec.py. All fields optional/nullable.
| Field | Type | Values |
|---|---|---|
re_admission | bool | None | — |
admit_source | AdmitSourcesChoices | None | see Admit source values |
discharge_disposition | DischargeDispositionChoices | None | see Discharge disposition values |
diet_preference | DietPreferenceChoices | None | see Diet preference values |
Enum values
Status values (StatusChoices)
| Value | |
|---|---|
planned | |
in_progress | |
on_hold | |
discharged | |
completed | terminal (in COMPLETED_CHOICES) |
cancelled | terminal (in COMPLETED_CHOICES) |
discontinued | terminal (in COMPLETED_CHOICES) |
entered_in_error | terminal (in COMPLETED_CHOICES and ERROR_CHOICES) |
unknown |
COMPLETED_CHOICES = [completed, cancelled, entered_in_error, discontinued]; ERROR_CHOICES = [entered_in_error].
Class values (ClassChoices)
| Value | Meaning |
|---|---|
imp | inpatient |
amb | ambulatory |
obsenc | observation |
emer | emergency |
vr | virtual |
hh | home health |
Priority values (EncounterPriorityChoices)
| Value |
|---|
ASAP |
callback_results |
callback_for_scheduling |
elective |
emergency |
preop |
as_needed |
routine |
rush_reporting |
stat |
timing_critical |
use_as_directed |
urgent |
Admit source values (AdmitSourcesChoices)
| Value | Display (get_admit_source_display) |
|---|---|
hosp_trans | Transferred from other hospital |
emd | From accident/emergency department |
outp | From outpatient department |
born | Born in hospital |
gp | General Practitioner referral |
mp | Medical Practitioner/physician referral |
nursing | From nursing home |
psych | From psychiatric hospital |
rehab | From rehabilitation facility |
other | Other |
Discharge disposition values (DischargeDispositionChoices)
| Value | Display (get_discharge_disposition_display) |
|---|---|
home | Home |
alt_home | Alternate Home |
other_hcf | Other Health Care Facility |
hosp | Hospital |
long | Long-term Care Facility |
aadvice | Against Medical Advice |
exp | Expired |
psy | Psychiatric Hospital |
rehab | Rehabilitation Facility |
snf | Skilled Nursing Facility |
oth | Other |
Diet preference values (DietPreferenceChoices)
| Value |
|---|
vegetarian |
dairy_free |
nut_free |
gluten_free |
vegan |
halal |
kosher |
none |
Resource specs (API schema)
All specs build on EMRResource, which provides serialize / de_serialize plus the perform_extra_serialization / perform_extra_deserialization hooks. EncounterSpecBase sets __exclude__ = [patient, organizations, facility, appointment, current_location, care_team]; those fields move through hooks or dedicated endpoints rather than direct assignment, which is why the create and read schemas diverge from the raw model.
| Spec | Role | Notes |
|---|---|---|
EncounterSpecBase | shared | Fields: id, status, encounter_class, period (PeriodSpec, default {}), hospitalization (HospitalizationSpec | None, default {}), priority, external_identifier, discharge_summary_advice. __model__ = Encounter |
EncounterCreateSpec | write · create | Extends base + ExtensionValidator. Adds patient: UUID4, facility: UUID4, organizations: list[UUID4] = [], appointment: UUID4 | None. Resolves FKs by external_id; validates appointment belongs to the patient and facility |
EncounterUpdateSpec | write · update | Extends base + ExtensionValidator. Does not accept patient/facility/organizations/appointment — only mutable fields. Appends to status/class history on change |
EncounterListSpec | read · list | Adds serialized patient (PatientListSpec), facility (FacilityBareMinimumSpec), status_history, encounter_class_history, created_date, modified_date, tags, current_location (FacilityLocationMinimalListSpec | None), care_team ([{ member, role }]) |
EncounterRetrieveSpec | read · detail | Extends list + EncounterPermissionsMixin. Adds appointment (TokenBookingReadSpec), created_by/updated_by (UserSpec), organizations (FacilityOrganizationReadSpec[]), location_history (FacilityLocationEncounter[], newest first), extensions; patient via PatientRetrieveSpec |
HospitalizationSpec | nested | Plain BaseModel; see shape above |
EncounterCareTeamMemberSpec | nested (write) | { user_id: UUID4, role: Coding }, role bound to the system-practitioner-role-code value set |
EncounterCareTeamMemberWriteSpec | write · care team | { members: list[EncounterCareTeamMemberSpec] }. Dedicated payload for replacing the care team |
Server-maintained behaviour
- Status history: on create,
status_historystarts as{ "history": [{ status, moved_at }] }carrying the initial status; on update, a new entry is appended only whenstatuschanges.encounter_class_history/encounter_classfollow the same rule. - FK resolution (create):
patient,facility, andappointmentresolve fromexternal_id(404 if not found). The appointment must belong to the same patient and to a slot whose resource sits in the same facility. - Organizations (create):
organizations, deduplicated to a list of UUIDs, is stashed onobj._organizations, and the resultingEncounterOrganizationrows drivefacility_organization_cache. - Discharge advice (update): omitting
discharge_summary_adviceon update clears it toNonerather than leaving the prior value. - Care team:
rolebinds to thePRACTITIONER_ROLE_VALUESET(slugsystem-practitioner-role-code), a SNOMED CT value set of descendants of223366009(healthcare professional) and224930009(healthcare-related occupation).
Related models
EncounterOrganization
Associates an encounter with a facility-scoped organization to drive organization-based access control on encounters.
encounter → FK Encounter (CASCADE)
organization → FK FacilityOrganization (CASCADE)
Saving an EncounterOrganization calls encounter.sync_organization_cache(), which rebuilds the encounter's facility_organization_cache.
Methods & save behaviour
sync_care_team_users_cache()
When care_team is a list, fills care_team_users with int(entry["user_id"]) for each entry, falling back to -1 when a user_id is missing. The flat integer array lets queries skip JSON traversal.
sync_organization_cache()
Recomputes facility_organization_cache as the union of:
- every linked
EncounterOrganizationorganization plus itsparent_cachechain, and - the facility's
default_internal_organization_id.
Persists the result with super().save(update_fields=["facility_organization_cache"]).
save() side effects
On every save:
sync_care_team_users_cache()runs, refreshingcare_team_users.- A record with no
pkis flagged as newlycreated. - Inside a single
transaction.atomic()block, the basesave()runs. For newly created encounters,evaluate_patient_facility_default_values(patient, facility)then generates facility-scoped patient identifier defaults. - After the transaction,
sync_organization_cache()runs, performing a second write (update_fields=["facility_organization_cache"]).
Expect two write passes per create or update through the ORM, plus identifier-default generation on creation.
API integration notes
- Reads use
EncounterListSpec/EncounterRetrieveSpec; writes useEncounterCreateSpec/EncounterUpdateSpec. status,encounter_class, andpriorityare required on write and validated against their enums, even though the DB columns are nullable.- Never set
status_history,encounter_class_history,care_team_users, orfacility_organization_cachefrom a client; the server appends history on create and update and derives the caches fromcare_teamandEncounterOrganization. - Create and update don't touch the care team. Replace it through the dedicated care-team endpoint with
EncounterCareTeamMemberWriteSpec, where each member'srolemust resolve within thesystem-practitioner-role-codevalue set. periodandhospitalizationcarry structured JSON matching the nested-spec shapes above;period.startandperiod.endmust be timezone-aware.current_locationis a cached convenience field. The authoritative record islocation_history, fromFacilityLocationEncounter.extensionsholds custom key-value data without a schema migration, validated byExtensionValidator.
Related
- Reference: Patient
- Reference: Service request
- Reference: Booking
- Reference: Location
- Reference: Organization
- Reference: Facility
- Reference: User
- Concept: Patient
- Source (model): encounter.py on GitHub
- Source (specs): resources/encounter on GitHub