In active developmentv0.0.1
Timetabling for schools and universities.
Calendry holds a term's timetable — rooms, cohorts, staff, courses, and your own daily block structure — and a separate solver service builds one for you to review, change, or throw away.
For the people who actually build the timetable: registrars, timetabling officers and department heads, plus the lecturers who have to live in the result.
A timetable you can hold, and one you can ask for
Two halves. This application stores and presents the schedule and everything it is made of; a separate solver service builds candidate placements from your rules and hands them back for a person to accept or reject.
One place for what a timetable is made of
Rooms, classes and cohorts, staff and students, courses, terms, and your own daily block structure. Your vocabulary stays yours: role names, session types and room equipment are things you define, not a fixed list you have to translate into.
Change a schedule without breaking it quietly
Move a session, swap two, or lock one so nothing may touch it. If a change clashes with something — a double-booked room, a class in two places at once — Calendry lets you make it and records the clash as state you can look up afterwards. It does not block you, and it does not forget.
Ask for a timetable, then judge it
Start a run and watch it work; cancel it if you change your mind. When it finishes you get a proposal shown against the schedule you already have, with what it would change and what it could not satisfy. Nothing is applied until you apply it.
Everyone sees their own slice
You compose the permission roles for your institution, so who can read the schedule, who can move a session and who can run the solver are separate questions. Lecturers can enter their own unavailability and preferred teaching days; staff review what they declare.
What works today
Not a demo reel. Everything below is implemented, in use against a real database, and covered by this repository's integration suite.
Working: Schedule view and editor
Week grid on a desktop, day agenda on a phone. Move, swap and lock, with clashes recorded rather than refused, and every edit appended to a log with an actor.
Working: Management screens for the core entities
Rooms, groups, people, courses, terms, the daily block grid and its breaks, plus a Ctrl+K palette that jumps to any of them.
Working: Academic calendar periods
Holidays, breaks and exam weeks, with a preview of exactly which weeks change classification before you save — because two dates do not obviously imply four exam weeks, and sometimes they do.
Working: One-off events alongside recurring courses
Create a session that belongs to no course — an open day, a guest lecture — with its own room, groups and people.
Working: Self-service availability
A lecturer declares when they cannot teach and which days they would rather have; staff approve or reject each declaration. Reaching only your own data is the point of that section.
Working: Solver integration, end to end
Start a run, keep the result even if the solver restarts, review the proposal against the current schedule, then apply or discard it. A run that succeeds with rules still broken is still offered — with the breaches listed — rather than thrown away.
Working: Shared rooms across a federation
Institutions in a consortium can share a lecture hall and see each other's occupancy of it, without seeing each other's schedules.
Working: Reproducible runs
The same inputs and the same seed produce a byte-identical timetable — as long as the run ended on its move budget rather than on a clock, which the run itself reports.
Working: Sign-in and per-institution permission roles
One account can act in several institutions and picks which at sign-in. Roles are composed from a fixed catalogue of permissions, per institution, in the UI.
Working: Multi-tenant data model and API
Institutions are isolated in the database itself, not only in application code. A query issued without institution context returns nothing rather than everything.
Not built yet, and honest about it
Calendry is being built in phases, and each of these is a phase rather than a promise with a date on it. Where a decision is still open, it says which one.
Not built yet: Import from CSV and Excel
For institutions arriving with years of spreadsheets. How the column mapping works — guided, or a fixed template — is not decided yet.
Not built yet: Export to iCal, Google Calendar and Outlook
Not started. Today a schedule lives in Calendry and is read there.
Not built yet: Notification delivery
Calendry already works out who an edit affects. Nothing sends anything yet, so the answer currently goes to a screen instead of an inbox.
Not built yet: Editing a one-off event after creating it
Its time can be moved, but its room, groups and people cannot be changed — correcting a mistake means deleting and recreating it.
Not built yet: A searchable person picker
The current control lists everyone in the institution. Fine for twenty people, wrong for two thousand — and the screen says so where it matters.
Not built yet: Several candidate schedules to choose between
Needs a design pass before it needs code: candidates that differ only trivially would be worse than one result. It may not be built at all.
Not built yet: A translated interface
Decided in principle and deliberately not started in pieces: a long half-translated interface was judged worse than an English one.
Decisions worth defending
Timetabling software fails in specific, recognisable ways. These are the choices made against them — each one is a rule the codebase is actually built on, not a slogan.
- Warn, never block
- A person editing a timetable usually knows something the rules do not. So an edit that breaks a hard rule is permitted, and the breach becomes queryable state attached to the schedule — findable next week, not a toast you dismissed.
- Nested groups really do clash
- Booking a programme blocks its cohorts, and booking a cohort blocks the programme. Clash detection walks the whole ancestor-and-descendant relationship through a maintained closure, rather than comparing the two names it was handed.
- Isolation in the database, not in the code
- Every institution-scoped table is guarded by row-level security, and the application connects as a role that owns nothing. The failure mode of a mistake is seeing no rows, which is loud, rather than seeing someone else's, which is not.
- Nothing about time is hardcoded
- Days per week, blocks per day, block length and the uneven breaks between blocks come from each institution's own grid. There is no assumed Monday-to-Friday and no fallback shape anywhere — an institution without a grid gets an empty state, not a guess.
- Every edit is an event
- Creates, moves, swaps, deletes and locks are appended to a log, on top of the generation they started from, with the person who did it. Reading that history back has no screen yet — the log is being kept properly before there is anything to show it in.
Tell us about your institution
Calendry is being built for real timetables, so the useful conversation is about yours: how many rooms and cohorts, what your week looks like, and what breaks today.
For the technically curious
27,000 sessions, placed in about 350 milliseconds.
That is a synthetic large-university instance. The development database's own term runs all the way to convergence in about four seconds. Both numbers came off a benchmark rather than a pitch deck, and both will move as the constraint set grows.
The application
Nuxt and TypeScript over PostgreSQL. Tenant isolation is enforced by row-level security policies; the schedule's history is an append-only event log applied on top of a versioned snapshot.
The solver
A separate stateless Rust service, reached over gRPC against a shared schema. Hybrid constructive placement followed by large-neighbourhood local search with simulated annealing, over fourteen constraint types. It holds no database: every run is a complete snapshot the application sends it.