first working version

This commit is contained in:
howard
2025-10-22 20:14:31 +08:00
parent c9767b830b
commit 8dc869634e
118 changed files with 22518 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
# 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`