"""Контент редактора: тест + активная версия + дерево вопросов с правильными вариантами.""" from __future__ import annotations import uuid as _uuid from sqlalchemy import func from sqlalchemy.orm import selectinload from ..db import get_session from ..messages import RU from ..models import AnswerOption, Question, Test, TestVersion from .test_access import is_test_author, is_test_edit_open from .test_chain import has_any_attempt_for_test class HttpError(Exception): def __init__(self, status: int, message: str): super().__init__(message) self.status = status self.message = message def load_questions_for_version(session, test_version_id, *, include_correct: bool) -> list[dict]: if not isinstance(test_version_id, _uuid.UUID): try: test_version_id = _uuid.UUID(str(test_version_id)) except (ValueError, AttributeError): return [] questions = ( session.query(Question) .options(selectinload(Question.options)) .filter(Question.test_version_id == test_version_id) .order_by(Question.question_order) .all() ) out = [] for q in questions: options = [] for o in sorted(q.options, key=lambda x: x.option_order): base = { 'id': str(o.id), 'text': o.text, 'optionOrder': o.option_order, } if include_correct: base['isCorrect'] = bool(o.is_correct) options.append(base) out.append({ 'id': str(q.id), 'text': q.text, 'questionOrder': q.question_order, 'hasMultipleAnswers': bool(q.has_multiple_answers), 'aiHint': (q.ai_hint or '').strip() or None, 'options': options, }) return out def get_editor_content(user_id: str, test_id: str) -> dict: session = get_session() try: tid = _uuid.UUID(test_id) except (ValueError, AttributeError): raise HttpError(404, 'Тест не найден.') test = session.get(Test, tid) if not test: raise HttpError(404, 'Тест не найден.') if not is_test_author(test.created_by, user_id) and not is_test_edit_open(): raise HttpError(403, 'Доступ запрещён.') active_version = ( session.query(TestVersion) .filter(TestVersion.test_id == tid, TestVersion.is_active.is_(True)) .first() ) if not active_version: raise HttpError(400, 'Нет активной версии теста.') version_count = ( session.query(func.count(TestVersion.id)) .filter(TestVersion.test_id == tid) .scalar() or 0 ) questions = load_questions_for_version(session, active_version.id, include_correct=True) has_attempts = has_any_attempt_for_test(session, tid) return { 'test': { 'id': str(test.id), 'title': test.title, 'description': test.description, 'passingThreshold': test.passing_threshold, 'timeLimit': test.time_limit, 'hintsEnabled': bool(test.hints_enabled), 'resultMode': test.result_mode or 'end', 'hasAttempts': bool(has_attempts), 'versionCount': version_count, 'hasForkRisk': bool(has_attempts) or version_count > 1, }, 'activeVersionId': str(active_version.id), 'questions': questions, }