Files
qa_test_app/backend/app/schemas/attempt.py
T
Aleksey Razorvin fc684e7c7d Спринт 5: Трекер результатов
- Миграция 005: user_id в test_attempts (дефолт 1 = Гость)
- GET /api/attempts с фильтрами по тесту, дате и пагинацией
- Страница /tracker: таблица попыток, фильтры, пагинация
- Ссылка «Трекер» в шапке приложения

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:26:40 +05:00

116 lines
3.5 KiB
Python

from datetime import datetime
from typing import Optional
from pydantic import BaseModel
# ── Начало попытки ──────────────────────────────────────────
class AttemptStart(BaseModel):
test_id: int
class AnswerForTest(BaseModel):
"""Вариант ответа без поля is_correct — не раскрываем правильные ответы."""
id: int
text: str
model_config = {"from_attributes": True}
class QuestionForTest(BaseModel):
id: int
text: str
is_multiple: bool # True = несколько правильных ответов → показываем чекбоксы
answers: list[AnswerForTest] # перемешаны случайно
model_config = {"from_attributes": True}
class AttemptStarted(BaseModel):
"""Возвращается после старта попытки."""
id: int
test_id: int
test_title: str
test_description: Optional[str]
started_at: datetime
time_limit: Optional[int] # минуты, из теста
allow_navigation_back: bool # из теста
questions: list[QuestionForTest] # перемешаны случайно
model_config = {"from_attributes": True}
# ── Сдача попытки ────────────────────────────────────────────
class QuestionAnswer(BaseModel):
"""Ответы сотрудника на один вопрос."""
question_id: int
answer_ids: list[int] # выбранные варианты (может быть пустым)
class AttemptSubmitDto(BaseModel):
answers: list[QuestionAnswer]
# ── Результат ────────────────────────────────────────────────
class AnswerResult(BaseModel):
id: int
text: str
is_correct: bool # правильный ли ответ вообще
is_selected: bool # выбрал ли его сотрудник
model_config = {"from_attributes": True}
class QuestionResult(BaseModel):
id: int
text: str
is_answered_correctly: bool # вся комбинация ответов верна
answers: list[AnswerResult]
model_config = {"from_attributes": True}
class AttemptResult(BaseModel):
id: int
test_id: int
test_title: str
started_at: datetime
finished_at: datetime
score: float # процент правильных ответов
passed: bool # преодолён ли порог зачёта
passing_score: int # порог из теста
correct_count: int
total_count: int
questions: list[QuestionResult]
model_config = {"from_attributes": True}
# ── Трекер результатов ────────────────────────────────────────
class AttemptListItem(BaseModel):
id: int
test_id: int
test_title: str
test_version: int
user_id: int
user_name: str
started_at: datetime
finished_at: Optional[datetime]
score: Optional[float]
correct_count: Optional[int]
total_count: Optional[int]
passed: Optional[bool]
model_config = {"from_attributes": True}
class AttemptListResponse(BaseModel):
items: list[AttemptListItem]
total: int
page: int
page_size: int