feat: Sprint 2 — test taking + results
Backend:
- Models: TestAttempt, AttemptAnswer (migration 002)
- POST /api/attempts: start attempt, shuffle questions/answers,
hide is_correct, expose is_multiple for UI hints
- POST /api/attempts/{id}/submit: save answers, calculate score,
strict matching (selected == correct), return full result
- GET /api/attempts/{id}/result: fetch saved result
- Register attempts router in main.py
Frontend:
- api/attempts.ts: types + API functions
- TestTake page: one question at a time, progress bar, timer
with auto-submit, back navigation controlled by test setting,
radio/checkbox based on is_multiple
- AttemptResult page: score, pass/fail, per-question breakdown
with correct/selected/missed answer highlighting
- App.tsx: add /tests/:testId/take and /attempts/:id/result routes
- TestDetail: add "Пройти тест" button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import client from './client'
|
||||
|
||||
export interface AnswerForTest {
|
||||
id: number
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface QuestionForTest {
|
||||
id: number
|
||||
text: string
|
||||
is_multiple: boolean // true → показываем чекбоксы, false → радио-кнопки
|
||||
answers: AnswerForTest[]
|
||||
}
|
||||
|
||||
export interface AttemptStarted {
|
||||
id: number
|
||||
test_id: number
|
||||
test_title: string
|
||||
test_description: string | null
|
||||
started_at: string
|
||||
time_limit: number | null // минуты
|
||||
allow_navigation_back: boolean
|
||||
questions: QuestionForTest[]
|
||||
}
|
||||
|
||||
export interface QuestionAnswer {
|
||||
question_id: number
|
||||
answer_ids: number[]
|
||||
}
|
||||
|
||||
export interface AnswerResult {
|
||||
id: number
|
||||
text: string
|
||||
is_correct: boolean
|
||||
is_selected: boolean
|
||||
}
|
||||
|
||||
export interface QuestionResult {
|
||||
id: number
|
||||
text: string
|
||||
is_answered_correctly: boolean
|
||||
answers: AnswerResult[]
|
||||
}
|
||||
|
||||
export interface AttemptResult {
|
||||
id: number
|
||||
test_id: number
|
||||
test_title: string
|
||||
started_at: string
|
||||
finished_at: string
|
||||
score: number
|
||||
passed: boolean
|
||||
passing_score: number
|
||||
correct_count: number
|
||||
total_count: number
|
||||
questions: QuestionResult[]
|
||||
}
|
||||
|
||||
export const attemptsApi = {
|
||||
start: (test_id: number) =>
|
||||
client.post<AttemptStarted>('/attempts', { test_id }),
|
||||
|
||||
submit: (attempt_id: number, answers: QuestionAnswer[]) =>
|
||||
client.post<AttemptResult>(`/attempts/${attempt_id}/submit`, { answers }),
|
||||
|
||||
getResult: (attempt_id: number) =>
|
||||
client.get<AttemptResult>(`/attempts/${attempt_id}/result`),
|
||||
}
|
||||
Reference in New Issue
Block a user