diff --git a/models/answer.py b/models/answer.py index 91b31c4..688f941 100644 --- a/models/answer.py +++ b/models/answer.py @@ -1,6 +1,6 @@ from enum import Enum from uuid import UUID -from typing import TypeAlias, Annotated +from typing import Annotated, Union, Literal from pydantic import field_validator, field_serializer, Field @@ -31,7 +31,7 @@ class BaseValue(BaseModel): class TextValue(BaseValue): - question_type: form.QuestionType = form.QuestionType.text + question_type: Literal[form.QuestionType.text] = form.QuestionType.text value: str def validate(self, question: form.TextQuestion) -> None: @@ -53,7 +53,7 @@ class TextValue(BaseValue): class ScaleValue(BaseValue): - question_type: form.QuestionType = form.QuestionType.scale + question_type: Literal[form.QuestionType.scale] = form.QuestionType.scale value: int def validate(self, question: form.ScaleQuestion) -> None: @@ -62,13 +62,17 @@ class ScaleValue(BaseValue): class SelectorValue(BaseValue): - question_type: form.QuestionType = form.QuestionType.selector + question_type: Literal[form.QuestionType.selector] = form.QuestionType.selector values: set[int] + @field_serializer("values") + def serialize_values(self, values: set[int]): + return list(values) + def validate(self, question: form.SelectorQuestion) -> None: min_values = max(question.min_values, 1) if question.min_values else 1 max_values = ( - min(question.max_values, question.options) + min(question.max_values, len(question.options)) if question.max_values else len(question.options) ) @@ -79,7 +83,7 @@ class SelectorValue(BaseValue): 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): diff --git a/models/form.py b/models/form.py index 16ff761..0cf199f 100644 --- a/models/form.py +++ b/models/form.py @@ -1,5 +1,5 @@ from enum import IntEnum, Enum -from typing import TypeAlias +from typing import Annotated, Union, Literal from uuid import UUID, uuid4 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" 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" + EMPTY_OPTIONS_ERR = "Options field cannot be empty" class QuestionType(IntEnum): @@ -44,11 +45,11 @@ class BaseQuestion(BaseModel): class Option(BaseModel): label: str - image_url: str | None + image_url: str | None = None class TextQuestion(BaseQuestion): - question_type: QuestionType = QuestionType.text + question_type: Literal[QuestionType.text] = QuestionType.text validator: TextValidator | None = None min_length: int | None = None max_length: int | None = None @@ -80,7 +81,7 @@ class TextQuestion(BaseQuestion): class ScaleQuestion(BaseQuestion): - question_type: QuestionType = QuestionType.scale + question_type: Literal[QuestionType.scale] = QuestionType.scale min_value: int min_label: str | None = None max_value: int @@ -102,17 +103,22 @@ class ScaleQuestion(BaseQuestion): class SelectorQuestion(BaseQuestion): - question_type: QuestionType = QuestionType.selector + question_type: Literal[QuestionType.selector] = QuestionType.selector min_values: int = 1 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") @classmethod def validate_min_values(cls, v, info): - options = info.data.get("options") - options = [] if not options else options - if v is not None and (v < 1 or v > len(options)): + if v is not None and v < 1: raise ValueError(FormError.MIN_VALUES_ERR.value) return v @@ -120,13 +126,12 @@ class SelectorQuestion(BaseQuestion): @classmethod def validate_max_values(cls, v, info): min_values = info.data.get("min_values") - options = info.data.get("options") - if v is not None and (v > len(options) or min_values > v): + if v is not None and min_values > v: raise ValueError(FormError.MAX_VALUES_ERR.value) return v -Question: TypeAlias = SelectorQuestion | TextQuestion | ScaleQuestion +Question = Annotated[Union[SelectorQuestion, TextQuestion, ScaleQuestion], Field(discriminator='question_type')] class Page(BaseModel):