from __future__ import annotations from typing import Any from labs.exams.attempt import ExamAttempt as DomainExamAttempt from labs.models import ( User, Exam, ExamVariant, ExamSection, ExamQuestion, ) def create_user( *, email: str = "test@example.com", language_code: str = "en", first_name: str = "Test", last_name: str = "User", ) -> User: return User.objects.create_user( email=email, language_code=language_code, first_name=first_name, last_name=last_name, ) def create_exam_with_variant( *, code: str = "exam_001", credits: int = 0, employee_only: bool = False, certificate_short_name: str | None = None, language_code: str = "en", title: str = "Exam 001 (EN)", duration_seconds: int = 3600, pass_score: int = 0, total_score: int = 0, ) -> tuple[Exam, ExamVariant]: exam = Exam.objects.create( code=code, credits=credits, employee_only=employee_only, certificate_short_name=certificate_short_name, ) variant = ExamVariant.objects.create( exam=exam, language_code=language_code, title=title, duration_seconds=duration_seconds, pass_score=pass_score, total_score=total_score, ) return exam, variant def create_section( *, variant: ExamVariant, section_type: int, display_order: int, title: str = "Section", content: Any | None = None, ) -> ExamSection: if content is None: content = {} return ExamSection.objects.create( variant=variant, title=title, section_type=section_type, content=content, display_order=display_order, ) def create_question( *, variant: ExamVariant, section: ExamSection, question_type: int, display_order: int, score: int = 1, stem: str = "Question", options: list[dict] | None = None, answer: list[Any] | None = None, ) -> ExamQuestion: if options is None: options = [{"id": "A", "text": "A"}, {"id": "B", "text": "B"}] if answer is None: answer = [options[0]["id"]] return ExamQuestion.objects.create( variant=variant, section=section, score=score, question_type=question_type, stem=stem, content=options, answer=answer, display_order=display_order, ) def create_attempt(*, user: User, exam: Exam, variant: ExamVariant) -> DomainExamAttempt: return DomainExamAttempt.create(user=user, exam=exam, variant=variant) def create_exam_topology( *, code: str = "exam_001", credits: int = 0, employee_only: bool = False, certificate_short_name: str | None = None, language_code: str = "en", title: str = "Exam 001 (EN)", duration_seconds: int = 3600, pass_score: int = 0, total_score: int = 0, with_instruction: bool = False, instruction_content: Any | None = None, question_count: int = 1, ) -> tuple[Exam, ExamVariant, list[ExamSection], list[ExamQuestion]]: """ Create a minimal exam topology: one QUESTION section (+ optional INSTRUCTION section) with N questions. """ exam, variant = create_exam_with_variant( code=code, credits=credits, employee_only=employee_only, certificate_short_name=certificate_short_name, language_code=language_code, title=title, duration_seconds=duration_seconds, pass_score=pass_score, total_score=total_score, ) sections: list[ExamSection] = [] if with_instruction: if instruction_content is None: instruction_content = {"text": "Read this"} sections.append( create_section( variant=variant, section_type=ExamSection.SectionType.INSTRUCTION, display_order=0, title="Instruction", content=instruction_content, ) ) question_section_display_order = 1 else: question_section_display_order = 0 question_section = create_section( variant=variant, section_type=ExamSection.SectionType.QUESTION, display_order=question_section_display_order, title="Questions", content={}, ) sections.append(question_section) questions: list[ExamQuestion] = [] for i in range(question_count): questions.append( create_question( variant=variant, section=question_section, question_type=ExamQuestion.QuestionType.SINGLE_CHOICE, display_order=i, ) ) return exam, variant, sections, questions