DEVALIDATION OF VALIDATION

This commit is contained in:
NoeBerdoz
2025-04-13 08:45:47 +02:00
parent 790cbb43ab
commit 54e794d670
5 changed files with 120 additions and 147 deletions

View File

@ -38,4 +38,4 @@ class GameDecisionResponseWithBotDecisionDTO(BaseModel):
client_id: Optional[UUID] = None client_id: Optional[UUID] = None
client_data: Optional[Dict[str, Any]] = None client_data: Optional[Dict[str, Any]] = None
bot_decision: Literal["Accept", "Reject"] bot_decision: Literal["Accept", "Reject"]
bot_reason: str bot_reason: Optional[str]

View File

@ -1,43 +1,56 @@
from typing import Literal, Optional, Self from typing import Optional # Removed Literal, Self
from pydantic import BaseModel, ConfigDict, EmailStr, Field, model_validator from pydantic import BaseModel, ConfigDict # Removed Field, model_validator
from pydantic_extra_types.phone_numbers import PhoneNumber # Removed PhoneNumber import as it's not used and types are simplified
class FromAccount(BaseModel): class FromAccount(BaseModel):
""" """
Fields which can be extracted from account.pdf Fields which can be extracted from account.pdf - All fields optional and simplified to string.
Validators removed for testing purposes.
""" """
model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True)
# Details of the Account and Client # Details of the Account and Client
account_name: str = Field(min_length=1) account_name: Optional[str] = None
account_holder_name: str = Field(min_length=1) account_holder_name: Optional[str] = None
account_holder_surname: str = Field(min_length=1) account_holder_surname: Optional[str] = None
@model_validator(mode='after') # --- Updated Validator 1 ---
def check_account_name_is_name_surname(self) -> Self: # @model_validator(mode='after')
combined = f"{self.account_holder_name} {self.account_holder_surname}" # def check_account_name_is_name_surname(self) -> Self:
if combined != self.account_name: # # Only validate if all relevant fields are present
raise ValueError(f'Account name is not name + surname: {self.account_name} != {combined}') # if self.account_holder_name is not None and \
return self # self.account_holder_surname is not None and \
# self.account_name is not None:
# combined = f"{self.account_holder_name} {self.account_holder_surname}"
# if combined != self.account_name:
# raise ValueError(f'Account name is not name + surname: {self.account_name} != {combined}')
# return self
passport_number: str = Field(min_length=5) passport_number: Optional[str] = None # Removed Field constraint
reference_currency: Literal["CHF", "EUR", "USD", "Other"] reference_currency: Optional[str] = None # Simplified from Literal
other_currency: Optional[str] = None other_currency: Optional[str] = None
# Delivery of Communication # Delivery of Communication
building_number: str = Field(min_length=1) building_number: Optional[str] = None
street_name: str = Field(min_length=1) street_name: Optional[str] = None
postal_code: str = Field(min_length=1) postal_code: Optional[str] = None
city: str = Field(min_length=1) city: Optional[str] = None
country: str = Field(min_length=1) country: Optional[str] = None
# Application for e-banking # Application for e-banking
ebanking_name: str = Field(min_length=1) ebanking_name: Optional[str] = None
@model_validator(mode='after')
def check_account_name_ebanking_name(self) -> Self: # --- Updated Validator 2 ---
if self.ebanking_name != self.account_name: # @model_validator(mode='after')
raise ValueError(f'Ebanking name is different from account name') # def check_account_name_ebanking_name(self) -> Self:
return self # # Only validate if both fields are present
phone_number: str = Field(..., min_length=8) # if self.ebanking_name is not None and self.account_name is not None:
email: str = Field(min_length=5) # if self.ebanking_name != self.account_name:
# raise ValueError(f'Ebanking name ({self.ebanking_name}) is different from account name ({self.account_name})')
# return self
# Kept as Optional[str], but you might consider Optional[PhoneNumber]
# if you want specific phone number validation using pydantic-extra-types
phone_number: Optional[str] = None # Removed Field constraint
email: Optional[str] = None # Removed Field constraint

View File

