Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions apps/questions/migrations/0015_alter_answersingle_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.3 on 2025-06-30 10:18

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("questions", "0014_alter_questionconnect_attempts_after_hint_and_more"),
]

operations = [
migrations.AlterField(
model_name="answersingle",
name="text",
field=models.CharField(max_length=150),
),
]
30 changes: 22 additions & 8 deletions apps/questions/models/answers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@

from files.models import FileModel
from progress.models import UserProfile
from questions.models.questions import (QuestionConnect, QuestionSingleAnswer,
QuestionWrite)
from questions.models.questions import (
QuestionConnect,
QuestionSingleAnswer,
QuestionWrite,
)


class AnswerSingle(models.Model):
text = models.CharField(max_length=100, null=False)
# Task изменить максимальную длину строки до 200 символов
text = models.CharField(max_length=150, null=False)
is_correct = models.BooleanField(default=False)
question = models.ForeignKey(
QuestionSingleAnswer,
Expand All @@ -22,8 +26,12 @@ class Meta:


class AnswerConnect(models.Model):
connect_left = models.TextField(max_length=450, null=True, blank=True, verbose_name="Вопрос (текст)")
connect_right = models.TextField(max_length=450, null=True, blank=True, verbose_name="Ответ (текст)")
connect_left = models.TextField(
max_length=450, null=True, blank=True, verbose_name="Вопрос (текст)"
)
connect_right = models.TextField(
max_length=450, null=True, blank=True, verbose_name="Ответ (текст)"
)
file_left = models.ForeignKey(
FileModel,
on_delete=models.PROTECT,
Expand Down Expand Up @@ -53,11 +61,17 @@ class Meta:
def clean(self):
"""Проверка на заполненность полей."""
if not (self.connect_left or self.file_left):
raise ValidationError("Необходимо заполнить хотя бы одно из полей 'Вопрос'.")
raise ValidationError(
"Необходимо заполнить хотя бы одно из полей 'Вопрос'."
)
if not (self.connect_right or self.file_right):
raise ValidationError("Необходимо заполнить хотя бы одно из полей 'Ответ'.")
if (self.connect_left and self.file_left) or (self.connect_right and self.file_right):
raise ValidationError("Заполните только одно из полей 'Вопрос' или 'Ответ'.")
if (self.connect_left and self.file_left) or (
self.connect_right and self.file_right
):
raise ValidationError(
"Заполните только одно из полей 'Вопрос' или 'Ответ'."
)

def save(self, *args, **kwargs):
self.full_clean()
Expand Down
107 changes: 74 additions & 33 deletions apps/questions/views/questions_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,36 @@

from django.db.models import QuerySet
from drf_spectacular.utils import extend_schema
from progress.models import TaskObjUserResult
from rest_framework import generics, status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from subscription.permissions import SubscriptionTaskObjectPermission

from progress.models import TaskObjUserResult
from questions.mapping import TypeQuestionPoints
from questions.models import (AnswerConnect, InfoSlide, QuestionConnect,
QuestionSingleAnswer, QuestionWrite)
from questions.models import (
AnswerConnect,
InfoSlide,
QuestionConnect,
QuestionSingleAnswer,
QuestionWrite,
)
from questions.permissions import CheckQuestionTypePermission
from questions.serializers import (ConnectQuestionSerializer,
InfoSlideSerializer,
SingleQuestionAnswerSerializer,
WriteQuestionSerializer)
from questions.serializers import (
ConnectQuestionSerializer,
InfoSlideSerializer,
SingleQuestionAnswerSerializer,
WriteQuestionSerializer,
)
from questions.services.helpers import add_popup_data
from questions.typing import (AnswerUserWriteData, QuestionSerializerData,
QuestionWriteSerializerData,
QuestionСonnectSerializerData, SingleAnswerData,
SingleConnectedAnswerData)
from subscription.permissions import SubscriptionTaskObjectPermission
from questions.typing import (
AnswerUserWriteData,
QuestionSerializerData,
QuestionWriteSerializerData,
QuestionСonnectSerializerData,
SingleAnswerData,
SingleConnectedAnswerData,
)


class QuestionSingleAnswerGet(generics.RetrieveAPIView):
Expand All @@ -37,22 +48,28 @@ class QuestionSingleAnswerGet(generics.RetrieveAPIView):
summary="Выводит данные для трёх видов вопросов (см. описание)",
tags=["Вопросы и инфо-слайд"],
description="""для: вопроса с одним правильным ответом, вопроса на исключение,
вопроса на выбор нескольких правильных
вопроса на выбор нескольких правильных
ответов (возможно пока его нет, но в будущем может появится).""",
)
def get(self, request, *args, **kwargs) -> Response:
question: QuestionSingleAnswer = self.request_question
all_answers = question.single_answers.all()
answers = [SingleAnswerData(id=answer.id, text=answer.text) for answer in all_answers]
all_answers = question.single_answers.order_by("id")

answers = [
SingleAnswerData(id=answer.id, text=answer.text) for answer in all_answers
]

user_result = TaskObjUserResult.objects.get_answered(
self.task_object_id, self.profile_id, TypeQuestionPoints.QUESTION_SINGLE_ANSWER
self.task_object_id,
self.profile_id,
TypeQuestionPoints.QUESTION_SINGLE_ANSWER,
)

correct_answer = [
SingleAnswerData(id=all_answers.get(is_correct=True).id, text=all_answers.get(is_correct=True).text)
]
random.shuffle(answers)
try:
correct = all_answers.get(is_correct=True)
correct_answer = [SingleAnswerData(id=correct.id, text=correct.text)]
except all_answers.model.DoesNotExist:
correct_answer = []

serializer = self.serializer_class(
QuestionSerializerData(
Expand All @@ -61,11 +78,14 @@ def get(self, request, *args, **kwargs) -> Response:
description=question.description,
files=[file.link for file in question.files.all()],
answers=correct_answer if user_result else answers,
is_answered=True if user_result else False,
is_answered=bool(user_result),
video_url=question.video_url,
)
)
return Response(add_popup_data(serializer.data, self.request_task_object), status=status.HTTP_200_OK)
return Response(
add_popup_data(serializer.data, self.request_task_object),
status=status.HTTP_200_OK,
)


class QuestionConnectGet(generics.RetrieveAPIView):
Expand Down Expand Up @@ -117,7 +137,10 @@ def get(self, request, *args, **kwargs):
random.shuffle(target_left)

serializer = self.serializer_class(question_data)
return Response(add_popup_data(serializer.data, self.request_task_object), status=status.HTTP_200_OK)
return Response(
add_popup_data(serializer.data, self.request_task_object),
status=status.HTTP_200_OK,
)


class QuestionExcludeAnswerGet(generics.RetrieveAPIView):
Expand All @@ -140,9 +163,13 @@ def get(self, request, *args, **kwargs):
question: QuestionSingleAnswer = self.request_question

all_answers = question.single_answers.all()
answers = [SingleAnswerData(id=answer.id, text=answer.text) for answer in all_answers]
answers = [
SingleAnswerData(id=answer.id, text=answer.text) for answer in all_answers
]
answers_to_exclude = [
SingleAnswerData(id=answer.id, text=answer.text) for answer in all_answers if answer.is_correct
SingleAnswerData(id=answer.id, text=answer.text)
for answer in all_answers
if answer.is_correct
]

data_to_serialize = QuestionSerializerData(
Expand All @@ -162,7 +189,10 @@ def get(self, request, *args, **kwargs):
random.shuffle(answers)

serializer = self.serializer_class(data_to_serialize)
return Response(add_popup_data(serializer.data, self.request_task_object), status=status.HTTP_200_OK)
return Response(
add_popup_data(serializer.data, self.request_task_object),
status=status.HTTP_200_OK,
)


class InfoSlideDetails(generics.RetrieveAPIView):
Expand All @@ -188,16 +218,23 @@ def get(self, request, *args, **kwargs):
"files": [file.link for file in info_slide.files.all()],
"is_done": bool(
TaskObjUserResult.objects.get_answered(
self.task_object_id, self.profile_id, TypeQuestionPoints.INFO_SLIDE
self.task_object_id,
self.profile_id,
TypeQuestionPoints.INFO_SLIDE,
)
),
"video_url": info_slide.video_url
"video_url": info_slide.video_url,
}
)
if serializer.is_valid():
return Response(add_popup_data(serializer.data, self.request_task_object), status=status.HTTP_200_OK)
return Response(
add_popup_data(serializer.data, self.request_task_object),
status=status.HTTP_200_OK,
)
else:
return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
return Response(
{"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST
)


class QuestionWriteAnswer(generics.RetrieveAPIView):
Expand All @@ -221,12 +258,16 @@ def get(self, request, *args, **kwargs):
text=question.text,
description=question.description,
files=[file.link for file in question.files.all()],
video_url=question.video_url
video_url=question.video_url,
)

if user_answer := TaskObjUserResult.objects.get_answered(
self.task_object_id, self.profile_id, TypeQuestionPoints.QUESTION_WRITE
):
write_question.answer = AnswerUserWriteData(id=user_answer.id, text=user_answer.text)
write_question.answer = AnswerUserWriteData(
id=user_answer.id, text=user_answer.text
)
data = asdict(write_question)
return Response(add_popup_data(data, self.request_task_object), status=status.HTTP_200_OK)
return Response(
add_popup_data(data, self.request_task_object), status=status.HTTP_200_OK
)