import datetime from dateutil.relativedelta import relativedelta from pydantic import ( BaseModel, computed_field, Field, ValidationInfo, model_validator, ConfigDict ) import pandas as pd class UKPassportSchema(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) full_name: str | None = Field( default=None, description="Applicant's full name. Must consist of at least 2 words, have length gte 2 & lte 61", examples=["Jodie Pippa"], ) # , min_length=2, max_length=61) expiry_date: datetime.date | None = Field( default=None, description="The passport's expiry date in YYYY-MM-DD format", examples=["2028-06-01"], ) full_name_err_msgs: str | None = None expiry_date_err_msgs: str | None = None validation_policy_status_df: pd.DataFrame = pd.DataFrame( columns=["Policy", "Value", "Status", "Message"]) @model_validator(mode="after") def validate_expiry_date(cls, values): try: err_msgs = list() expiry_date_val = values.expiry_date if not expiry_date_val: err_msgs.append("Expiry date must be present") values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Expiry date must be present", values.expiry_date, False, "Expiry date is not present"] else: values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Expiry date must be present", values.expiry_date, True, "Expiry date is present"] if expiry_date_val < datetime.date.today() + relativedelta(years=1): # raise ValueError("Provided passport expires within 1 year") err_msgs.append("Provided passport expires within 1 year") values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Provided passport expiry should be more than 1 year", values.expiry_date, False, "Provided passport expires within 1 year &/or is expired", ] else: values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Provided passport expiry should be more than 1 year", values.expiry_date, True, "Provided passport does not expire within 1 year", ] values.expiry_date_err_msgs = ", ".join( err_msgs) if err_msgs else None return values except Exception as e: raise # if not values.expiry_date_err_msgs: # values.expiry_date_err_msgs = "Provided passport expires within 1 year" # else: # values.expiry_date_err_msgs = f"{values.expiry_date_err_msgs}, Provided passport expires within 1 year" # if not values.expiry_date_err_msgs: # values.expiry_date_err_msgs = None # return values @model_validator(mode="after") def validate_full_name(cls, values, info: ValidationInfo): """Match applicant's full name against provided name (case-insensitive)""" try: err_msgs = [] expected = ( info.context.get("application_summary_full_name") if info.context else None ) full_name_val = values.full_name if not full_name_val: err_msgs.append("Applicant's full name not present") values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Applicant's full name should be present", full_name_val, False, "Applicant's full name not present"] else: values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Applicant's full name should be present", full_name_val, True, "Applicant's full name is present"] full_name_val_len = 0 if full_name_val: full_name_val_len = len(full_name_val) if not full_name_val and not ( full_name_val_len >= 2 and full_name_val_len <= 61 ): err_msgs.append( "Full name must have a length of at least 2 & at most 61" ) values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = [ "Full name must have a length of at least 2 & at most 61", full_name_val_len, False, "Full name does not have a length of at least 2 & at most 61"] else: values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = [ "Full name must have a length of at least 2 & at most 61", full_name_val_len, True, "Full name has a length of at least 2 & at most 61"] if ( not expected or not full_name_val or full_name_val.lower() != expected.lower() ): err_msgs.append("Name mismatch with provided value") values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Name should match with provided value", full_name_val, False, "Name does not match with provided value"] else: values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Name should match with provided value", full_name_val, True, "Name matches with provided value"] if not full_name_val or len(full_name_val.strip().split(" ")) < 2: err_msgs.append( "Full name must consist of at least 2 words (first name + last name)" ) values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Full name must consist of at least 2 words (first name + last name)", len(full_name_val.strip().split(" ")), False, "Full name does not consist of at least 2 words (first name + last name)"] else: values.validation_policy_status_df.loc[len( values.validation_policy_status_df)] = ["Full name must consist of at least 2 words (first name + last name)", len(full_name_val.strip().split(" ")), True, "Full name does consist of at least 2 words (first name + last name)"] if err_msgs: values.full_name_err_msgs = ", ".join(err_msgs) else: values.full_name_err_msgs = None return values except Exception as e: # logger.exception(e, exc_info=True) # return None raise @computed_field @property def is_red_flagged(self) -> bool: if self.full_name_err_msgs or self.expiry_date_err_msgs: return True return False class UKDrivingLicense(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) full_name: str | None = Field( default=None, description="Applicant's full name. Must consist of at least 2 words, have length gte 2 & lte 61", examples=["Jodie Pippa"], ) # , min_length=2, max_length=61) full_name_err_msgs: str | None = None expiry_date_err_msgs: str | None = None validation_policy_status_df: pd.DataFrame = pd.DataFrame( columns=["Policy", "Value", "Status", "Message"]) @model_validator(mode="after") def validate_full_name(cls, values, info: ValidationInfo): """Match applicant's full name against provided name (case-insensitive)""" try: err_msgs = [] expected = ( info.context.get("application_summary_full_name") if info.context else None ) full_name_val = values.full_name if not full_name_val: err_msgs.append("Applicant's full name not present") values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Applicant's full name should be present", full_name_val, False, "Applicant's full name not present", ] else: values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Applicant's full name should be present", full_name_val, True, "Applicant's full name is present", ] full_name_val_len = 0 if full_name_val: full_name_val_len = len(full_name_val) if not full_name_val and not ( full_name_val_len >= 2 and full_name_val_len <= 61 ): err_msgs.append( "Full name must have a length of at least 2 & at most 61" ) values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Full name must have a length of at least 2 & at most 61", full_name_val_len, False, "Full name does not have a length of at least 2 & at most 61", ] else: values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Full name must have a length of at least 2 & at most 61", full_name_val_len, True, "Full name has a length of at least 2 & at most 61", ] if ( not expected or not full_name_val or full_name_val.lower() != expected.lower() ): err_msgs.append("Name mismatch with provided value") values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Name should match with provided value", full_name_val, False, "Name does not match with provided value", ] else: values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Name should match with provided value", full_name_val, True, "Name matches with provided value", ] if not full_name_val or len(full_name_val.strip().split(" ")) < 2: err_msgs.append( "Full name must consist of at least 2 words (first name + last name)" ) values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Full name must consist of at least 2 words (first name + last name)", len(full_name_val.strip().split(" ")), False, "Full name does not consist of at least 2 words (first name + last name)", ] else: values.validation_policy_status_df.loc[ len(values.validation_policy_status_df) ] = [ "Full name must consist of at least 2 words (first name + last name)", len(full_name_val.strip().split(" ")), True, "Full name does consist of at least 2 words (first name + last name)", ] if err_msgs: values.full_name_err_msgs = ", ".join(err_msgs) else: values.full_name_err_msgs = None return values except Exception as e: # logger.exception(e, exc_info=True) # return None raise @computed_field @property def is_red_flagged(self) -> bool: if self.full_name_err_msgs or self.expiry_date_err_msgs: return True return False