fixes && TIN and SNILS support

This commit is contained in:
grey-cat-1908 2024-08-17 13:01:26 +00:00
parent d891d9de86
commit cc7c6aace1
4 changed files with 121 additions and 5 deletions

View file

@ -5,6 +5,7 @@ from typing import TypeAlias, Annotated
from pydantic import field_validator, field_serializer, Field from pydantic import field_validator, field_serializer, Field
from models import BaseModel, form from models import BaseModel, form
from utils import validate_tin, validate_snils
class AnswerError(Enum): class AnswerError(Enum):
@ -13,7 +14,10 @@ class AnswerError(Enum):
TOO_FEW_SELECTED = "The number of selected items is less than the minimum required." 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." TOO_MANY_SELECTED = "The number of selected items is more than the maximum allowed."
DUPLICATE_QUESTIONS = "Each value must correspond to a different question." DUPLICATE_QUESTIONS = "Each value must correspond to a different question."
REQUIRED_QUIESTION_NOT_ANSWERED = "The required question was not answered."
INCORRECT_IDS = "The ids for some questions are incorrect." INCORRECT_IDS = "The ids for some questions are incorrect."
TIN_VALIDATION_FAILED = "The TIN validation process failed."
SNILS_VALIDATION_FAILED = "The SNILS validation process failed."
class BaseValue(BaseModel): class BaseValue(BaseModel):
@ -35,6 +39,17 @@ class TextValue(BaseValue):
if question.max_length and len(self.value) > question.max_length: if question.max_length and len(self.value) > question.max_length:
raise ValueError(AnswerError.TOO_LONG.value) raise ValueError(AnswerError.TOO_LONG.value)
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)
class SelectorValue(BaseValue): class SelectorValue(BaseValue):
question_type: form.QuestionType = form.QuestionType.selector question_type: form.QuestionType = form.QuestionType.selector

View file

@ -21,6 +21,11 @@ class QuestionType(IntEnum):
selector = 2 selector = 2
class TextValidator(IntEnum):
tin = 1
snils = 2
class BaseQuestion(BaseModel): class BaseQuestion(BaseModel):
id: UUID = Field(default_factory=uuid4) id: UUID = Field(default_factory=uuid4)
question_type: QuestionType question_type: QuestionType
@ -39,25 +44,33 @@ class Option(BaseModel):
class TextQuestion(BaseQuestion): class TextQuestion(BaseQuestion):
question_type: QuestionType = QuestionType.text question_type: QuestionType = QuestionType.text
validator: TextValidator | None = None
min_length: int | None = None min_length: int | None = None
max_length: int | None = None max_length: int | None = None
@field_validator("min_length") @field_validator("min_length")
@classmethod @classmethod
def validate_min_length(cls, v, info): def validate_min_length(cls, v, info):
if v is not None and v < 0: validator = info.data.get("validator")
if v is not None:
if v < 0:
raise ValueError(FormError.MIN_LENGTH_ERR.value) raise ValueError(FormError.MIN_LENGTH_ERR.value)
if validator is not None:
return None
return v return v
@field_validator("max_length") @field_validator("max_length")
@classmethod @classmethod
def validate_max_length(cls, v, info): def validate_max_length(cls, v, info):
min_length = info.data.get("min_length") min_length = info.data.get("min_length")
validator = info.data.get("validator")
if v is not None: if v is not None:
if v <= 0: if v <= 0:
raise ValueError(FormError.MAX_LENGTH_TOO_SMALL.value) raise ValueError(FormError.MAX_LENGTH_TOO_SMALL.value)
if min_length is not None and v < min_length: if min_length is not None and v < min_length:
raise ValueError(FormError.MAX_LENGTH_LESS_THAN_MIN_LENGTH.value) raise ValueError(FormError.MAX_LENGTH_LESS_THAN_MIN_LENGTH.value)
if validator is not None:
return None
return v return v
@ -89,9 +102,8 @@ class SelectorQuestion(BaseQuestion):
Question: TypeAlias = SelectorQuestion | TextQuestion Question: TypeAlias = SelectorQuestion | TextQuestion
class FormData(BaseModel): class Page(BaseModel):
name: str = Field(min_length=1) text: str | None = Field(None, min_length=1)
description: str | None = Field(None, min_length=1)
questions: list[Question] = [] questions: list[Question] = []
@field_validator("questions") @field_validator("questions")
@ -107,6 +119,18 @@ class FormData(BaseModel):
return v return v
class FormData(BaseModel):
name: str = Field(min_length=1)
pages: list[Page] = []
@property
def questions(self) -> list[Question]:
questions = []
for page in self.pages:
questions.extend(page.questions)
return questions
class Form(BaseModel): class Form(BaseModel):
id: int id: int
data: FormData data: FormData

1
utils/__init__.py Normal file
View file

@ -0,0 +1 @@
from .validator import *

76
utils/validator.py Normal file
View file

@ -0,0 +1,76 @@
def validate_tin(value: str) -> bool:
if len(value) != 10 and len(value) != 12:
return False
value = list(map(int, value))
if len(value) == 10:
checksum = (
(
2 * value[0]
+ 4 * value[1]
+ 10 * value[2]
+ 3 * value[3]
+ 5 * value[4]
+ 9 * value[5]
+ 4 * value[6]
+ 6 * value[7]
+ 8 * value[8]
)
% 11
% 10
)
return value[9] == checksum
if len(value) == 12:
checksum1 = (
(
7 * value[0]
+ 2 * value[1]
+ 4 * value[2]
+ 10 * value[3]
+ 3 * value[4]
+ 5 * value[5]
+ 9 * value[6]
+ 4 * value[7]
+ 6 * value[8]
+ 8 * value[9]
)
% 11
% 10
)
checksum2 = (
(
3 * value[0]
+ 7 * value[1]
+ 2 * value[2]
+ 4 * value[3]
+ 10 * value[4]
+ 3 * value[5]
+ 5 * value[6]
+ 9 * value[7]
+ 4 * value[8]
+ 6 * value[9]
+ 8 * value[10]
)
% 11
% 10
)
return value[10] == checksum1 and value[11] == checksum2
return False
def validate_snils(value: str) -> bool:
if len(value) == 11:
checksum = 0
for i in range(9):
checksum += int(value[i]) * (9 - i)
if checksum > 101:
checksum = checksum % 101
if checksum == 100 or checksum == 101:
checksum = 0
return checksum == int(value[9:])
return False