Skip to content

Commit e89f759

Browse files
authored
Merge pull request #209 from PROCOLLAB-github/dev
Расширены поля skills в endpoint all-skills, доработана система начисления баллов
2 parents 7b4a1af + 71350dc commit e89f759

3 files changed

Lines changed: 53 additions & 31 deletions

File tree

apps/courses/serializers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class Meta:
5454
fields = (
5555
"id",
5656
"name",
57+
"is_from_trajectory",
58+
"requires_subscription",
5759
"who_created",
5860
"file_link",
5961
"quantity_of_levels",

apps/questions/services/check_questions_answers.py

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
from copy import deepcopy
33
from typing import Any, Iterable, Optional, Union
44

5+
from courses.models import Task, TaskObject
56
from django.db import transaction
67
from django.db.models import QuerySet
7-
from rest_framework import status
8-
9-
from courses.models import TaskObject
108
from progress.models import TaskObjUserResult, UserAnswersAttemptCounter
11-
from questions.exceptions import (QustionConnectException,
12-
UserAlreadyAnsweredException)
9+
from questions.exceptions import QustionConnectException, UserAlreadyAnsweredException
1310
from questions.mapping import TypeQuestionPoints
1411
from questions.models.answers import AnswerConnect, AnswerSingle
15-
from questions.models.questions import (InfoSlide, QuestionConnect,
16-
QuestionSingleAnswer, QuestionWrite)
12+
from questions.models.questions import (
13+
InfoSlide,
14+
QuestionConnect,
15+
QuestionSingleAnswer,
16+
QuestionWrite,
17+
)
18+
from rest_framework import status
1719

1820

1921
class AbstractAnswersService(ABC):
@@ -27,7 +29,9 @@ class AbstractAnswersService(ABC):
2729
def __init__(
2830
self,
2931
request_profile_id: int,
30-
request_question: Union[InfoSlide, QuestionWrite, QuestionConnect, QuestionSingleAnswer],
32+
request_question: Union[
33+
InfoSlide, QuestionWrite, QuestionConnect, QuestionSingleAnswer
34+
],
3135
request_task_object: TaskObject,
3236
request_data: dict,
3337
) -> None:
@@ -90,9 +94,14 @@ def _process_answer_attempt_counter(self, question_answers: Any):
9094
counter.attempts_made_before += 1
9195

9296
# Проверка после подсказки, если counter `after` max, то сохранение без баллов:
93-
if counter.is_take_hint and counter.attempts_made_after >= self.request_question.attempts_after_hint:
97+
if (
98+
counter.is_take_hint
99+
and counter.attempts_made_after >= self.request_question.attempts_after_hint
100+
):
94101
counter.delete()
95-
self._create_tast_obj_result(TypeQuestionPoints.QUESTION_WO_POINTS, correct_answer=False)
102+
self._create_tast_obj_result(
103+
TypeQuestionPoints.QUESTION_WO_POINTS, correct_answer=False
104+
)
96105
question_answer_body = self._create_answer_response_body(question_answers)
97106
question_answer_body["hint"] = self.request_question.hint_text
98107
return question_answer_body, status.HTTP_201_CREATED
@@ -105,13 +114,20 @@ def _process_answer_attempt_counter(self, question_answers: Any):
105114
counter.save()
106115
response_body = deepcopy(self._UNSUCCESS_RESPONSE_BODY)
107116

108-
if self.request_question.attempts_after_hint and self.request_question.hint_text:
117+
if (
118+
self.request_question.attempts_after_hint
119+
and self.request_question.hint_text
120+
):
109121
response_body["hint"] = self.request_question.hint_text
110122
return response_body, status.HTTP_400_BAD_REQUEST
111123
else:
112124
counter.delete()
113-
self._create_tast_obj_result(TypeQuestionPoints.QUESTION_WO_POINTS, correct_answer=False)
114-
question_answer_body = self._create_answer_response_body(question_answers)
125+
self._create_tast_obj_result(
126+
TypeQuestionPoints.QUESTION_WO_POINTS, correct_answer=False
127+
)
128+
question_answer_body = self._create_answer_response_body(
129+
question_answers
130+
)
115131
if self.request_question.hint_text:
116132
question_answer_body["hint"] = self.request_question.hint_text
117133
return question_answer_body, status.HTTP_201_CREATED
@@ -125,7 +141,9 @@ def _handle_no_question_validation(
125141
required_data: Optional[Iterable] = None,
126142
) -> tuple[dict, int]:
127143
"""Если у TaskObject отключена проверка (необходимо ответить хоть что-то/сопоставить все даже неправильно)."""
128-
if (required_data and len(self.request_data) < len(required_data)) or not self.request_data:
144+
if (
145+
required_data and len(self.request_data) < len(required_data)
146+
) or not self.request_data:
129147
return self._WRONG_ANSWER_WO_VALIDATION, status.HTTP_400_BAD_REQUEST
130148
self._create_tast_obj_result(point_type)
131149
return self._SUCCESS_RESPONSE_BODY, status.HTTP_201_CREATED
@@ -145,7 +163,7 @@ def _create_tast_obj_result(
145163
points = 0
146164
else:
147165
skill = self.request_task_object.task.skill
148-
tasks_count = skill.tasks.count() or 1 # защита от деления на 0
166+
tasks_count = Task.published.filter(skill=skill).count()
149167
points = round(80 / tasks_count)
150168

151169
TaskObjUserResult.objects.create(
@@ -174,10 +192,7 @@ class SingleCorrectAnswerService(AbstractAnswersService):
174192
def _check_correct_answer(self) -> tuple[dict[str, Any], int]:
175193
"""Проверка на корректность ответа + формирование процесса с `couter` (если вопросом это предполагается)."""
176194
question_answer: QuerySet[AnswerSingle] = (
177-
self.request_question
178-
.single_answers
179-
.filter(is_correct=True)
180-
.first()
195+
self.request_question.single_answers.filter(is_correct=True).first()
181196
)
182197

183198
if self.request_data.get("answer_id") == question_answer.id:
@@ -202,7 +217,9 @@ class QuestionExcludeAnswerService(AbstractAnswersService):
202217

203218
def _check_correct_answer(self) -> tuple[dict[str, Any], int]:
204219
correct_answers_ids: set[int] = set(
205-
self.request_question.single_answers.filter(is_correct=False).values_list("id", flat=True)
220+
self.request_question.single_answers.filter(is_correct=False).values_list(
221+
"id", flat=True
222+
)
206223
)
207224
given_answer_ids: set[int] = set(self.request_data)
208225

@@ -228,18 +245,21 @@ class QuestionConnectAnswerService(AbstractAnswersService):
228245
_QUSTION_POINTS: TypeQuestionPoints = TypeQuestionPoints.QUESTION_CONNECT
229246

230247
def _check_correct_answer(self) -> tuple[dict[str, Any], int]:
231-
all_answer_options: QuerySet[AnswerConnect] = self.request_question.connect_answers.all()
248+
all_answer_options: QuerySet[AnswerConnect] = (
249+
self.request_question.connect_answers.all()
250+
)
232251

233252
answers_ids: list[int] = set(all_answer_options.values_list("id", flat=True))
234-
user_answer_ids: set[int] = (
235-
{answer["right_id"] for answer in self.request_data}
236-
| {answer["left_id"] for answer in self.request_data}
237-
)
253+
user_answer_ids: set[int] = {
254+
answer["right_id"] for answer in self.request_data
255+
} | {answer["left_id"] for answer in self.request_data}
238256

239257
if answers_ids != user_answer_ids:
240258
raise QustionConnectException("Wrong ids or need more answers.")
241259

242-
answer_is_correct: bool = all([answer["right_id"] == answer["left_id"] for answer in self.request_data])
260+
answer_is_correct: bool = all(
261+
[answer["right_id"] == answer["left_id"] for answer in self.request_data]
262+
)
243263
if answer_is_correct:
244264
self._create_tast_obj_result(self._QUSTION_POINTS)
245265
self._delete_self_counter()
@@ -253,17 +273,14 @@ def _check_correct_answer(self) -> tuple[dict[str, Any], int]:
253273
def _create_answer_response_body(self, question_answer: AnswerSingle):
254274
response_body = deepcopy(self._UNSUCCESS_RESPONSE_BODY)
255275
response_body["answer_ids"] = [
256-
{
257-
"left_id": answer.id,
258-
"right_id": answer.id
259-
}
260-
for answer in question_answer
276+
{"left_id": answer.id, "right_id": answer.id} for answer in question_answer
261277
]
262278
return response_body
263279

264280

265281
class QuestionWriteAnswerService(AbstractAnswersService):
266282
"""Проверка (запись) ответа пользователя на вопрос с вводом ответа."""
283+
267284
_QUSTION_POINTS: TypeQuestionPoints = TypeQuestionPoints.QUESTION_WRITE
268285

269286
def create_answer(self) -> tuple[dict[str, Any], int]:
@@ -282,6 +299,7 @@ def _create_answer_response_body(self) -> dict[str, Any]:
282299

283300
class InfoSlideAnswerService(AbstractAnswersService):
284301
"""Проверка (запись) ответа пользователя инфо слайд."""
302+
285303
_QUSTION_POINTS: TypeQuestionPoints = TypeQuestionPoints.INFO_SLIDE
286304

287305
def create_answer(self) -> tuple[dict[str, Any], int]:

apps/tests/courses_tests/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@
8989
{
9090
"id": 1,
9191
"name": "Навык 1",
92+
"is_from_trajectory": False,
93+
"requires_subscription": True,
9294
"who_created": "Создатель",
9395
"file_link": "http://some.com/",
9496
"free_access": False,

0 commit comments

Comments
 (0)