@ -1,35 +1,32 @@
from typing import Literal from typing import Optional # Removed Literal
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict # Removed Field
class FromDescription(BaseModel): class FromDescription(BaseModel):
""" """
Fields which can be extracted from description.txt Fields which can be extracted from description.txt - All fields optional and simplified to string.
""" """
model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True)
full_name: Optional[str] = None
full_name: str = Field(..., min_length=1) age: Optional[str] = None # Simplified from int
age: int = Field(..., ge=0, le=120) nationality: Optional[str] = None
nationality: str = Field(..., min_length=1) marital_status: Optional[str] = None # Simplified from Literal
has_children: Optional[str] = None # Simplified from bool
marital_status: Literal["single", "married", "divorced", "widowed"] secondary_education_school: Optional[str] = None
has_children: bool secondary_education_year: Optional[str] = None # Simplified from int
university_name: Optional[str] = None
university_graduation_year: Optional[str] = None # Simplified from int
secondary_education_school: str occupation_title: Optional[str] = None
secondary_education_year: int = Field(..., ge=1900, le=2100) employer: Optional[str] = None
university_name: str start_year: Optional[str] = None # Simplified from int
university_graduation_year: int = Field(..., ge=1900, le=2100) annual_salary_eur: Optional[str] = None # Simplified from float
occupation_title: str total_savings_eur: Optional[str] = None # Simplified from float
employer: str has_properties: Optional[str] = None # Simplified from bool
start_year: int = Field(..., ge=1900, le=2100)
annual_salary_eur: float = Field(..., ge=0)
total_savings_eur: float = Field(..., ge=0)
has_properties: bool
inheritance_amount_eur: float = Field(..., ge=0)
inheritance_year: int = Field(..., ge=1900, le=2100)
inheritance_source: str
inheritance_amount_eur: Optional[str] = None # Simplified from float
inheritance_year: Optional[str] = None # Simplified from int
inheritance_source: Optional[str] = None

View File

@ -10,61 +10,22 @@ class FromPassport(BaseModel):
""" """
model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True)
country: str = Field( country: Optional[str] = None
..., passport_number: Optional[str] = None
min_length=3, surname: Optional[str] = None
max_length=3, given_names: Optional[str] = None
description="The country issuing the passport, as a 3-letter ISO 3166-1 alpha-3 code (e.g., 'CHE' for Switzerland)." birth_date: Optional[str] = None # Simplified from date
) citizenship: Optional[str] = None
sex: Optional[str] = None # Simplified from Literal
issue_date: Optional[str] = None # Simplified from date
expiry_date: Optional[str] = None # Simplified from date
passport_number: str = Field( # --- Updated Validator ---
..., # @model_validator(mode='after')
min_length=9, # def check_expiry_date_after_issue_date(self) -> Self:
max_length=9, # # Only validate if both dates are present
pattern=r"^[A-Z0-9]{9}$", # if self.issue_date is not None and self.expiry_date is not None:
description="The passport number, exactly 9 alphanumeric uppercase characters." # if self.issue_date >= self.expiry_date:
) # # Raise error only if both dates are present and invalid
# raise ValueError(f'Expiry date ({self.expiry_date}) must be after issue date ({self.issue_date})')
surname: str = Field( # return self
...,
min_length=1,
description="The surname (family name) of the passport holder."
)
given_names: str = Field(
...,
min_length=1,
description="The given names (first and middle names) of the passport holder."
)
@model_validator(mode='after')
def check_expiry_date_after_issue_date(self) -> Self:
if self.issue_date >= self.expiry_date:
raise ValueError(f'Expiry date is not after issue date')
return self
birth_date: date = Field(
...,
description="Date of birth of the passport holder in ISO format (YYYY-MM-DD)."
)
citizenship: str = Field(
...,
min_length=2,
description="The nationality or citizenship of the passport holder, preferably as a country name or ISO code."
)
sex: Literal["M", "F"] = Field(
...,
description="Sex of the passport holder: 'M' for male, 'F' for female."
)
issue_date: date = Field(
...,
description="Date when the passport was issued, in ISO format (YYYY-MM-DD)."
)
expiry_date: date = Field(
...,
description="Date when the passport expires, in ISO format (YYYY-MM-DD)."
)

View File

