# Django Backend Specification (Minimal) ## Purpose Provide a tiny REST API to read exams from files, manage attempts, autosave progress, and produce an output JSON bundle. ## Endpoints - GET `/api/exams` → list published exams (from `manifest.json` and `input/`) - GET `/api/exams/{examId}` → return exam JSON (from `input/`) - POST `/api/exams/{examId}/attempt` → start or resume attempt; returns attempt JSON - PUT `/api/attempts/{attemptId}` → autosave answers and timestamps; returns updated attempt - POST `/api/attempts/{attemptId}/submit` → finalize, write output bundle, mark finished; returns output path - GET `/api/progress/me` → return progress snapshot for current user ## Storage Conventions - `input/{examId}.json` — canonical exam file - `attempts/{userId}/{examId}/{attemptId}.json` — active/resumed attempt - `output/{examId}_{attemptId}.json` — bundled `{ exam, attempt }` - `progress/{userId}.json` — progress summary - `manifest.json` — published flags and per-user finished/active sets ## Attempt JSON shape ``` { attemptId, userId, examId, status, startedAt, updatedAt, submittedAt?, answers: [ { questionId, response, timeSec? } ] } ``` ## Rules - One active attempt per exam per user (unless configured otherwise) - Use temp file + atomic rename for all writes - Validate exam exists and is published before starting - Resume uses most recent attempt by `updatedAt` ## Autosave - Accept partial answers; update progress percent = answered/total - Return server `updatedAt` for client reconciliation ## Submit - Change status to `submitted`, then `finished` - Write bundle `{ exam, attempt }` to `output/` - Update manifest and progress ## Security (minimal) - Cookie-based session with `userId` - CSRF for state-changing requests - CORS allow Angular origin ## Errors (examples) - 404 `EXAM_NOT_FOUND` - 409 `ATTEMPT_EXISTS` - 400 `INVALID_PAYLOAD` - 423 `EXAM_NOT_PUBLISHED`