From 54e794d670dd7600b839f8d00a4050728d2d037d Mon Sep 17 00:00:00 2001 From: NoeBerdoz Date: Sun, 13 Apr 2025 08:45:47 +0200 Subject: [PATCH] DEVALIDATION OF VALIDATION --- dto/responses.py | 2 +- validation/from_account.py | 69 +++++++++++++++++------------- validation/from_description.py | 45 ++++++++++---------- validation/from_passport.py | 75 ++++++++------------------------- validation/from_profile.py | 76 +++++++++++++++++----------------- 5 files changed, 120 insertions(+), 147 deletions(-) diff --git a/dto/responses.py b/dto/responses.py index 78dfe7f..e07e5be 100644 --- a/dto/responses.py +++ b/dto/responses.py @@ -38,4 +38,4 @@ class GameDecisionResponseWithBotDecisionDTO(BaseModel): client_id: Optional[UUID] = None client_data: Optional[Dict[str, Any]] = None bot_decision: Literal["Accept", "Reject"] - bot_reason: str \ No newline at end of file + bot_reason: Optional[str] \ No newline at end of file diff --git a/validation/from_account.py b/validation/from_account.py index ac057d3..c459795 100644 --- a/validation/from_account.py +++ b/validation/from_account.py @@ -1,43 +1,56 @@ -from typing import Literal, Optional, Self -from pydantic import BaseModel, ConfigDict, EmailStr, Field, model_validator -from pydantic_extra_types.phone_numbers import PhoneNumber +from typing import Optional # Removed Literal, Self +from pydantic import BaseModel, ConfigDict # Removed Field, model_validator +# Removed PhoneNumber import as it's not used and types are simplified 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) # Details of the Account and Client - account_name: str = Field(min_length=1) - account_holder_name: str = Field(min_length=1) - account_holder_surname: str = Field(min_length=1) + account_name: Optional[str] = None + account_holder_name: Optional[str] = None + account_holder_surname: Optional[str] = None - @model_validator(mode='after') - def check_account_name_is_name_surname(self) -> Self: - 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 + # --- Updated Validator 1 --- + # @model_validator(mode='after') + # def check_account_name_is_name_surname(self) -> Self: + # # Only validate if all relevant fields are present + # if self.account_holder_name is not None and \ + # 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 # Delivery of Communication - building_number: str = Field(min_length=1) - street_name: str = Field(min_length=1) - postal_code: str = Field(min_length=1) - city: str = Field(min_length=1) - country: str = Field(min_length=1) + building_number: Optional[str] = None + street_name: Optional[str] = None + postal_code: Optional[str] = None + city: Optional[str] = None + country: Optional[str] = None # Application for e-banking - ebanking_name: str = Field(min_length=1) - @model_validator(mode='after') - def check_account_name_ebanking_name(self) -> Self: - if self.ebanking_name != self.account_name: - raise ValueError(f'Ebanking name is different from account name') - return self - phone_number: str = Field(..., min_length=8) - email: str = Field(min_length=5) \ No newline at end of file + ebanking_name: Optional[str] = None + + # --- Updated Validator 2 --- + # @model_validator(mode='after') + # def check_account_name_ebanking_name(self) -> Self: + # # Only validate if both fields are present + # if self.ebanking_name is not None and self.account_name is not None: + # 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 \ No newline at end of file diff --git a/validation/from_description.py b/validation/from_description.py index fab40b8..39105f6 100644 --- a/validation/from_description.py +++ b/validation/from_description.py @@ -1,35 +1,32 @@ -from typing import Literal -from pydantic import BaseModel, ConfigDict, Field +from typing import Optional # Removed Literal +from pydantic import BaseModel, ConfigDict # Removed Field 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) - - full_name: str = Field(..., min_length=1) - age: int = Field(..., ge=0, le=120) - nationality: str = Field(..., min_length=1) + full_name: Optional[str] = None + age: Optional[str] = None # Simplified from int + nationality: Optional[str] = None + marital_status: Optional[str] = None # Simplified from Literal + has_children: Optional[str] = None # Simplified from bool - marital_status: Literal["single", "married", "divorced", "widowed"] - has_children: bool + secondary_education_school: Optional[str] = None + 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 - secondary_education_year: int = Field(..., ge=1900, le=2100) - university_name: str - university_graduation_year: int = Field(..., ge=1900, le=2100) + occupation_title: Optional[str] = None + employer: Optional[str] = None + start_year: Optional[str] = None # Simplified from int + annual_salary_eur: Optional[str] = None # Simplified from float - occupation_title: str - employer: str - 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 + total_savings_eur: Optional[str] = None # Simplified from float + has_properties: Optional[str] = None # Simplified from bool + inheritance_amount_eur: Optional[str] = None # Simplified from float + inheritance_year: Optional[str] = None # Simplified from int + inheritance_source: Optional[str] = None \ No newline at end of file diff --git a/validation/from_passport.py b/validation/from_passport.py index dbdb1ac..6a59c3e 100644 --- a/validation/from_passport.py +++ b/validation/from_passport.py @@ -10,61 +10,22 @@ class FromPassport(BaseModel): """ model_config = ConfigDict(validate_assignment=True, str_strip_whitespace=True) - country: str = Field( - ..., - min_length=3, - max_length=3, - description="The country issuing the passport, as a 3-letter ISO 3166-1 alpha-3 code (e.g., 'CHE' for Switzerland)." - ) + country: Optional[str] = None + passport_number: Optional[str] = None + surname: Optional[str] = None + given_names: Optional[str] = None + 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( - ..., - min_length=9, - max_length=9, - pattern=r"^[A-Z0-9]{9}$", - description="The passport number, exactly 9 alphanumeric uppercase characters." - ) - - surname: str = Field( - ..., - 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)." - ) \ No newline at end of file + # --- Updated Validator --- + # @model_validator(mode='after') + # def check_expiry_date_after_issue_date(self) -> Self: + # # Only validate if both dates are present + # if self.issue_date is not None and self.expiry_date is not None: + # 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})') + # return self \ No newline at end of file diff --git a/validation/from_profile.py b/validation/from_profile.py index 89b617f..35bd130 100644 --- a/validation/from_profile.py +++ b/validation/from_profile.py @@ -1,65 +1,67 @@ -from datetime import date -from typing import List, Literal, Optional -from pydantic import BaseModel, ConfigDict, EmailStr, Field +from typing import Optional # Keep Optional, remove others +from pydantic import BaseModel, ConfigDict # Removed Field 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) - last_name: str = Field(..., min_length=1) - date_of_birth: date - nationality: str - country_of_domicile: str - gender: Literal["Female", "Male"] + first_name: Optional[str] = None + last_name: Optional[str] = None + date_of_birth: Optional[str] = None # Simplified from date + nationality: Optional[str] = None + country_of_domicile: Optional[str] = None + gender: Optional[str] = None # Simplified from Literal # ID information - passport_number: str = Field(..., min_length=9, max_length=9, pattern=r"^[A-Z0-9]{9}$") - id_type: Literal["passport"] - id_issue_date: date - id_expiry_date: date + passport_number: Optional[str] = None # Simplified, removed Field constraints + id_type: Optional[str] = None # Simplified from Literal + id_issue_date: Optional[str] = None # Simplified from date + id_expiry_date: Optional[str] = None # Simplified from date # Contact - phone: str = Field(..., min_length=8) - email: str = Field(min_length=5) - address: str + phone: Optional[str] = None # Simplified, removed Field constraints + email: Optional[str] = None # Simplified, removed Field constraints + address: Optional[str] = None # Personal info - politically_exposed_person: bool - marital_status: Literal["Single", "Married", "Divorced", "Widowed"] - highest_education: Literal["Tertiary", "Secondary", "Primary", "None"] + politically_exposed_person: Optional[str] = None # Simplified from bool + marital_status: Optional[str] = None # Simplified from Literal + highest_education: Optional[str] = None # Simplified from Literal education_history: Optional[str] = None # Employment - employment_status: Literal["Employee", "Self-Employed", "Unemployed", "Retired", "Student", "Diplomat", "Military", "Homemaker", "Other"] - employment_since: Optional[int] = None + employment_status: Optional[str] = None # Simplified from Literal + employment_since: Optional[str] = None # Simplified from int employer: Optional[str] = None position: Optional[str] = None - annual_salary_eur: Optional[float] = None + annual_salary_eur: Optional[str] = None # Simplified from float # Wealth background - total_wealth_range: Literal["<1.5m", "1.5m-5m", "5m-10m", "10m-20m", "20m-50m", ">50m"] - origin_of_wealth: List[Literal["Employment", "Inheritance", "Business", "Investments", "Sale of real estate", "Retirement package", "Other"]] + total_wealth_range: Optional[str] = None # Simplified from Literal + # 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 # Assets - business_assets_eur: float = Field(..., ge=0) + business_assets_eur: Optional[str] = None # Simplified from float, removed Field constraint # Income - estimated_annual_income: Literal["<250k", "250k-500k", "500k-1m", ">1m"] - income_country: str + estimated_annual_income: Optional[str] = None # Was already Optional[str] + income_country: Optional[str] = None # Account preferences - commercial_account: bool - investment_risk_profile: Literal["Low", "Moderate", "Considerable", "High"] - mandate_type: Literal["Advisory", "Discretionary"] - investment_experience: Literal["Inexperienced", "Experienced", "Expert"] - investment_horizon: Literal["Short", "Medium", "Long-Term"] - preferred_markets: List[str] + commercial_account: Optional[str] = None # Simplified from bool + investment_risk_profile: Optional[str] = None # Simplified from Literal + mandate_type: Optional[str] = None # Simplified from Literal + investment_experience: Optional[str] = None # Simplified from Literal + investment_horizon: Optional[str] = None # Simplified from Literal + # 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 - total_aum: float - aum_to_transfer: float \ No newline at end of file + total_aum: Optional[str] = None # Simplified from float + aum_to_transfer: Optional[str] = None # Simplified from float \ No newline at end of file