Source code for q2_sdk.models.demographic

from __future__ import annotations
from dataclasses import dataclass
import re

from dateutil import parser
from q2_sdk.models.recursive_encoder import JsonSerializable

EMAIL_REGEX = re.compile(r"[^@]+@[^\.]+\..+")


[docs] class PhoneType: """Enum for Q2 Phone Types""" PERSONAL = "Home" BUSINESS = "Business" CELL = "Mobile" OTHER = "Other"
[docs] class Phone(JsonSerializable): """Standard Phone class so developers don't all roll their own""" def __init__( self, area_code: str, phone_number: str, phone_type: PhoneType, extension="", country: str | None = None, additional_details: dict | None = None, is_sac_target: bool = True, is_sac_sms_target: bool = True, full_phone_number="", ): if extension is None: extension = "" self.area_code = area_code self.phone_number = phone_number self.type = phone_type self.extension = extension.strip() self.country = "USA" if not country else country if not additional_details: additional_details = {} self.additional_details = ( additional_details # To store additional demo info that may not be standard ) self.is_sac_target = is_sac_target self.is_sac_sms_target = is_sac_sms_target self.full_phone_number = full_phone_number
[docs] @staticmethod def build_from_str(phone_str: str, phone_type: PhoneType): """Takes a string of numbers in several formats and returns a Phone instance """ if not phone_str: return None extension = "" if "x" in phone_str: phone_str, extension = phone_str.split("x") phone_num = "".join([x for x in phone_str if x.isdigit()]) return Phone(phone_num[:3], phone_num[3:], phone_type, extension=extension)
def __eq__(self, other): return vars(self) == vars(other) def __repr__(self): return "({}) {}-{} {}({})".format( self.area_code, self.phone_number[:3], self.phone_number[3:], "x{} ".format(self.extension) if self.extension else "", self.type, )
[docs] class AddressType: """Enum for Q2 Address Types""" RESIDENTIAL = "Residential" POSTAL = "Postal" VACATION = "Vacation" HOME = "Home" OTHER = "Other" BUSINESS = "Business" BILLPAYEE = "BillPayee"
[docs] class Address(JsonSerializable): """Standard Address class so developers don't all roll their own""" def __init__( self, address_1: str, address_2: str, city: str, state: str, zipcode: str, address_type: AddressType = AddressType.HOME, province="", country: str | None = None, additional_details: dict | None = None, address_id: int | None = None, ): self.address_1 = address_1 self.address_2 = address_2 self.city = city self.state = state self.zipcode = zipcode self.address_type = address_type self.country = "USA" if not country else country self.province = province if not additional_details: additional_details = {} self.additional_details = ( additional_details # To store additional demo info that may not be standard ) self.address_id = address_id def __eq__(self, other): return vars(self) == vars(other) def __repr__(self): return str(vars(self))
[docs] @dataclass class Address3: """ Parses city, state, zipcode, and country from address3 line such as "Austin, TX 94730 USA" """ city: str state: str zipcode: str country: str
[docs] @staticmethod def from_str(inp: str) -> Address3: city, remaining = inp.split(",") state, zipcode, country = remaining.strip().split(" ", 2) return Address3(city, state, zipcode, country)
[docs] class DriverLicense(JsonSerializable): """Standard DriverLicense class so developers don't all roll their own""" def __init__(self, dl_number: str, state: str): if dl_number: dl_number = "".join(x for x in dl_number if x.isdigit() or x.isalpha()) self.dl_number = dl_number self.state = state def __eq__(self, other): if isinstance(other, DriverLicense): return vars(self) == vars(other) return False def __repr__(self): return str(vars(self))
[docs] class DemographicInfo(JsonSerializable): """ Smart little class that takes most of the pain out of 'What information do we know about a user?' Does some data cleansing on input ensuring consumers will all work together. """ def __init__( self, date_of_birth: str | None, list_of_emails: list[str] | list[Email], list_of_phones: list[Phone], list_of_addresses: list[Address], first_name: str, last_name: str, ssn: str, mothers_maiden_name="", middle_name: str = "", title="", driver_license: DriverLicense | None = None, user_info=None, primary_cif=None, additional_details: dict | None = None, user_role_id: int | None = None, auth_token_serial: str | None = None, is_admin: bool = False, email_is_sac_target: bool = None, ): if date_of_birth: try: date_of_birth = parser.parse(date_of_birth).strftime("%m-%d-%Y") except ValueError: date_of_birth = None self.dob = date_of_birth self.email_objects: list[Email] = [] self.emails: list[str] = self._unpack_emails(list_of_emails) self.phones: list[Phone] = self._get_uniques(list_of_phones) self.addresses: list[Address] = self._get_uniques(list_of_addresses) self.first_name = first_name self.last_name = last_name self.middle_name = middle_name self.title = title self.mothers_maiden_name = mothers_maiden_name if ssn: ssn = "".join(x for x in ssn if x.isdigit()) self.social_security_number = ssn self.driver_license = driver_license self.user_info = user_info self.primary_cif = primary_cif if not additional_details: additional_details = {} self.additional_details = ( additional_details # To store additional demo info that may not be standard ) # self.user_role_id is used for commercial or treasury users to define the user's role (role must exist # before user is created) self.user_role_id = user_role_id # self.is_admin used for commercial and/or treasury users to indicate the admin user. This will be used to # enable predefined user properties that apply to a commercial/treasury admin user. See dbo.apispAddUser stored # proc for more details self.is_admin = is_admin # email_is_sac_target must default to None to maintain backwards compatibility self.email_is_sac_target = email_is_sac_target # refers to Verisign token (or other vendor). Some FIs have the option to enter a Verisgn token instead # of the Q2 generated TAC. self.auth_token_serial = auth_token_serial @staticmethod def _get_uniques(full_list: list): unique_list = [] for item in full_list: if item and item not in unique_list: unique_list.append(item) return unique_list def __eq__(self, other): return vars(self) == vars(other) def __repr__(self): return str(vars(self)) def _unpack_emails(self, email_list: list[str] | list[Email]): string_emails = set() unique_object = [] for obj in email_list: if isinstance(obj, str): string_emails.add(obj) if isinstance(obj, Email) and obj.is_valid_format(): if ( obj.email_address not in unique_object and obj.email_address not in string_emails ): self.email_objects.append(obj) unique_object.append(obj.email_address) string_emails.add(obj.email_address) return self._validate_email_format(list(string_emails)) @staticmethod def _validate_email_format(email_list: list): return [x for x in email_list if EMAIL_REGEX.match(x)]
[docs] class Email(JsonSerializable): def __init__(self, email_address: str, is_sac_target: bool = True) -> None: self.email_address: str = email_address self.is_sac_target: bool = is_sac_target super().__init__()
[docs] def is_valid_format(self): is_valid = True if not EMAIL_REGEX.match(self.email_address): is_valid = False return is_valid
def __repr__(self): return ( f"<Email address={self.email_address} is_sac_target={self.is_sac_target} />" )