The canvas "Auto-detect" path (spec R3.1/R3.3/R3.5/R5.4). Async because extraction on
an image-only paper takes ~15-60s (Card-12 grounding):
POST /api/exam/templates/{id}/analyse -> {job_id} (BackgroundTasks)
GET /api/exam/jobs/{id} -> {status, counts, suggestions}
suggestions = questions + response_areas + boundaries, each source:'ai',confirmed:false
with confidence + page-fraction bounds + deterministic uuids — rendered as ghosts the
teacher confirms/dismisses; never overwrites manual shapes; persistence stays PUT /templates/{id}.
Access is as-the-user (E1/E2): the template is read under RLS (unseen -> 404, IDOR-safe)
and a job is only visible to the user who started it.
The heavy pipeline stays OUT of the API behind modules/services/exam_extract.py — an HTTP
client (aiohttp) to the exam-structure extraction service (the docling-exam-spike pipeline,
GPU-attached, deterministic), mirroring how the app calls Docling Serve/Ollama. Configured
via EXAM_EXTRACT_URL; unset -> the job fails cleanly as 'extraction unavailable'.
Follow-on (S6-1b): stand up that extraction service (POST /api/extract: source -> suggestions)
wrapping scripts/structure.py + scripts/analyse.py.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
19 lines
646 B
Python
19 lines
646 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
|
|
from routers.exam.analyse import router as analyse_router
|
|
|
|
router = APIRouter()
|
|
router.include_router(templates_router)
|
|
router.include_router(batches_router)
|
|
router.include_router(analyse_router)
|
|
|
|
__all__ = ["router"]
|