Exam Seating: Cross-Branch Row Distribution¶
Status: shipped (kwiloai_webapp#1438).
When an exam room seats students from more than one branch (e.g. CEC and Mechanical sharing a hall), the seating plan alternates branches across each row so no two same-branch students sit next to each other. Same-branch students sitting front-to-back (same column) is fine. This replaces the exam admin's manual seating-chart work and removes the anti-cheating risk of an unmixed row.
Why this exists¶
- Who asked — Kantharaju (co-owner), in the ERP planning thread listing pending asks for exam operations. Picked as one of the first two ERP gaps to build (alongside payroll).
- User pain — exam admins currently hand-build seating charts every exam cycle to keep same-branch students apart.
erp/exam_ops.ExamSeatAllocationtoday does flat capacity-fill seating with no branch awareness, so admins can't trust it and override manually every session. - Cost of not doing it — recurring manual ops burden each cycle, and a seating mistake (same-branch students adjacent) is a malpractice/academic-integrity incident, not just an inconvenience.
- Validated or guess — validated. Concrete ops ask with real college process detail (branch pairing per hall), not an internal engineering guess.
- Counterfactual — pull it a week after shipping and exam admins go straight back to manual seat charts, and can name the exact exam cycle where it broke.
How it works¶
Mental model¶
Room roster (mixed branches)
│
▼
group students by branch, sort each group by roll no
│
▼
for each row: alternate branch A, branch B, branch A, ... across the row's seats
│
▼
seat_number assigned row-major (row 1 left-to-right, then row 2, ...)
Rule: same-branch students must not be row-neighbors (left/right adjacent). Same-branch students directly in front of/behind each other (same column, different row) is acceptable — this is the simplest case of the algorithm since no diagonal/zig-zag shift between rows is required.
Two-branch rooms (the common case: one room, two branches paired) reduce to bench-pairing — alternate the two branches straight across each row. Rooms with 3+ branches use the same row-alternation rule with a round-robin cursor across branch groups so no adjacent pair repeats.
Non-goals (v1)¶
- Invigilator ratio allocation (1 per 40 students, 2 per 60+) — separate ERP gap, now covered by exam invigilator allocation.
- Automatic room/hall booking — this only assigns seats within rooms already chosen for the exam.
- Print layout / seating-chart PDF design — output is data (seat assignments); print formatting is a later concern.
Rejected alternatives¶
- Full zig-zag with diagonal row shift (no same-branch neighbor in any direction, including column) — rejected because the confirmed rule only bans row-adjacency; column repeats are allowed, so the simpler row-alternation is sufficient and avoids the extra bookkeeping a diagonal shift needs.
- General N-branch graph coloring / backtracking solver — rejected for v1; round-robin row alternation handles the realistic case (rooms shared by 2, occasionally 3, branches) without the complexity of a constraint solver. Revisit only if uneven branch-group sizes in practice produce bad allocations.
Edge cases¶
- Odd number of branch groups or uneven group sizes: round-robin cursor continues from wherever it left off in the previous row rather than resetting, so no branch runs out early and leaves a same-branch pair at a row's end.
- Single-branch exam: no mixing needed, falls back to today's flat capacity-fill.
- Roster size exceeds total room capacity across selected rooms: reuse today's
InsufficientCapacityErrorfromexam_ops.service.
Where it lives (planned)¶
apps/backend/src/erp/exam_ops/models.py:ExamRoomneeds a rows × seats-per-row layout (currently flatcapacity);ExamSeatAllocationneeds arow_numberalongsideseat_number.apps/backend/src/erp/exam_ops/service.py: new branch-aware allocation function alongside the existing seat-allocation logic (_ROOM_ENTITY, capacity-fill loop).apps/backend/src/erp/students/models.py:StudentProfileneeds a branch/program reference the allocator can group by (confirm exact field during implementation).