References/Schedule & Availability: Difference between revisions
More languages
More actions
OHC identity seed |
Automated edit (via update-page on MediaWiki MCP Server) |
||
| Line 328: | Line 328: | ||
* When reading a <code>SchedulableResource</code>, dispatch on <code>resource_type</code> to pick the populated relation (<code>user</code>, <code>location</code>, or <code>healthcare_service</code>) — only one is set, and you can't assume which. |
* When reading a <code>SchedulableResource</code>, dispatch on <code>resource_type</code> to pick the populated relation (<code>user</code>, <code>location</code>, or <code>healthcare_service</code>) — only one is set, and you can't assume which. |
||
* A <code>ChargeItemDefinition</code> referenced by a schedule can't be deleted while the schedule exists (<code>PROTECT</code>). |
* A <code>ChargeItemDefinition</code> referenced by a schedule can't be deleted while the schedule exists (<code>PROTECT</code>). |
||
{{Navbox scheduling}} |
|||
{{Related}} |
{{Related}} |
||
Latest revision as of 09:49, 6 July 2026
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:
- Model: [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/scheduling/schedule.py
care/emr/models/scheduling/schedule.py] - Resource specs: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/scheduling/schedule/spec.py
schedule/spec.py], [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/scheduling/availability_exception/spec.pyavailability_exception/spec.py], [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/scheduling/resource/spec.pyresource/spec.py]
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 |
Billing links
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
|
Related models
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_weekmay overlap (has_overlapping_availability: ranges overlap whena.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_minutesandtokens_per_slotare mandatory, each window's duration must be an exact multiple ofslot_size_in_minutes, and the resulting slot count must not exceedsettings.MAX_SLOTS_PER_AVAILABILITY(default30).
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 id → external_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 aFacilityOrganizationUser; a location or healthcare service must belong to the facility), then gets-or-creates theSchedulableResource.facilityis injected from the URL viaclean_create_data. - Nested availability create.
ScheduleViewSet.perform_create(atomic) setsresource, saves the schedule, then de-serializes and saves eachavailabilitylinked to it. - Charge items.
revisit_allowed_days,charge_item_definition, andrevisit_charge_item_definitionmove only through theset_charge_item_definitiondetail 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 allocatedTokenSlots exist; otherwise availabilities and slots are soft-deleted (deleted=True). - Exception slot clearing. Creating an
AvailabilityExceptionsoft-deletes overlappingTokenSlots, but rejects the request if any are already allocated ("There are bookings during this exception"). - List requires resource. The
ScheduleandAvailabilityExceptionlist endpoints requireresource_typeandresource_idquery params and scope results to thatSchedulableResource.
Integration notes
- Slots are computed from
Availabilityat read time, not stored as rows on the schedule. - When reading a
SchedulableResource, dispatch onresource_typeto pick the populated relation (user,location, orhealthcare_service) — only one is set, and you can't assume which. - A
ChargeItemDefinitionreferenced by a schedule can't be deleted while the schedule exists (PROTECT).
Related
Pages that link here:
- Reference: References/User & Skills