Timetable planner

ANU publishes its class timetable weeks before enrolment opens, but the only place to try a timetable out is the enrolment system itself. There you can’t save a draft, and you can’t see a clash until you’ve already committed to a class. This app is that missing step for COMP courses in Semester 2, 2026. Start a draft, add courses, and choose a class for every lab, tutorial and workshop. Each option tells you before you pick it which of your other classes it would clash with, and in which teaching weeks. Every change is saved, so a draft is still there when you come back, and a draft open in a second tab catches up when the first tab changes it.

It models one slice of ANU timetabling end to end. The data is real class times from the public timetable, and the draft plan is kept in a database. It is not enrolment, and it can’t tell you whether a class still has places.

What good looks like here

The times have to be right, or nothing else matters. Every course, class, time, week range and room comes from ANU’s public timetable viewer, scraped by a committed script (scripts/scrape-timetable.ts) into data/timetable-2026-s2.json. None of it was typed in by hand. The copy covers all 48 COMP offerings for Semester 2 2026: 794 classes and 831 sessions. The home page says when the copy was taken and links the source, because a planner that quietly goes stale is worse than none. The 2027 viewer had no COMP classes when this was built, so 2026 S2 is the most recent full semester available.

A clash is a fact about weeks, not just hours. Two classes clash only if they fall on the same day, their times overlap, and they share at least one teaching week. Back-to-back classes don’t clash. A one-off week-41 workshop only collides with a weekly lab in week 41, and the app says “weeks 32–36, 39–44” rather than just “clash”. This rule lives in src/lib/timetable.ts and is tested on its own in spec/timetable.test.ts.

Planning means seeing a clash before you choose. Every option lists the classes it would collide with, given your other picks. That’s the question you actually have (“which tutorial fits?”), and answering it is what the enrolment system doesn’t do.

Some clashes can’t be fixed, and the app says so. Every lecture in S2 2026 runs at one time only, so two courses whose lectures overlap can’t both be attended as timetabled. When neither class has another time, the clash is listed as “can’t be avoided” rather than sent to a choice that doesn’t exist. The “Add a course” list marks courses that would bring one in, so you find out before adding them, not after.

It works without JavaScript. Every change is a plain form POST answered with a redirect, and the page is re-rendered from the database. Script adds two things: a mouse click on a class saves it immediately, and other open tabs reload when the draft changes. Keyboard users keep an explicit Save button, because arrowing through radio buttons shouldn’t submit at every step.

Enforced, and judgement calls

What pnpm check enforces (spec/):

Judgement calls, made while reading the scraped data:

What I chose not to build