Adds routers/exam/batches.py (mounted alongside templates under /api/exam):
- POST/GET /batches — batch creation seeds the cohort from class_students AS
THE USER (cs_read requires caller teaches/admins the class); each active
enrollee becomes a student_submissions row (status='absent') so no student
is ever dropped from results (A7). Display names denormalised via a
documented service-role profiles read (deny-all as-user, E4).
- GET /batches/{id}/queue — submissions + per-submission mark counts + progress.
- GET /batches/{id}/results + /csv — every roster student incl. absent (blank
marks/total); CSV row always present (A7 baked into the contract).
- PUT /marks/{id} — upsert; batch_id derived server-side from the submission
(client never supplies the RLS scoping key).
- POST /batches/{id}/scans — E3 guards: MIME check, hard size ceiling (chunked
read), %PDF magic-byte sniff; owner-only; stores via service-role storage;
manual/ordered matching (QR-decode is a follow-on, no QR fixtures yet).
Unit tests cover batch/roster-seed/list, queue, results+CSV A7, mark upsert
round-trip, and all scan guards + owner check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
17 lines
550 B
Python
17 lines
550 B
Python
"""Exam-marker API package (/api/exam/).
|
|
|
|
A clean top-level router group (R5.1/E5), deliberately NOT nested under /database/. Every
|
|
endpoint authenticates the JWT and calls Supabase as-the-user so the RLS in
|
|
volumes/db/cc/72-exam-marker.sql is enforced (spec E1/E2 fixes).
|
|
"""
|
|
from fastapi import APIRouter
|
|
|
|
from routers.exam.templates import router as templates_router
|
|
from routers.exam.batches import router as batches_router
|
|
|
|
router = APIRouter()
|
|
router.include_router(templates_router)
|
|
router.include_router(batches_router)
|
|
|
|
__all__ = ["router"]
|