from rest_framework import status from rest_framework.exceptions import APIException, NotFound, PermissionDenied from rest_framework.request import Request from labs.api.base import Endpoint from labs.exams.exam import Exam from labs.exams.attempt import ExamAttempt from labs.models import ( User, Exam as DBExam, ExamAttempt as DBExamAttempt, ExamAttemptSection as DBExamAttemptSection, ) class ExamDetailBaseEndpoint(Endpoint): def convert_args( self, request: Request, exam_id: int, *args, **kwargs, ): try: db_exam = DBExam.objects.get(id=exam_id, published=True) except DBExam.DoesNotExist: raise NotFound(detail="Exam not found") user: User = request.user if db_exam.employee_only: if not (user.is_authenticated and user.is_pingcap_employee): raise PermissionDenied(detail="This exam is for employees only.") kwargs["exam"] = Exam(db_exam, request.user) return args, kwargs class ExamAttemptDetailBaseEndpoint(Endpoint): def convert_args( self, request: Request, exam_attempt_id: int, *args, **kwargs, ): try: db_exam_attempt = DBExamAttempt.objects.get( id=exam_attempt_id, user=request.user, ) except DBExamAttempt.DoesNotExist: raise NotFound(detail="Exam attempt not found") attempt = ExamAttempt(db_exam_attempt) self.ensure_attempt_accessible(attempt) kwargs["exam_attempt"] = attempt return args, kwargs def ensure_attempt_accessible(self, exam_attempt: ExamAttempt) -> None: pass class ExamAttemptInProgressDetailBaseEndpoint(ExamAttemptDetailBaseEndpoint): def ensure_attempt_accessible(self, exam_attempt: ExamAttempt) -> None: exam_attempt.ensure_in_progress() class ExamAttemptSectionDetailBaseEndpoint(Endpoint): class SectionNotAvailable(APIException): status_code = status.HTTP_409_CONFLICT default_detail = "This section is not available." default_code = "section_not_available" def convert_args( self, request: Request, exam_attempt_id: int, section_id: int, *args, **kwargs, ): try: section = DBExamAttemptSection.objects.select_related("attempt").get( section_id=section_id, attempt__id=exam_attempt_id, attempt__user=request.user, ) except DBExamAttemptSection.DoesNotExist: raise NotFound(detail="Section not found") exam_attempt = ExamAttempt(section.attempt) exam_attempt.ensure_in_progress() section_handler = exam_attempt.get_section_handler(section) if not section_handler.avaliable(): raise self.SectionNotAvailable() kwargs["exam_attempt"] = exam_attempt kwargs["section_handler"] = section_handler return args, kwargs