Google Calendar

Introduction #

When a provider connects their Google account through native Google Calendar sync, Canvas imports the busy time from their selected Google calendars. Canvas stores that busy time in its own records rather than as appointments or calendar events, so a scheduling or booking plugin that works out availability only from Appointment and Event misses it.

The Google Calendar data models give plugins read-only access to that busy time and to the state of each provider’s connection:

  • GoogleCalendarBusyBlock is a period of busy time imported from Google for a provider.
  • GoogleCalendarConnection is a provider’s connection to their Google account.
  • GoogleCalendar is a calendar on that connection.

Busy blocks carry only the time a provider is busy. They don’t include the title or content of the Google event. These models have a dbid but no id.

Busy blocks #

A provider’s busy blocks are available through the google_calendar_busy_blocks attribute on a Staff object. When busy time is released, its busy block stays in the table with active set to False, so filter on active=True to get current busy time:

from canvas_sdk.v1.data.staff import Staff

provider = Staff.objects.get(id="4150cd20de8a470aa570a852859ac87e")
busy_blocks = provider.google_calendar_busy_blocks.filter(active=True)

A busy block records its start and its length in minutes, not an end time. A Google event that spans several days arrives as one busy block for each day.

Releasing a busy block also updates its modified time. A plugin that keeps its own copy of busy time can query for blocks modified since its last check and remove those where active is False. If released busy time returns to Google, Canvas creates a new busy block rather than reactivating the released one.

Find a provider’s busy time for a day #

This example lists a provider’s current busy intervals that start on a given day, ordered by start time:

import datetime

from canvas_sdk.v1.data.google_calendar import GoogleCalendarBusyBlock
from logger import log

day_start = datetime.datetime(2026, 10, 20, tzinfo=datetime.UTC)
day_end = day_start + datetime.timedelta(days=1)

busy_blocks = GoogleCalendarBusyBlock.objects.filter(
    provider__id="4150cd20de8a470aa570a852859ac87e",
    active=True,
    start_time__gte=day_start,
    start_time__lt=day_end,
).order_by("start_time")

for block in busy_blocks:
    block_end = block.start_time + datetime.timedelta(minutes=block.duration_minutes)
    log.info(f"Busy from {block.start_time} to {block_end} at {block.location.full_name}")

Connections and calendars #

A provider’s Google connection is available through the google_calendar_connection attribute on a Staff object. A provider who hasn’t connected a Google account has no connection, so filter on the staff field to get None back instead of an exception:

from canvas_sdk.v1.data.google_calendar import GoogleCalendarConnection
from canvas_sdk.v1.data.staff import Staff

provider = Staff.objects.get(id="4150cd20de8a470aa570a852859ac87e")
connection = GoogleCalendarConnection.objects.filter(staff=provider).first()

When needs_reconnect is True, the provider needs to reconnect their Google account in Canvas.

The calendars on a connection are available through its calendars attribute. Only selected calendars feed busy blocks. The calendar with is_app_created set to True is the one Canvas writes appointments to:

from canvas_sdk.v1.data.google_calendar import GoogleCalendarConnection

connection = GoogleCalendarConnection.objects.filter(
    staff__id="4150cd20de8a470aa570a852859ac87e"
).first()

if connection is not None:
    busy_time_calendars = connection.calendars.filter(selected=True)
    canvas_calendar = connection.calendars.filter(is_app_created=True).first()

Attributes #

GoogleCalendarBusyBlock #

Field NameTypeDescription
dbidIntegerDatabase identifier
createdDateTimeWhen the record was created.
modifiedDateTimeWhen the record was last modified.
providerStaffThe provider who is busy
locationPracticeLocationThe practice location the busy block applies to
start_timeDateTimeWhen the busy time starts
duration_minutesIntegerHow long the busy time lasts, in minutes
activeBooleanWhether the block is current busy time; False once the time is released

GoogleCalendarConnection #

Field NameTypeDescription
dbidIntegerDatabase identifier
createdDateTimeWhen the record was created.
modifiedDateTimeWhen the record was last modified.
staffStaffThe provider who owns the connection
google_account_emailStringThe email address of the connected Google account
needs_reconnectBooleanWhether the provider must reconnect their Google account
calendarsGoogleCalendar[]The Google calendars on this connection

GoogleCalendar #

Field NameTypeDescription
dbidIntegerDatabase identifier
createdDateTimeWhen the record was created.
modifiedDateTimeWhen the record was last modified.
connectionGoogleCalendarConnectionThe connection the calendar belongs to
summaryStringThe calendar’s name in Google
selectedBooleanWhether the calendar’s events feed busy blocks
is_app_createdBooleanWhether this is the calendar Canvas writes appointments to