Declaring calendar datasets

The protocol: start/end columns, titles, status tints, access levels, and the bundled calendars

Declaring calendar datasets

A calendar dataset is an ordinary @dataset with the calendar protocol attached. There are two ways to declare one; both live in an app's datasets.py, which SmallStack imports at startup.

In one declaration

from apps.calendar import calendar_dataset

@calendar_dataset(
    "ticket_due_dates",
    start="due_at",                 # a date or datetime column — required
    title="subject",                # chip label: attribute, dotted path or callable
    subtitle="queue.name",
    status="sla_status",            # → "success" | "warning" | "danger" | None
    access="authenticated",         # any signed-in user may see it
    label="Ticket due dates",
    description="Open tickets on the day they are due.",
    enable_api=True,
)
def ticket_due_dates(request):
    return Ticket.objects.filter(assignee=request.user, status="open")

Every keyword @dataset accepts passes straight through (label, description, columns, filterable, enable_api, enable_mcp, link …). The calendar-specific ones are:

Keyword Meaning
start Column holding the event's start. A DateField makes the event all-day; a DateTimeField gives it a time. Required.
end Column holding the end, for ranged events (windows, bookings). The chip spans every day between.
title What the chip says. An attribute name, a dotted path ("blog.name") or a callable taking the row. Defaults to str(row).
subtitle A second line in the day panel — the monitor, the queue, the cadence.
status Resolves to "success", "warning", "danger" or None; tints the chip's edge and shows a dot in the day panel.
access "staff" (default) or "authenticated".

start and end must be real columns of the dataset, because they drive the month-window query; the protocol is checked the first time the calendar is read and a bad column raises a clear error rather than an empty grid.

Attached to a dataset declared elsewhere

When the dataset already exists — one of the bundled smallstack_* datasets, or your own that you would rather not re-decorate — attach the protocol by key:

from apps.calendar import register_calendar

register_calendar(
    "smallstack_maintenance_windows",
    start="start", end="end", title="title", subtitle="monitor_key",
)

One spec per dataset key: a dataset has one time axis. To plot a second column of the same model (say, tickets by created date as well as by due date), declare a second dataset over the model and give that its own calendar.

Computed calendars — a provider

When the dates aren't stored anywhere — holidays worked out from rules like "the last Monday in May", the first and last day of a long contract, a rota that belongs on the night it starts — write the function that produces them:

from apps.calendar import calendar_provider
from apps.calendar.core import CalendarEvent

@calendar_provider("company_holidays", label="Company holidays", access="authenticated")
def company_holidays(lower, upper, request):
    for name, day in resolve_holidays(lower, upper):
        yield CalendarEvent.make("company_holidays", f"{name}:{day}", name, day)

The function receives the day window being shown and yields events built with CalendarEvent.make(calendar, id, title, start, end=None, ...). A date start makes an all-day event, a datetime a timed one. Keep id stable: it is what a subscribed calendar app uses to update an event rather than duplicate it. Optional keywords: subtitle, status, url, cancelled (shown struck through, still listed) and image (a small picture beside the row).

A provider calendar appears everywhere a column calendar does — the page, saved views, the events API, the .ics feed and the MCP tools — and has no dataset behind it.

Colours and weekends

Each calendar gets one of eight colours by position; pass color=3 to pin one. Saturdays and Sundays show by default; a site whose desk isn't staffed at the weekend sets SMALLSTACK_CALENDAR_WEEKENDS_DEFAULT = False and every calendar opens as a work week, with a note beside the Weekends toggle counting anything that falls on the hidden days.

Scoping per user

A calendar with access="authenticated" appears for every signed-in user, so its rows must be the user's own. The dataset function's request argument is the place:

def my_deadlines(request):
    return Deadline.objects.filter(owner=request.user)

The page, the events API, the .ics feed and the MCP tool all pass the calling user's request, so one filter covers every surface.

The bundled calendars

SmallStack attaches the protocol to eleven of its own datasets. All are staff-only and follow the bundled datasets' switches (SMALLSTACK_FRAMEWORK_DATASETS_ENABLED).

Calendar Dataset Plots
Maintenance windows smallstack_maintenance_windows each window across the days it spans
Daily uptime smallstack_heartbeat_daily one entry per monitor per day, tinted against the SLA
Upcoming scheduled jobs smallstack_scheduled_jobs each enabled schedule on its next fire, tinted by its last run
Scheduled job runs smallstack_scheduled_job_runs every firing on the day it was due
API token expiry smallstack_api_tokens amber inside two weeks, red once past
Backups smallstack_backups runs on the day taken, green/red by outcome
Approval deadlines smallstack_approval_requests requests on the day they expire
Blog posts smallstack_blog_posts posts on their publish date
Newsletter sends smallstack_blog_issues issues on the day their send began
Log capture windows smallstack_log_capture_windows each window from open to expiry
User sign-ups smallstack_users each account on the day it joined
Events smallstack_events hand-entered entries (open to every signed-in user)