d5f6abb5ad
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>
69 lines
1.5 KiB
TypeScript
69 lines
1.5 KiB
TypeScript
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`),
|
|
}
|