This commit is contained in:
grey-cat-1908 2024-08-21 08:36:02 +00:00
parent 4c06904661
commit a1e46dd7bb
2 changed files with 27 additions and 18 deletions

View file

@ -1,6 +1,6 @@
from enum import Enum from enum import Enum
from uuid import UUID from uuid import UUID
from typing import TypeAlias, Annotated from typing import Annotated, Union, Literal
from pydantic import field_validator, field_serializer, Field from pydantic import field_validator, field_serializer, Field
@ -31,7 +31,7 @@ class BaseValue(BaseModel):
class TextValue(BaseValue): class TextValue(BaseValue):
question_type: form.QuestionType = form.QuestionType.text question_type: Literal[form.QuestionType.text] = form.QuestionType.text
value: str value: str
def validate(self, question: form.TextQuestion) -> None: def validate(self, question: form.TextQuestion) -> None:
@ -53,7 +53,7 @@ class TextValue(BaseValue):
class ScaleValue(BaseValue): class ScaleValue(BaseValue):
question_type: form.QuestionType = form.QuestionType.scale question_type: Literal[form.QuestionType.scale] = form.QuestionType.scale
value: int value: int
def validate(self, question: form.ScaleQuestion) -> None: def validate(self, question: form.ScaleQuestion) -> None:
@ -62,13 +62,17 @@ class ScaleValue(BaseValue):
class SelectorValue(BaseValue): class SelectorValue(BaseValue):
question_type: form.QuestionType = form.QuestionType.selector question_type: Literal[form.QuestionType.selector] = form.QuestionType.selector
values: set[int] values: set[int]
@field_serializer("values")
def serialize_values(self, values: set[int]):
return list(values)
def validate(self, question: form.SelectorQuestion) -> None: def validate(self, question: form.SelectorQuestion) -> None:
min_values = max(question.min_values, 1) if question.min_values else 1 min_values = max(question.min_values, 1) if question.min_values else 1
max_values = ( max_values = (
min(question.max_values, question.options) min(question.max_values, len(question.options))
if question.max_values if question.max_values
else len(question.options) else len(question.options)
) )
@ -79,7 +83,7 @@ class SelectorValue(BaseValue):
raise ValueError(AnswerError.TOO_MANY_SELECTED.value) raise ValueError(AnswerError.TOO_MANY_SELECTED.value)
Value: TypeAlias = SelectorValue | TextValue | ScaleValue Value = Annotated[Union[SelectorValue, TextValue, ScaleValue], Field(discriminator='question_type')]
class AnswerData(BaseModel): class AnswerData(BaseModel):

View file

@ -1,5 +1,5 @@
from enum import IntEnum, Enum from enum import IntEnum, Enum
from typing import TypeAlias from typing import Annotated, Union, Literal
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from pydantic import Field, field_validator, field_serializer from pydantic import Field, field_validator, field_serializer
@ -16,6 +16,7 @@ class FormError(Enum):
SIMMILAR_ID_ERR = "All questions must have different id's" SIMMILAR_ID_ERR = "All questions must have different id's"
SCALE_MIN_VALUE_ERR = "min_value must be in range from 0 to 1" SCALE_MIN_VALUE_ERR = "min_value must be in range from 0 to 1"
SCALE_MAX_VALUE_ERR = "max_value must be in range from 2 to 10" SCALE_MAX_VALUE_ERR = "max_value must be in range from 2 to 10"
EMPTY_OPTIONS_ERR = "Options field cannot be empty"
class QuestionType(IntEnum): class QuestionType(IntEnum):
@ -44,11 +45,11 @@ class BaseQuestion(BaseModel):
class Option(BaseModel): class Option(BaseModel):
label: str label: str
image_url: str | None image_url: str | None = None
class TextQuestion(BaseQuestion): class TextQuestion(BaseQuestion):
question_type: QuestionType = QuestionType.text question_type: Literal[QuestionType.text] = QuestionType.text
validator: TextValidator | None = None validator: TextValidator | None = None
min_length: int | None = None min_length: int | None = None
max_length: int | None = None max_length: int | None = None
@ -80,7 +81,7 @@ class TextQuestion(BaseQuestion):
class ScaleQuestion(BaseQuestion): class ScaleQuestion(BaseQuestion):
question_type: QuestionType = QuestionType.scale question_type: Literal[QuestionType.scale] = QuestionType.scale
min_value: int min_value: int
min_label: str | None = None min_label: str | None = None
max_value: int max_value: int
@ -102,17 +103,22 @@ class ScaleQuestion(BaseQuestion):
class SelectorQuestion(BaseQuestion): class SelectorQuestion(BaseQuestion):
question_type: QuestionType = QuestionType.selector question_type: Literal[QuestionType.selector] = QuestionType.selector
min_values: int = 1 min_values: int = 1
max_values: int | None = None max_values: int | None = None
options: list[Option] = [] options: list[Option]
@field_validator("options")
@classmethod
def validate_options(cls, v, info):
if len(v) < 1:
raise ValueError(FormError.EMPTY_OPTIONS_ERR.value)
return v
@field_validator("min_values") @field_validator("min_values")
@classmethod @classmethod
def validate_min_values(cls, v, info): def validate_min_values(cls, v, info):
options = info.data.get("options") if v is not None and v < 1:
options = [] if not options else options
if v is not None and (v < 1 or v > len(options)):
raise ValueError(FormError.MIN_VALUES_ERR.value) raise ValueError(FormError.MIN_VALUES_ERR.value)
return v return v
@ -120,13 +126,12 @@ class SelectorQuestion(BaseQuestion):
@classmethod @classmethod
def validate_max_values(cls, v, info): def validate_max_values(cls, v, info):
min_values = info.data.get("min_values") min_values = info.data.get("min_values")
options = info.data.get("options") if v is not None and min_values > v:
if v is not None and (v > len(options) or min_values > v):
raise ValueError(FormError.MAX_VALUES_ERR.value) raise ValueError(FormError.MAX_VALUES_ERR.value)
return v return v
Question: TypeAlias = SelectorQuestion | TextQuestion | ScaleQuestion Question = Annotated[Union[SelectorQuestion, TextQuestion, ScaleQuestion], Field(discriminator='question_type')]
class Page(BaseModel): class Page(BaseModel):