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

References/Schedule & Availability

From OHC Network Wiki
Revision as of 09:49, 6 July 2026 by Admin (talk | contribs) (Automated edit (via update-page on MediaWiki MCP Server))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
referenceschedulingCARE 3.0+

A schedule is a repeatable block of time during which a resource — a practitioner, location, or healthcare service — can be booked. You attach availabilities to it (these decide how the block splits into slots) and exceptions that block out specific dates.

Source:

The Django model (care/emr/models/scheduling/schedule.py) is storage only, down to the opaque Availability.availability JSONField. The contract lives in the Pydantic resource specs (care/emr/resources/scheduling/...): the enums, the real shape of that JSON field, the validation rules, and the separate read/write schemas. Everything below ## Models describes the specs.

Models

Model Purpose
SchedulableResource A bookable resource (a user, location, or healthcare service) within a facility
Schedule A named, date-bounded block of availability attached to a resource
Availability Slot configuration for a schedule (slot size, tokens, weekly recurrence)
AvailabilityException A date/time range that blocks a resource (leave, holidays)

All four extend EMRBaseModel, which supplies external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.

Schedule fields

Field Type Req. Notes
resource FK → SchedulableResource (CASCADE) server
name CharField(255) yes
valid_from DateTimeField yes
valid_to DateTimeField yes
revisit_allowed_days IntegerField no
is_public BooleanField yes

Both charge links are set only through the set_charge_item_definition action (see Resource specs) — never the create/update body.

Field Type Notes
charge_item_definition FK → ChargeItemDefinition (PROTECT) Nullable; charge applied to bookings against this schedule. related_name="schedule_charge_item_definition"
revisit_charge_item_definition FK → ChargeItemDefinition (PROTECT) Nullable; charge applied when a booking falls within revisit_allowed_days. related_name="schedule_revisit_charge_item_definition"

Enums

SchedulableResourceTypeOptions

Discriminates the kind of entity a schedule or exception attaches to. On write, send it as resource_type; the matching resource_id is that entity's external_id.

