Source: calendar_integration/models.py —
CalendarEvent,EventAttendance,ExternalAttendee,EventExternalAttendance,ResourceAllocation.
A CalendarEvent is a booking. It always lives on exactly one
Calendar (calendar_fk) and has a start_time / end_time plus an
IANA timezone. Around that core, it carries:
| Field | What it models |
|---|---|
attendees (via EventAttendance) |
Internal users participating, with RSVP status. |
external_attendees (via EventExternalAttendance) |
Non-user attendees identified by email + name. |
resources (via ResourceAllocation) |
Resource calendars allocated to the event. |
bundle_calendar, bundle_primary_event, is_bundle_primary |
Bundle membership (see calendar-bundles.md). |
calendar_group, group_selections |
Group booking metadata (see calendar-groups.md). |
recurrence_rule, recurrence_id, parent_recurring_object, is_recurring_exception |
Recurrence (see recurrence.md). |
bulk_modification_parent |
If the event is a continuation produced by a bulk modification split. |
external_id |
Stable id from the upstream provider for synced events. |
EventAttendance is for users with an account in the system. The
RSVP status (accepted, declined, pending) and downstream
notification logic use it. Example: an internal cardiologist invited to
a tumour-board video call.ExternalAttendee / EventExternalAttendance is for
non-account participants identified by email — typically the patient
on a clinic appointment or a referring physician copied on a consult.Both attend the same event; they're separate models because the system knows much more about internal users (permissions, notification preferences, calendar ownerships) than it does about external ones.
ResourceAllocation.ResourceAllocation ties an event to one or more resource calendars
(see calendars.md). The allocation has its own
RSVP status (think: "the OR-2 calendar provisionally accepts" pending
confirmation by the OR scheduler).
Examples:
OR-3
and C-arm fluoroscopy unit. If the C-arm is double-booked, the
scheduler can decline its allocation and reroute.Note on bookings via
CalendarGroup: when a slot of a group is filled with a resource calendar, the per-slot picks are stored inCalendarEventGroupSelectionrather thanResourceAllocation. The two models coexist:ResourceAllocationis the older "this event uses these resources" mechanism;CalendarEventGroupSelectionis the "which calendars satisfied each slot of the booking template" record.
A CalendarEvent knows whether it was created through a higher-level
booking primitive:
bundle_calendar is non-null when the event was created via a
BUNDLE calendar. is_bundle_primary=True marks the canonical event
(the one synced to the external provider); other child calendars get a
representation event or a BlockedTime. See
calendar-bundles.md.calendar_group is non-null when the event was booked via a
CalendarGroup. The companion CalendarEventGroupSelection rows
record which calendar from each slot's pool was picked. See
calendar-groups.md.Both fields are independent of recurrence — a recurring weekly tumour board can absolutely be a grouped event, with each occurrence inheriting the same selections.
CalendarService (in calendar_integration/services/calendar_service.py)
is the main entry point. It:
only_calendars_available_in_ranges.For grouped/bundled bookings, callers should use the higher-level
services (CalendarGroupService.create_grouped_event,
CalendarService.create_bundle_calendar + _create_bundle_event)
rather than create_event directly — those services handle picking the
primary calendar, propagating to children, and writing the per-slot or
per-bundle metadata in one transaction.
RSVPStatus (accepted, declined, pending) is shared between
EventAttendance, EventExternalAttendance, and ResourceAllocation.
A few practical patterns:
EventExternalAttendance —
"patient confirmed the visit" updates this row.EventAttendance, often synced from the provider's reply on
Google/Outlook.