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.