54 lines
1.9 KiB
Markdown
54 lines
1.9 KiB
Markdown
# 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`
|