Value Resolves to
practitioner A [[References/User & Skills
location A FacilityLocation in the facility (serialized via FacilityLocationListSpec)
healthcare_service A [[References/Healthcare Service

The SchedulableResource.resource_type model column defaults to the string "practitioner".

SlotTypeOptions

The type of an individual Availability block (Availability.slot_type on the model, slot_type in the spec).

Value Meaning
open Open block; slot_size_in_minutes / tokens_per_slot are cleared to null on save
appointment Time-precise appointment block; requires both slot_size_in_minutes and tokens_per_slot
closed Closed block; slot_size_in_minutes / tokens_per_slot are cleared to null on save

SchedulableResource

The bookable entity within a facility, and the join point that booking hangs off. Exactly one of user, location, or healthcare_service points at the underlying resource; resource_type says which. You never create one directly: Schedule and AvailabilityException writes call get_or_create_resource(resource_type, resource_id, facility) server-side, which checks the target belongs to the facility first.

facility            → FK Facility (CASCADE)
resource_type       → CharField(255), default "practitioner"
user                → FK User (CASCADE, nullable)
location            → FK FacilityLocation (CASCADE, nullable)
healthcare_service  → FK HealthcareService (CASCADE, nullable)

Three UniqueConstraints keep one SchedulableResource per facility, resource_type, and target:

Constraint Fields
unique_facility_resource_user facility, resource_type, user
unique_facility_resource_location facility, resource_type, location
unique_facility_resource_healthcare_service facility, resource_type, healthcare_service

On read, serialize_resource(obj) (resource/spec.py) dispatches on resource_type to UserSpec / HealthcareServiceReadSpec / FacilityLocationListSpec.

Availability

Decides how a schedule's time divides into bookable slots. One schedule can carry several availabilities — say, a morning block and an afternoon block.

Field Type Req. Spec detail
schedule FK Schedule (CASCADE) server Excluded from the availability spec; set from the URL / parent schedule
name CharField(255) yes Block name
slot_type CharField yes One of [[#slottypeoptions
slot_size_in_minutes IntegerField (nullable) conditional None, ge=1. Required when slot_type == appointment; forced to null otherwise
tokens_per_slot IntegerField (nullable) conditional None, ge=1. Capacity per slot. Required when slot_type == appointment; forced to null otherwise
create_tokens BooleanField no Default False. When True, each booking gets a token (queue/token workflows)
reason TextField (nullable) no Spec default ""
availability JSONField, default dict yes Despite the dict default, the spec models it as a list[AvailabilityDateTimeSpec] (weekly recurrence). See below

availability JSON shape — AvailabilityDateTimeSpec

Each entry is one weekly recurrence window. The field stores a list of these objects, not a single dict.

AvailabilityDateTimeSpec {
  day_of_week: int   # 0–6, validated le=6 (Monday=0 … Sunday=6, ISO-style)
  start_time: time   # HH:MM:SS
  end_time:   time   # HH:MM:SS
}

The list is validated by AvailabilityForScheduleSpec.validate_availability + validate_for_slot_type:

  • Every entry needs start_time < end_time (strict).
  • No two entries on the same day_of_week may overlap (has_overlapping_availability: ranges overlap when a.start ≤ b.end and b.start ≤ a.end). On create, overlap is checked across all availabilities of the same schedule.
  • For slot_type == appointment: slot_size_in_minutes and tokens_per_slot are mandatory, each window's duration must be an exact multiple of slot_size_in_minutes, and the resulting slot count must not exceed settings.MAX_SLOTS_PER_AVAILABILITY (default 30).

AvailabilityException

Blocks a resource for a date/time range regardless of its schedules — leave, holidays, one-off closures.

Field Type Req. Spec detail
resource FK SchedulableResource (CASCADE) server Resolved from resource_type + resource_id (get-or-create); excluded from the spec body
name CharField(255) yes Exception name
reason TextField (nullable) no None
valid_from DateField yes date. Must be ≥ today and ≤ valid_to
valid_to DateField yes date. Must be ≥ today
start_time TimeField yes time
end_time TimeField yes time

Resource specs (API schema)

Every spec extends EMRResource ([https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/base.py base.py]), which provides serialize() (DB → pydantic), de_serialize() (pydantic → DB), and the perform_extra_serialization / perform_extra_deserialization hooks. __exclude__ lists fields skipped during (de)serialization.

Spec Role Exposes / behaviour
ScheduleBaseSpec shared id, is_public. __exclude__ = ["resource", "facility"]
ScheduleCreateSpec write · create facility, name, valid_from, valid_to, availabilities: list[AvailabilityForScheduleSpec], resource_type, resource_id, is_public. Validates dates ≥ now, valid_from ≤ valid_to, and cross-availability non-overlap. perform_extra_deserialization stashes facility, _resource_id, _resource_type, and availabilities on the instance for the viewset
ScheduleUpdateSpec write · update name, valid_from, valid_to, is_public. perform_extra_deserialization blocks narrowing validity that would drop allocated slots — it compares Sum(TokenSlot.allocated) across the old vs new range and raises if they differ
ScheduleReadSpec read · list + detail All of the above plus availabilities (re-serialized from Availability rows), resource_type, charge_item_definition / revisit_charge_item_definition (full ChargeItemDefinitionReadSpec or null), revisit_allowed_days, created_by, updated_by
AvailabilityBaseSpec shared id. __exclude__ = ["schedule"]
AvailabilityForScheduleSpec write/read (nested in schedule) name, slot_type, slot_size_in_minutes, tokens_per_slot, create_tokens, reason, availability: list[AvailabilityDateTimeSpec]. Carries the availability + slot-type validators
AvailabilityCreateSpec write · create (standalone) Adds schedule: UUID4; on create, re-checks overlap against all existing availabilities of that schedule
AvailabilityDateTimeSpec nested day_of_week (le=6), start_time, end_time — the shape of the availability JSON field
AvailabilityExceptionBaseSpec shared id, reason, valid_from, valid_to, start_time, end_time. __exclude__ = ["resource", "facility"]
AvailabilityExceptionWriteSpec write · create/upsert Adds facility, resource_type, resource_id. Validates dates ≥ today and valid_from ≤ valid_to; stashes _resource_type/_resource_id
AvailabilityExceptionReadSpec read · list + detail Base fields; perform_extra_serialization maps idexternal_id
ChargeItemDefinitionSetSpec write (action body) None, re_visit_allowed_days: int, re_visit_charge_item_definition: str | None — body for POST .../set_charge_item_definition (charge fields resolved by slug within the facility)

Server-maintained behaviour (viewset hooks)

  • Resource resolution. On schedule or exception create, get_or_create_resource(resource_type, resource_id, facility) checks the target is in the facility (a practitioner must be a FacilityOrganizationUser; a location or healthcare service must belong to the facility), then gets-or-creates the SchedulableResource. facility is injected from the URL via clean_create_data.
  • Nested availability create. ScheduleViewSet.perform_create (atomic) sets resource, saves the schedule, then de-serializes and saves each availability linked to it.
  • Charge items. revisit_allowed_days, charge_item_definition, and revisit_charge_item_definition move only through the set_charge_item_definition detail action, never the create/update body.
  • Locking + slot guards. Update and destroy take a Lock("booking:resource:<id>"). Deleting a schedule or availability is rejected when future allocated TokenSlots exist; otherwise availabilities and slots are soft-deleted (deleted=True).
  • Exception slot clearing. Creating an AvailabilityException soft-deletes overlapping TokenSlots, but rejects the request if any are already allocated ("There are bookings during this exception").
  • List requires resource. The Schedule and AvailabilityException list endpoints require resource_type and resource_id query params and scope results to that SchedulableResource.

Integration notes

  • Slots are computed from Availability at read time, not stored as rows on the schedule.
  • When reading a SchedulableResource, dispatch on resource_type to pick the populated relation (user, location, or healthcare_service) — only one is set, and you can't assume which.
  • A ChargeItemDefinition referenced by a schedule can't be deleted while the schedule exists (PROTECT).

Pages that link here: