formaptix-server/models/answer.py

134 lines
4.5 KiB
Python
Raw Normal View History

2024-08-14 16:21:23 +03:00
from enum import Enum
from uuid import UUID
2024-08-21 11:36:02 +03:00
from typing import Annotated, Union, Literal
2024-08-14 16:21:23 +03:00
2024-08-16 14:39:50 +03:00
from pydantic import field_validator, field_serializer, Field
2024-08-14 16:21:23 +03:00
from models import BaseModel, form
2024-08-17 16:01:26 +03:00
from utils import validate_tin, validate_snils
2024-08-14 16:21:23 +03:00
class AnswerError(Enum):
TOO_SHORT = "The text value is shorter than the minimum allowed length."
TOO_LONG = "The text value is longer than the maximum allowed length."
TOO_FEW_SELECTED = "The number of selected items is less than the minimum required."
TOO_MANY_SELECTED = "The number of selected items is more than the maximum allowed."
DUPLICATE_QUESTIONS = "Each value must correspond to a different question."
2024-08-17 16:01:26 +03:00
REQUIRED_QUIESTION_NOT_ANSWERED = "The required question was not answered."
2024-08-14 16:21:23 +03:00
INCORRECT_IDS = "The ids for some questions are incorrect."
2024-08-17 16:01:26 +03:00
TIN_VALIDATION_FAILED = "The TIN validation process failed."
SNILS_VALIDATION_FAILED = "The SNILS validation process failed."
2024-08-20 09:45:07 +03:00
SCALE_VALUE_NOT_IN_RANGE = "The scale value must be within the specified range."
2024-08-14 16:21:23 +03:00
class BaseValue(BaseModel):
question_id: UUID
question_type: form.QuestionType
2024-08-15 21:01:01 +03:00
@field_serializer("question_id")
def serialize_id(self, id: UUID):
return str(id)
2024-08-14 16:21:23 +03:00
class TextValue(BaseValue):
2024-08-21 11:36:02 +03:00
question_type: Literal[form.QuestionType.text] = form.QuestionType.text
2024-08-14 16:21:23 +03:00
value: str
def validate(self, question: form.TextQuestion) -> None:
if question.min_length and len(self.value) < question.min_length:
2024-08-15 21:01:01 +03:00
raise ValueError(AnswerError.TOO_SHORT.value)
2024-08-14 16:21:23 +03:00
if question.max_length and len(self.value) > question.max_length:
2024-08-15 21:01:01 +03:00
raise ValueError(AnswerError.TOO_LONG.value)
2024-08-14 16:21:23 +03:00
2024-08-17 16:01:26 +03:00
if (
question.validator == form.TextValidator.tin
and validate_tin(self.value) is False
):
raise ValueError(AnswerError.TIN_VALIDATION_FAILED.value)
if (
question.validator == form.TextValidator.snils
and validate_snils(self.value) is False
):
raise ValueError(AnswerError.SNILS_VALIDATION_FAILED.value)
2024-08-14 16:21:23 +03:00
2024-08-20 09:45:07 +03:00
class ScaleValue(BaseValue):
2024-08-21 11:36:02 +03:00
question_type: Literal[form.QuestionType.scale] = form.QuestionType.scale
2024-08-20 09:45:07 +03:00
value: int
def validate(self, question: form.ScaleQuestion) -> None:
if not (question.min_value <= self.value <= question.max_value):
raise ValueError(AnswerError.SCALE_VALUE_NOT_IN_RANGE.value)
2024-08-14 16:21:23 +03:00
class SelectorValue(BaseValue):
2024-08-21 11:36:02 +03:00
question_type: Literal[form.QuestionType.selector] = form.QuestionType.selector
2024-08-14 16:21:23 +03:00
values: set[int]
2024-08-21 11:36:02 +03:00
@field_serializer("values")
def serialize_values(self, values: set[int]):
return list(values)
2024-08-14 16:21:23 +03:00
def validate(self, question: form.SelectorQuestion) -> None:
2024-08-14 16:46:48 +03:00
min_values = max(question.min_values, 1) if question.min_values else 1
2024-08-14 16:21:23 +03:00
max_values = (
2024-08-21 11:36:02 +03:00
min(question.max_values, len(question.options))
2024-08-14 16:21:23 +03:00
if question.max_values
else len(question.options)
)
if len(self.values) < min_values:
2024-08-15 21:01:01 +03:00
raise ValueError(AnswerError.TOO_FEW_SELECTED.value)
2024-08-14 16:21:23 +03:00
if len(self.values) > max_values:
2024-08-15 21:01:01 +03:00
raise ValueError(AnswerError.TOO_MANY_SELECTED.value)
2024-08-14 16:21:23 +03:00
2024-08-21 11:44:25 +03:00
Value = Annotated[
Union[SelectorValue, TextValue, ScaleValue], Field(discriminator="question_type")
]
2024-08-14 16:21:23 +03:00
2024-08-15 21:01:01 +03:00
class AnswerData(BaseModel):
2024-08-14 16:21:23 +03:00
values: list[Value]
@property
def question_uuids(self) -> dict[UUID, Value]:
return {value.question_id: value for value in self.values}
@field_validator("values")
@classmethod
def validate_values(cls, v, info):
uuids = set()
for value in v:
uuids.add(value.question_id)
if len(v) != len(uuids):
2024-08-15 21:01:01 +03:00
raise ValueError(AnswerError.DUPLICATE_QUESTIONS.value)
return v
2024-08-14 16:46:48 +03:00
2024-08-14 16:21:23 +03:00
class Answer(BaseModel):
id: int
2024-08-16 14:39:50 +03:00
form: Annotated[form.Form, Field(exclude=True)]
2024-08-14 16:21:23 +03:00
data: AnswerData
@field_validator("data")
@classmethod
2024-08-14 16:46:48 +03:00
def answer_validator(cls, v, info):
2024-08-14 16:21:23 +03:00
uuids = v.question_uuids
2024-08-15 21:01:01 +03:00
questions = info.data["form"].data.questions
2024-08-14 16:21:23 +03:00
for question in questions:
if question.required and question.id not in uuids:
2024-08-15 21:01:01 +03:00
raise ValueError(AnswerError.REQUIRED_QUIESTION_NOT_ANSWERED.value)
2024-08-27 13:09:36 +03:00
if uuids.get(question.id):
if question.question_type != uuids[question.id].question_type:
raise ValueError(AnswerError.REQUIRED_QUIESTION_NOT_ANSWERED.value)
uuids[question.id].validate(question)
del uuids[question.id]
2024-08-14 16:21:23 +03:00
if len(uuids) > 0:
2024-08-15 21:01:01 +03:00
raise ValueError(AnswerError.INCORRECT_IDS.value)
2024-08-14 16:21:23 +03:00
return v