← Knowledge library

Facility administration

Device

A Device tracks a physical or logical piece of equipment within a facility — from its lifecycle status to where it's placed and which encounter it's attached to. The Django model is only the storage layer; it types fields loosely — CharField…

3.1 · versioned documentation · 8 min read

A Device tracks a physical or logical piece of equipment within a facility — from its lifecycle status to where it's placed and which encounter it's attached to.

The Django model is only the storage layer; it types fields loosely — CharField for status enums, JSONField for contact/metadata. The contract lives in the Pydantic resource specs under care/emr/resources/device/, which constrain those CharFields to enums, structure the JSON fields, and define validation, read/write schemas, and server-maintained side effects.

Source:

Models

Model Purpose
Device A physical or logical device tracked within a facility
DeviceEncounterHistory Records each period a device was associated with an Encounter
DeviceLocationHistory Records each period a device was placed at a FacilityLocation
DeviceServiceHistory Records servicing/maintenance events for a device

All models extend EMRBaseModel (shared MANSOQ EMR base with external_id, audit fields, and soft-delete semantics).

Device fields

Device data

Field Type Spec constraint Notes
identifier CharField(1024) str | None Free-form device identifier. Filterable via ?identifier= (case-insensitive exact match)
status CharField(16) DeviceStatusChoices (required) Lifecycle status — see enum
availability_status CharField(14) DeviceAvailabilityStatusChoices (required) Physical availability — see enum
manufacturer CharField(1024) str | None Optional in the spec, though the model column itself is non-null
manufacture_date DateTimeField datetime | None Optional, tz-aware
expiration_date DateTimeField datetime | None Optional, tz-aware
lot_number CharField(1024) str | None Optional
serial_number CharField(1024) str | None Optional
registered_name CharField(1024) str (required) Formal/registered device name; required by every write spec. Searchable via ?search=
user_friendly_name CharField(1024) str | None Display name. Searchable via ?search=
model_number CharField(1024) str | None Optional
part_number CharField(1024) str | None Optional
contact JSONField (default {}) list[ContactPoint] (default []) A list of structured contact points, not a free-form dict. See ContactPoint shape
care_type CharField(1024) (default None) str | None (create only) Discriminator selecting a registered device-type plugin. Validated against DeviceTypeRegistry on create — must be a registered key (e.g. "camera") or null. Filterable via ?care_type=
metadata JSONField (default {}) not exposed directly Excluded from all specs. Written server-side by the care_type plugin's handle_create/handle_update. Read it back through the virtual mansoq_metadata field (below)

Virtual / spec-only field

Field Spec type Notes
mansoq_metadata dict (default {}) Not a DB column. On read it's populated by the care_type plugin — DeviceListSpec calls the plugin's list(obj), DeviceRetrieveSpec calls retrieve(obj). On write it carries plugin-specific data for handle_create/handle_update, which typically persist into the model's metadata column. Falls back to {} when there's no care_type or the plugin lookup fails

Relations

Field Type Notes
facility FK → Facility CASCADE; owning facility (required). Set server-side from the URL (facility_external_id), never from the request body
managing_organization FK → FacilityOrganization SET_NULL, nullable; org responsible for the device. Excluded from the create/update body — managed via dedicated endpoints (see API integration notes)
current_location FK → FacilityLocation SET_NULL, nullable; where the device is currently placed. Excluded from the create/update body — managed via associate_location
current_encounter FK → Encounter SET_NULL, nullable; encounter the device is currently attached to. Excluded from the create/update body — managed via associate_encounter

Access cache

Field Type Notes
facility_organization_cache ArrayField[int] Denormalized cache of facility-organization IDs, rebuilt on every save(). Don't set it directly. get_queryset reads it to scope which devices a user may see

Enum values

DeviceStatusChoices values

status field. Defined in spec.py.

Value Meaning
active Device is in service
inactive Device is not currently in service
entered_in_error Record created in error

DeviceAvailabilityStatusChoices values

availability_status field. Defined in spec.py.

Value Meaning
lost Device cannot be located
damaged Device is damaged
destroyed Device is destroyed
available Device is available for use

ContactPoint shape

contact is list[ContactPoint]. Each item (from care/emr/resources/common/contact_point.py):

ContactPoint {
  system: ContactPointSystemChoices   # required
  value:  str                         # required
  use:    ContactPointUseChoices      # required
}
system values use values
phone, fax, email, pager, url, sms, other home, work, temp, old, mobile

Related models

DeviceEncounterHistory