@ -1,65 +1,67 @@
from datetime import date from typing import Optional # Keep Optional, remove others
from typing import List, Literal, Optional from pydantic import BaseModel, ConfigDict # Removed Field
from pydantic import BaseModel, ConfigDict, EmailStr, Field
class FromProfile(BaseModel): class FromProfile(BaseModel):
""" """
Fields which can be extracted from description.txt Fields which can be extracted from description.txt - All fields optional and simplified to string where possible.
""" """
model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) # Keep config if needed
first_name: str = Field(..., min_length=1) first_name: Optional[str] = None
last_name: str = Field(..., min_length=1) last_name: Optional[str] = None
date_of_birth: date date_of_birth: Optional[str] = None # Simplified from date
nationality: str nationality: Optional[str] = None
country_of_domicile: str country_of_domicile: Optional[str] = None
gender: Literal["Female", "Male"] gender: Optional[str] = None # Simplified from Literal
# ID information # ID information
passport_number: str = Field(..., min_length=9, max_length=9, pattern=r"^[A-Z0-9]{9}$") passport_number: Optional[str] = None # Simplified, removed Field constraints
id_type: Literal["passport"] id_type: Optional[str] = None # Simplified from Literal
id_issue_date: date id_issue_date: Optional[str] = None # Simplified from date
id_expiry_date: date id_expiry_date: Optional[str] = None # Simplified from date
# Contact # Contact
phone: str = Field(..., min_length=8) phone: Optional[str] = None # Simplified, removed Field constraints
email: str = Field(min_length=5) email: Optional[str] = None # Simplified, removed Field constraints
address: str address: Optional[str] = None
# Personal info # Personal info
politically_exposed_person: bool politically_exposed_person: Optional[str] = None # Simplified from bool
marital_status: Literal["Single", "Married", "Divorced", "Widowed"] marital_status: Optional[str] = None # Simplified from Literal
highest_education: Literal["Tertiary", "Secondary", "Primary", "None"] highest_education: Optional[str] = None # Simplified from Literal
education_history: Optional[str] = None education_history: Optional[str] = None
# Employment # Employment
employment_status: Literal["Employee", "Self-Employed", "Unemployed", "Retired", "Student", "Diplomat", "Military", "Homemaker", "Other"] employment_status: Optional[str] = None # Simplified from Literal
employment_since: Optional[int] = None employment_since: Optional[str] = None # Simplified from int
employer: Optional[str] = None employer: Optional[str] = None
position: Optional[str] = None position: Optional[str] = None
annual_salary_eur: Optional[float] = None annual_salary_eur: Optional[str] = None # Simplified from float
# Wealth background # Wealth background
total_wealth_range: Literal["<1.5m", "1.5m-5m", "5m-10m", "10m-20m", "20m-50m", ">50m"] total_wealth_range: Optional[str] = None # Simplified from Literal
origin_of_wealth: List[Literal["Employment", "Inheritance", "Business", "Investments", "Sale of real estate", "Retirement package", "Other"]] # List types are often handled differently; simplifying to a single string might lose info.
# Keeping as Optional[str] based on request, but consider if Optional[List[str]] = None is better long-term.
origin_of_wealth: Optional[str] = None # Simplified from List[Literal]
inheritance_details: Optional[str] = None inheritance_details: Optional[str] = None
# Assets # Assets
business_assets_eur: float = Field(..., ge=0) business_assets_eur: Optional[str] = None # Simplified from float, removed Field constraint
# Income # Income
estimated_annual_income: Literal["<250k", "250k-500k", "500k-1m", ">1m"] estimated_annual_income: Optional[str] = None # Was already Optional[str]
income_country: str income_country: Optional[str] = None
# Account preferences # Account preferences
commercial_account: bool commercial_account: Optional[str] = None # Simplified from bool
investment_risk_profile: Literal["Low", "Moderate", "Considerable", "High"] investment_risk_profile: Optional[str] = None # Simplified from Literal
mandate_type: Literal["Advisory", "Discretionary"] mandate_type: Optional[str] = None # Simplified from Literal
investment_experience: Literal["Inexperienced", "Experienced", "Expert"] investment_experience: Optional[str] = None # Simplified from Literal
investment_horizon: Literal["Short", "Medium", "Long-Term"] investment_horizon: Optional[str] = None # Simplified from Literal
preferred_markets: List[str] # Keeping as Optional[str] based on request, but consider if Optional[List[str]] = None is better long-term.
preferred_markets: Optional[str] = None # Simplified from List[str]
# Assets under management # Assets under management
total_aum: float total_aum: Optional[str] = None # Simplified from float
aum_to_transfer: float aum_to_transfer: Optional[str] = None # Simplified from float