import logging from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.request import Request from rest_framework.response import Response from rest_framework import status from labs.api.base import Endpoint from labs.api.bases.exam import ( ExamDetailBaseEndpoint, ExamAttemptDetailBaseEndpoint, ExamAttemptInProgressDetailBaseEndpoint, ExamAttemptSectionDetailBaseEndpoint, ) from labs.api.serializers.exam import ExamAttemptCreateSerializer from labs.exams.exam import Exam from labs.exams.attempt import ExamAttempt from labs.exams.section import ExamSectionHandler from labs.exams.exceptions import ( ExamRedeemError, ExamStartError, ExamAttemptSubmitSectionError, ExamSectionHandlerCapabilityError, ExamQuestionNotFoundError, ExamAnswerValidationError, ) logger = logging.getLogger(__name__) class ExamListEndpoint(Endpoint): permission_classes = (AllowAny,) def get(self, request: Request) -> Response: return Response(Exam.get_frontend_exam_list(request.user)) class ExamDetailEndpoint(ExamDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def get(self, request: Request, exam: Exam) -> Response: return Response(exam.get_frontend_detail()) class ExamRedeemEndpoint(ExamDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def post(self, request: Request, exam: Exam) -> Response: try: exam.redeem() except ExamRedeemError as e: return Response( {"non_field_errors": [str(e)]}, status=status.HTTP_400_BAD_REQUEST, ) return Response(status=status.HTTP_204_NO_CONTENT) class ExamAttemptCreateEndpoint(Endpoint): permission_classes = (IsAuthenticated,) def post(self, request: Request) -> Response: serializer = ExamAttemptCreateSerializer(data=request.data) serializer.is_valid(raise_exception=True) exam_variant = serializer.variant try: exam_attempt = Exam( exam_variant.exam, request.user, ).start_exam( exam_variant ) except ExamStartError as e: return Response( {"non_field_errors": [str(e)]}, status=status.HTTP_400_BAD_REQUEST, ) return Response(exam_attempt.get_frontend_detail()) class ExamAttemptDetailEndpoint(ExamAttemptDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def get(self, request: Request, exam_attempt: ExamAttempt) -> Response: return Response(exam_attempt.get_frontend_detail()) class ExamAttemptSectionListEndpoint(ExamAttemptInProgressDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def get(self, request: Request, exam_attempt: ExamAttempt) -> Response: return Response(exam_attempt.get_frontend_sections()) class ExamAttemptSectionDetailEndpoint(ExamAttemptSectionDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def get( self, request: Request, exam_attempt: ExamAttempt, section_handler: ExamSectionHandler, ) -> Response: return Response(section_handler.detail()) class ExamAttemptSectionSubmitEndpoint(ExamAttemptSectionDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def post( self, request: Request, exam_attempt: ExamAttempt, section_handler: ExamSectionHandler, ) -> Response: try: exam_attempt.submit_section(section_handler) except ExamAttemptSubmitSectionError as e: return Response( {"non_field_errors": [str(e)]}, status=status.HTTP_400_BAD_REQUEST, ) return Response(status=status.HTTP_204_NO_CONTENT) class ExamAttemptSectionQuestionListEndpoint(ExamAttemptSectionDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def get( self, request: Request, exam_attempt: ExamAttempt, section_handler: ExamSectionHandler, ) -> Response: try: get_frontend_questions = section_handler.ensure_capability( "get_frontend_questions" ) return Response(get_frontend_questions()) except ExamSectionHandlerCapabilityError as e: return Response( status=status.HTTP_404_NOT_FOUND, ) class ExamAttemptSectionQuestionDetailEndpoint(ExamAttemptSectionDetailBaseEndpoint): permission_classes = (IsAuthenticated,) def get( self, request: Request, exam_attempt: ExamAttempt, section_handler: ExamSectionHandler, question_id: int, ) -> Response: try: get_frontend_question = section_handler.ensure_capability( "get_frontend_question" ) return Response(get_frontend_question(question_id)) except ExamSectionHandlerCapabilityError as e: return Response( status=status.HTTP_404_NOT_FOUND, ) def put( self, request: Request, exam_attempt: ExamAttempt, section_handler: ExamSectionHandler, question_id: int, ) -> Response: answer = request.data.get("answer") try: submit_answer = section_handler.ensure_capability("submit_answer") submit_answer(question_id, answer) except ExamSectionHandlerCapabilityError: return Response( status=status.HTTP_404_NOT_FOUND, ) except ExamQuestionNotFoundError: return Response( status=status.HTTP_404_NOT_FOUND, ) except ExamAnswerValidationError as e: return Response( {"answer": [str(e.detail)]}, status=status.HTTP_400_BAD_REQUEST, ) return Response(status=status.HTTP_204_NO_CONTENT)