Audit trail of device-to-encounter associations. Attaching a device to an encounter creates a new row; detaching it stamps end. Read-only via the API (EMRModelReadOnlyViewSet), ordered by -end.

device    → FK Device (CASCADE)
encounter → FK Encounter (CASCADE)
start     → DateTimeField
end       → DateTimeField (nullable)

DeviceLocationHistory

Audit trail of device placements. Moving a device to a location creates a new row; leaving it stamps end. Read-only via the API, ordered by -end.

device   → FK Device (CASCADE)
location → FK FacilityLocation (CASCADE)
start    → DateTimeField
end      → DateTimeField (nullable)

DeviceServiceHistory

Servicing/maintenance log for a device. Writable via the API (create + update + list + retrieve).

device       → FK Device (PROTECT, required)
serviced_on  → DateTimeField (nullable, default None)
note         → TextField (default "")
edit_history → JSONField (default [])

device uses PROTECT, so a Device with service-history rows can't be hard-deleted while those rows exist. edit_history is an append-only audit list the server maintains on every update (see Methods & save behaviour).

Resource specs (API schema)

The viewset wires these specs as create = DeviceCreateSpec, update = DeviceUpdateSpec, list = DeviceListSpec, retrieve = DeviceRetrieveSpec. All extend EMRResource (serialize / de_serialize, with perform_extra_serialization / perform_extra_deserialization hooks).

Device specs

Spec class Role Adds / overrides Excludes
DeviceSpecBase shared base id, all device-data fields, status/availability_status enums, contact: list[ContactPoint], registered_name required facility, managing_organization, current_location, current_encounter, mansoq_metadata
DeviceCreateSpec write · create care_type (validated against DeviceTypeRegistry), mansoq_metadata: dict (inherits base excludes)
DeviceUpdateSpec write · update mansoq_metadata: dict — no care_type, which makes care_type immutable after create (inherits base excludes)
DeviceListSpec read · list id = external_id; mansoq_metadata from plugin list(obj) (inherits)
DeviceRetrieveSpec read · detail full nested objects: current_location (FacilityLocationListSpec), current_encounter (EncounterListSpec), managing_organization (FacilityOrganizationReadSpec), created_by/updated_by (audit users); mansoq_metadata from plugin retrieve(obj) (inherits)

Device history specs

Spec class Role Fields Excludes
DeviceLocationHistoryListSpec read · list id, location (nested FacilityLocationListSpec), created_by, start, end? device, location (raw FK)
DeviceEncounterHistoryListSpec read · list id, encounter (nested EncounterListSpec), created_by, start, end? device, encounter (raw FK)
DeviceServiceHistorySpecBase shared base id device, edit_history
DeviceServiceHistoryWriteSpec write serviced_on: datetime (required), note: str (required) (inherits)
DeviceServiceHistoryListSpec read · list adds created_date, modified_date; id = external_id (inherits)
DeviceServiceHistoryRetrieveSpec read · detail adds edit_history: list[dict] (each entry's updated_by hydrated to a UserSpec from cache), created_by, updated_by (inherits)

Validation rules

Methods & save behaviour

Device.save() — organization cache

Every save rebuilds facility_organization_cache:

  1. Look up the FacilityOrganization with org_type="root" for the device's facility; its ID seeds the cache.
  2. If managing_organization is set, add its id and its parent_cache (the full ancestor chain).
  3. Persist the resulting set as facility_organization_cache.

This lets the list endpoint filter by organization hierarchy without traversing joins. Treat the cache as platform-maintained; managing_organization and facility are the writable inputs that drive it.

care_type plugin hooks

When care_type is set, the matching DeviceTypeBase subclass from DeviceTypeRegistry runs inside the create/update/delete transaction:

Stage Hook Effect
create handle_create(request_data, obj) May mutate obj / obj.metadata and persist (e.g. the bundled camera plugin stores request_data["some_data"] into metadata)
update handle_update(request_data, obj) Same, on update
destroy handle_delete(obj) Validation/cleanup before the device is soft-deleted (deleted=True)
list list(obj) Supplies mansoq_metadata for DeviceListSpec
retrieve retrieve(obj) Supplies mansoq_metadata for DeviceRetrieveSpec

DeviceServiceHistory edit history

On perform_update, the viewset snapshots the current DB row and appends {serviced_on, note, updated_by} to edit_history before writing the new values — a server-maintained, append-only trail of previous states. Updates are refused once it holds 50 entries.

API integration notes

Related

Source evidence

References are pinned to the private MANSOQ source repository. Repository access is required to open these links.