Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Device

From OHC Network Wiki
Revision as of 04:14, 5 July 2026 by Admin (talk | contribs) (OHC identity seed)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
referencefacilityCARE 3.0+

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 Care EMR base with external_id, audit fields, and soft-delete semantics).

Device fields

Device data

Field Type Spec constraint Notes
identifier CharField(1024) str
status CharField(16) DeviceStatusChoices (required)
availability_status CharField(14) DeviceAvailabilityStatusChoices (required)
manufacturer CharField(1024) str
manufacture_date DateTimeField datetime
expiration_date DateTimeField datetime
lot_number CharField(1024) str
serial_number CharField(1024) str
registered_name CharField(1024) str (required)
user_friendly_name CharField(1024) str
model_number CharField(1024) str
part_number CharField(1024) str
contact JSONField (default {}) list[ContactPoint] (default [])
care_type CharField(1024) (default None) str
metadata JSONField (default {}) not exposed directly

Virtual / spec-only field

Field Spec type Notes
care_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

{{Field|model=Device|section=Relations|name=managing_organization|type=FK → FacilityOrganization|notes=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

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, care_metadata
DeviceCreateSpec write · create care_type (validated against DeviceTypeRegistry), care_metadata: dict (inherits base excludes)
DeviceUpdateSpec write · update care_metadata: dict — no care_type, which makes care_type immutable after create (inherits base excludes)
DeviceListSpec read · list id = external_id; care_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); care_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

  • status and availability_status must each match one of their enum values, or the request is rejected.
  • registered_name is required on every write.
  • care_type (create only) must be a registered device-type key or null. DeviceCreateSpec.validate_care_type calls DeviceTypeRegistry.get_care_device_class(value), which raises for unknown types.
  • care_type can't change on update — DeviceUpdateSpec doesn't expose it.
  • DeviceServiceHistory.serviced_on and note are required on write; updates are blocked once edit_history reaches 50 entries ("Cannot Edit instance anymore").
  • ContactPoint.system, value, and use are all required for each contact entry.

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 care_metadata for DeviceListSpec
retrieve retrieve(obj) Supplies care_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

  • facility comes from the URL (facility_external_id) on create, not from the body.
  • current_location, current_encounter, and managing_organization aren't writable through the device create/update body. Dedicated detail actions mutate them, and each one closes the open history row (stamps end) before opening a new one:
    • POST associate_encounter{ encounter: uuid | null }; closes the open DeviceEncounterHistory, sets current_encounter, and opens (and returns) a new history row. null detaches.
    • POST associate_location{ location: uuid | null }; same pattern against DeviceLocationHistory.
    • POST add_managing_organization{ managing_organization: uuid }.
    • POST remove_managing_organization — clears managing_organization.
  • When an encounter reaches a completed status, disassociate_device_from_encounter clears current_encounter on all attached devices and stamps end on their open DeviceEncounterHistory rows.
  • metadata (the model column) is plugin-owned; clients read deployment-specific data back through the care_metadata virtual field, not metadata.
  • facility_organization_cache is platform-maintained — don't set it directly. The list endpoint scopes results to devices whose cache overlaps the requesting user's facility-organization memberships, or, when ?location= is supplied, by location permission / parent_cache with ?include_children=.

Pages that link here: