Building a quarter
Generate, review, and publish a quarterly schedule.
The lifecycle
A quarter moves through these states:
- Empty — the quarter exists (Q2 2026, Q3 2026, …) but no assignments.
- Draft — you’ve run the generator at least once. The scheduler has filled slots according to eligibility, availability, and fairness. Nothing is visible to volunteers yet.
- Published — assignments are real, volunteers can see them on their
/mepages, and the first “here’s your whole quarter” email goes out. - Active — weekly reminder campaigns fire each Sunday evening for the following Sunday, reflecting any swaps / coverage that landed in the meantime.
Generating the draft
On /admin/quarter/<quarter>, click Generate Draft. You’ll see a few knobs:
- Seed — a number. Same seed + same inputs = same output, every time. Change it if you want to shake up who gets what without changing your rules.
- Frequency strictness —
hard/soft/off. Users can set their own “at most every N weeks” preference; this controls whether we enforce it (hard), penalize it in the score (soft), or ignore it (off). - Preserve locked — cells you’ve locked keep their assignments; unlocked cells get regenerated.
- Leave unsafe blank — if the scheduler can’t place someone without a hard conflict, leave the slot empty rather than force a pick.
The algorithm is a greedy heuristic (not an optimizer) — processes services chronologically, roles hardest-first within each service, picks the candidate with the best composite score. Full writeup in the plan doc.
The review grid
After generate, you’re in a 2D grid: rows are Sundays, columns are role slots. Each cell is a dropdown of eligible volunteers. Cells are colored by concern:
- Green — fresh pick, no warnings.
- Amber — soft warning (over their preferred frequency, same group already scheduled today, etc.).
- Red — hard conflict (blackout, ineligible). These shouldn’t happen if you trust the generator, but they can appear if you manually edit.
A warnings panel on the right aggregates everything that needs attention: unfilled slots, availability conflicts, users never scheduled this quarter, double-duty on the same Sunday.
Click a cell → see that volunteer’s stats for the quarter and their other assignments. Click a name → jump to their directory page.
Locking
Click the padlock on any cell to lock it. Locked cells are preserved by the next Generate Draft run (with Preserve Locked on). Use this when you’ve hand-picked someone specific for a Sunday (say, a new volunteer’s first time) and don’t want the scheduler to undo it.
Diff + Publish
Before publishing, the Diff view shows what’s changed since the last publish (useful if you publish, then swap flurries happen, and you want to re-publish).
Publish:
- Flips assignments from
drafttopublished. - Fires the quarterly-kickoff email (one per volunteer, listing every Sunday they’re on).
- Stamps a publish record into the audit log.
- Doesn’t schedule the weekly reminder campaigns — those happen one week at a time via the cron worker, reflecting whatever swaps/coverage have happened by then.
Re-publish
After publish, if you edit (to cover a gap, reassign, etc.), a Re-publish button appears. It re-emails only the affected volunteers — the ones whose schedule actually changed — not everyone.
Skipped Sundays
Mark a service as “skipped” (Christmas Day, Youth Sunday with no regular roles, etc.) and the scheduler won’t try to fill it.
Exports
- CSV — matches the legacy
qNNN-schedule.csvshape so you can diff against the old Python-CLI output. - PDF — print-friendly, one row per Sunday.
- ICS per role — e.g. the Sound team can subscribe to just the Sound slots.