AvailableTime and BlockedTimeSource: calendar_integration/models.py —
AvailableTime,BlockedTime,Calendar.manage_available_windows. Queryset:CalendarQuerySet.only_calendars_available_in_rangesin calendar_integration/querysets.py.
A calendar's "is this slot bookable?" answer is computed from three sources:
CalendarEvent rows that overlap the slot — already-booked time.BlockedTime rows — explicit unavailability (vacations, surgeries
from another system, paged emergencies).manage_available_windows=True,
AvailableTime rows — the only periods that count as open.For calendars with manage_available_windows=False, anything that isn't
an event or a BlockedTime is implicitly available (during reasonable
hours; the upstream provider is the source of truth).
AvailableTime — opening hoursPositive declarations of when a calendar is open. Only meaningful for
calendars with manage_available_windows=True.
Healthcare examples:
AvailableTime for every Tuesday
9 AM – 12 PM and Thursday 1 PM – 5 PM, recurring weekly.AvailableTime for weekdays 7 AM – 9 PM (the rad-tech
shift); nights and weekends are not bookable at all.AvailableTime
declaring 9 AM – 1 PM on BYDAY=SA.AvailableTime rows for the dates a locum is
on-site, no recurrence.AvailableTime is itself a recurring object (it inherits RecurringMixin
— see recurrence.md). Most opening-hours patterns are
expressed as a single recurring row with a weekly RecurrenceRule.
BlockedTime — explicit unavailabilityNegative declarations: even if the calendar would otherwise look open, this period is not bookable.
Healthcare examples:
BlockedTime so it counts as busy without polluting the local event
list (see external_id, bundle_calendar, and the sync flow in
calendar_integration/services/calendar_service.py).BlockedTime so it shows as busy without duplicating event data.BlockedTime is also recurring (e.g. "the standing OR cleaning slot
8:00–8:30 every weekday" can be one recurring BlockedTime).
only_calendars_available_in_rangesThe canonical "is this calendar free for these windows?" check. Given a
list of (start, end) ranges, it returns the calendars that have:
CalendarEvent (including expanded recurring
occurrences).BlockedTime (also recurrence-expanded).manage_available_windows=True calendars, some AvailableTime
fully covering the range.Group-level availability (calendar-groups.md) delegates to this method per range, so any improvement here flows into group bookability automatically.
A patient needs a 45-minute MRI on a Wednesday afternoon. The booking
flow asks: "Is MRI Suite A free for any 45-minute window between 1 PM
and 5 PM Wednesday?"
AvailableTime: weekdays 7 AM – 9 PM (managed window). ✔️BlockedTime: Tuesday-night maintenance, no Wednesday entry. ✔️CalendarEvents: existing MRI scans at 1 PM (60 min), 3 PM (30 min),
4 PM (45 min). ❌ for those windows.only_calendars_available_in_ranges walks the candidate windows
(2:00, 2:15, …, 4:15) and returns the suite as available for 2:00, 2:15,
3:30, and 3:45.
Use manage_available_windows=True |
Use manage_available_windows=False |
|---|---|
| Clinic provider with a publishable schedule. | Hospital staff with a normal Outlook calendar; we want "free if no event." |
| Resource calendars that are bookable only during specific hours (MRI suite, infusion bay). | Resource calendars used 24/7 (a portable monitor that's always available unless explicitly booked). |
| Virtual calendars for telehealth that should be bookable only during clinic hours. | Calendars synced from external systems that already publish their own busy/free. |
AvailableTime and BlockedTime both inherit RecurringMixin, so all of
the patterns in recurrence.md apply: recurring weekly
clinics, single-occurrence cancellations ("clinic is closed Dec 24
this year only"), and bulk modifications ("starting next quarter we
move the Thursday clinic to Friday") work the same way they do for
events.
*_with_bulk_modificationsWhen a recurring AvailableTime is split (e.g. "from April 1 onward,
Tuesday clinic shifts from 9–12 to 8–11"), the original row is truncated
with UNTIL=2026-03-31 and a continuation row is created. Helpers like
only_calendars_available_in_ranges_with_bulk_modifications traverse
both the original and any continuations so callers see the consolidated
schedule. See recurrence.md
for the full mechanism.