sanyam.
students.xlsxteachers.xlsxrulesslots, gapsslot gridone slot, one datetimeconflict graphnode = courseedge = shared studentschedulergreedy, re-prioritisedvalidateslots, then datetimesHTTP 400which knobto turnseatingA, A*, AB, ABCDdutiestiers, capsreports19 kindsexams.thapar.edu · 157 tests · a failed run saves nothing
spreadsheetsstudents, teachersconflict graphshared studentsgreedy schedulerre-prioritised, validated0 clashesseating, duties, 19 reportsLive at exams.thapar.edu. 157 tests.
All projects

Exam Datesheet Generator

The scheduler Thapar's exams run on

Building an exam timetable by hand means weeks of juggling subjects, rooms, and invigilators, and one late change can break it. The generator takes the constraints and produces a clash-free datesheet, seating plan, and duty roster in one run.

Result
0 clashes, by construction
157 tests, deterministic reruns
live at exams.thapar.edu
What it does

The system Thapar Institute uses to run its exams, live at exams.thapar.edu: a graph-colouring scheduler that places every course in a clash-free slot, then seats every student, assigns every invigilator, and prints the reports. 17,000 lines of Flask over a 47-table MySQL schema, with 157 tests.

What I built
  • Scheduler on a networkx conflict graph: a course is a node, a shared student is an edge weighted by the gap that student needs. One global slot grid means a slot number is one datetime for every year, which closed the bug that gave students two exams at once.
  • Greedy placement with re-prioritisation after every move (multi-year courses first, then by enrolment), seeded so a rerun reproduces. Two validation passes, in slot space and in real datetimes.
  • Nothing partial ships: the scheduler returns a ScheduleResult with the unplaced courses and rejected pins, and a failed run is an HTTP 400 naming the constraint to relax, not a half datesheet in the database.
  • After the datesheet: four seating patterns (A, A*, AB, ABCD by parity class) with a per-slot reconciliation that fails loudly if a student has no seat, invigilator allocation by designation tier and duty cap, and 19 generated reports.
  • Per-day time-slot rules, teacher unavailability, manual swap and move checked against the same rules, and a shadow replay that pushes every historical job through a new scheduler before it is deployed.
How it works
  1. 01Students and teachers spreadsheets
  2. 02Subjects, groups, breaks, time slots, fixed slots
  3. 03Slot grid (one timeline for every year)
  4. 04networkx conflict graph
  5. 05Greedy scheduler with re-prioritisation
  6. 06Validation in slot space and in datetimes
  7. 07Swap and move through the conflict checker
  8. 08Seating allocation (A, A*, AB, ABCD)
  9. 09Invigilator duty allocation
  10. 1019 reports, MySQL
Features
  • Per-year start dates, daily time ranges, and per-day slot rules on one shared timeline
  • Fixed slots, subject groups, and minimum gaps between a student's consecutive exams
  • A failed run reports which knob to turn: add dates, add a time range, raise students per slot, loosen breaks
  • Four seating patterns with block priority, special requests, and validated emergency room moves
  • Invigilator allocation by designation priority, duty caps, and unavailability, with validated bulk swaps
  • Checkpoints at every stage, so a token can be reopened where it was left
  • Datesheet, seating plans, attendance sheets, and duty rosters as PDF, DOCX, and Excel