diff --git a/apps/courses/admin.py b/apps/courses/admin.py index a535e87..39d6737 100644 --- a/apps/courses/admin.py +++ b/apps/courses/admin.py @@ -54,6 +54,7 @@ class TaskObjectAdmin(admin.ModelAdmin): "id", "task_name", "question_type", + "validate_answer", "has_popups", "ordinal_number", ) diff --git a/apps/courses/migrations/0010_taskobject_validate_answer.py b/apps/courses/migrations/0010_taskobject_validate_answer.py new file mode 100644 index 0000000..8b9fe6c --- /dev/null +++ b/apps/courses/migrations/0010_taskobject_validate_answer.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-06-24 18:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("courses", "0009_task_status"), + ] + + operations = [ + migrations.AddField( + model_name="taskobject", + name="validate_answer", + field=models.BooleanField(default=True, verbose_name="Проверять ответ на вопрос?"), + ), + ] diff --git a/apps/courses/models.py b/apps/courses/models.py index 8547c3b..5ac38eb 100644 --- a/apps/courses/models.py +++ b/apps/courses/models.py @@ -118,6 +118,15 @@ class TaskObject(models.Model): object_id = models.PositiveIntegerField(verbose_name="ID единицы задачи") popup = models.ManyToManyField("Popup", blank=True, related_name="task_objects", verbose_name="Поп-ап") content_object = GenericForeignKey("content_type", "object_id") + validate_answer = models.BooleanField( + default=True, + verbose_name="Проверка ответа", + help_text=( + "Отключает проверку ответа.
" + "(Только для 'Вопрос с одним правильным ответом', 'Вопрос с одним правильным ответом (Исключающий)' " + "и 'Вопрос на соотношение', на остальных проверка не требуется)" + ), + ) def __str__(self): return f"{self.task.name} {self.ordinal_number}" diff --git a/apps/questions/services.py b/apps/questions/services.py index 13a5347..52d3888 100644 --- a/apps/questions/services.py +++ b/apps/questions/services.py @@ -1,6 +1,12 @@ +from typing import Iterable, Optional + +from rest_framework.response import Response +from rest_framework import status from rest_framework.utils.serializer_helpers import ReturnDict +from questions.mapping import TypeQuestionPoints from courses.models import TaskObject +from progress.models import TaskObjUserResult def get_error_message_for_permissions(needed_model_class: str, gotten_model_class: str) -> dict[str: str]: @@ -28,3 +34,22 @@ def add_popup_data(data: ReturnDict | dict, task_object: TaskObject) -> ReturnDi "ordinal_number": popup.ordinal_number, }) return data + + +def handle_no_validation_required( + task_object_id: int, + profile_id: int, + point_type: TypeQuestionPoints, + request_data: Iterable, + response_data: dict, + required_data: Optional[Iterable] = None, +) -> Response: + """ + Если у TaskObject отключена проверка. + Проверка лишь на наличие ответа, а в случае с вопросом на соотношение, что все блоки сопоставлены. + """ + if (required_data and len(request_data) < len(required_data)) or not request_data: + return Response({"text": "need more..."}, status=status.HTTP_400_BAD_REQUEST) + + TaskObjUserResult.objects.create_user_result(task_object_id, profile_id, point_type) + return Response(response_data, status=status.HTTP_201_CREATED) diff --git a/apps/questions/views/answers.py b/apps/questions/views/answers.py index bebee01..74e1d4d 100644 --- a/apps/questions/views/answers.py +++ b/apps/questions/views/answers.py @@ -8,9 +8,12 @@ from procollab_skills.permissions import IfSubscriptionOutdatedPermission from progress.models import TaskObjUserResult +from questions import serializers +from questions import api_examples from questions.exceptions import UserAlreadyAnsweredException from questions.mapping import TypeQuestionPoints from questions.permissions import CheckQuestionTypePermission, SimpleCheckQuestionTypePermission +from questions.services import handle_no_validation_required from questions.models import ( QuestionSingleAnswer, QuestionConnect, @@ -19,8 +22,6 @@ AnswerSingle, AnswerConnect, ) -from questions import serializers -from questions import api_examples @extend_schema( @@ -47,6 +48,17 @@ class SingleCorrectPost(generics.CreateAPIView): def create(self, request, *args, **kwargs) -> Response: try: + + # Если у TaskObject отключена проверка ответа, то дальнешие действия не нужны. + if not self.request_task_object.validate_answer: + return handle_no_validation_required( + self.task_object_id, + self.profile_id, + TypeQuestionPoints.QUESTION_SINGLE_ANSWER, + request.data.get("answer_id"), + {"is_correct": True}, + ) + question_answers: QuerySet[AnswerSingle] = self.request_question.single_answers.all() given_answer: AnswerSingle = question_answers.get(id=request.data.get("answer_id")) is_correct_answer: bool = given_answer.is_correct @@ -94,10 +106,21 @@ class ConnectQuestionPost(generics.CreateAPIView): def create(self, request, *args, **kwargs) -> Response: try: - user_answers = request.data - question: QuestionConnect = self.request_question all_answer_options: QuerySet[AnswerConnect] = question.connect_answers.all() + + # Если у TaskObject отключена проверка ответа, то дальнешие действия не нужны. + if not self.request_task_object.validate_answer: + return handle_no_validation_required( + self.task_object_id, + self.profile_id, + TypeQuestionPoints.QUESTION_CONNECT, + request.data, + {"text": "success"}, + required_data=all_answer_options, + ) + + user_answers = request.data answers_left_to_check: list[int] = list(all_answer_options.values_list("id", flat=True)) # all_answers_count = all_answer_options.count() @@ -163,6 +186,16 @@ def create(self, request, *args, **kwargs) -> Response: try: given_answer_ids: list[int] = request.data + # Если у TaskObject отключена проверка ответа, то дальнешие действия не нужны. + if not self.request_task_object.validate_answer: + return handle_no_validation_required( + self.task_object_id, + self.profile_id, + TypeQuestionPoints.QUESTION_EXCLUDE, + given_answer_ids, + {"text": "success"}, + ) + # Все правильные ответы (в рамках исключения). set_correct_answer_ids: set[int] = set( self.request_question.single_answers.filter(is_correct=False).values_list("id", flat=True)