import logging
from hashlib import md5
from typing import Optional
from uuid import UUID
from dateutil import parser
from lxml import etree
from q2_sdk.models.demographic import Address, DemographicInfo, Phone, PhoneType
from .xml_helper import find_with_default
[docs]
class OnlineUser:
"""
Object representation of the User information that comes in on a
Q2 Online request
"""
def __init__(
self,
request_xml: Optional[etree.Element] = None,
*,
customer_key: Optional[str] = None,
):
"""
:param request_xml: XML node from Q2 Online request
"""
self.logger = logging.getLogger("q2_sdk.general")
self.user_id: Optional[int] = None
self.user_guid: Optional[str] = None
"""A consistent ID for this user guaranteed to be unique across databases. Useful for multitenant extensions"""
self.customer_id: Optional[int] = None
self.group_id: Optional[int] = None
self.group_desc: Optional[str] = None
self.login_name: Optional[str] = None
self.customer_name: Optional[str] = None
self.first_name: Optional[str] = None
self.middle_name: Optional[str] = None
self.last_name: Optional[str] = None
self.ssn: Optional[str] = None
self.email_address: Optional[str] = None
self.city: Optional[str] = None
self.state: Optional[str] = None
self.language: Optional[str] = None
self.postal_code: Optional[str] = None
self.home_phone: Optional[str] = None
self.mobile_phone: Optional[str] = None
self.work_phone: Optional[str] = None
self.hq_session_id: Optional[str] = None
self.user_logon_id: Optional[int] = None
self.customer_primary_cif: Optional[str] = None
self.user_primary_cif: Optional[str] = None
self.address1: Optional[str] = None
self.address2: Optional[str] = None
self.demographic_info: Optional[DemographicInfo] = None
self.user_role_id: Optional[int] = None
"""This value is not present in the incoming request from HQ so it has to be
set manually if needed. You can use the get_user_role method provided in online form extensions"""
self.is_company: Optional[bool] = None
"""This value is not present in the incoming request from HQ so it has to be
set manually if needed. It can be used to provide core mappers additional
context for making calls."""
self.is_treasury: Optional[bool] = None
"""This value is not present in the incoming request from HQ so it has to be
set manually if needed."""
self.is_commercial: Optional[bool] = None
"""This value is not present in the incoming request from HQ so it has to be
set manually if needed."""
self.dob: Optional[str] = None
"""This value is not present in the incoming request from HQ so it has to be
set manually if needed. You can use the get_user_dob method provided in online form extensions"""
self.zone_id: Optional[str] = None
if request_xml is not None:
self._hydrate_from_xml(request_xml)
if customer_key is not None:
self.user_guid = self._get_user_guid(customer_key)
def _get_user_guid(self, customer_key: str) -> str:
"""One way hash of this user's id at this institution"""
user_hash = md5()
user_hash.update(customer_key.encode("utf-8"))
user_hash.update(str(self.user_id).encode("utf-8"))
user_guid = str(UUID(bytes=user_hash.digest(), version=4)).upper()
return user_guid
def _hydrate_from_xml(self, request_xml: etree.Element):
form = request_xml.find(".//Form")
self.user_id = find_with_default(form, "UserID", data_type=int)
self.user_role_id = find_with_default(
form, "UserRoleID", default=None, data_type=int
)
self.customer_id = find_with_default(form, "CustomerID", data_type=int)
self.group_id = find_with_default(form, "GroupID", data_type=int)
self.group_desc = find_with_default(form, "GroupDesc")
self.login_name = find_with_default(form, "LoginName", data_type=str)
self.customer_name = find_with_default(
form, "CustomerName", default="", data_type=str
)
self.first_name = find_with_default(
form, "FirstName", default="", data_type=str
)
self.middle_name = find_with_default(
form, "MiddleName", default="", data_type=str
)
self.last_name = find_with_default(form, "LastName", default="", data_type=str)
ssn = find_with_default(form, "SSN", default="", data_type=str)
if ssn:
ssn = "".join(x for x in ssn if x.isdigit())
self.ssn = ssn
self.email_address = find_with_default(form, "EmailAddress", data_type=str)
self.city = find_with_default(form, "City", data_type=str)
self.state = find_with_default(form, "State", data_type=str)
self.language = find_with_default(form, "LanguageShortName")
self.postal_code = find_with_default(
form, "PostalCode", default="", data_type=str
)
self.home_phone = find_with_default(
form, "HomePhone", default="", data_type=str
)
self.mobile_phone = find_with_default(
form, "MobilePhone", default="", data_type=str
)
self.work_phone = find_with_default(
form, "WorkPhone", default="", data_type=str
)
self.hq_session_id = find_with_default(form, "SessionId")
self.user_logon_id = find_with_default(form, "UserLogonID", data_type=int)
self.customer_primary_cif = find_with_default(
form, "CustomerPrimaryCIF", data_type=str
)
self.user_primary_cif = find_with_default(form, "UserPrimaryCIF", data_type=str)
self.address1 = find_with_default(
form, "StreetAddress1", default="", data_type=str
)
self.address2 = find_with_default(
form, "StreetAddress2", default="", data_type=str
)
self.is_company = find_with_default(
form, "IsCompany", default=None, data_type=bool
)
self.is_treasury = find_with_default(
form, "IsTreasury", default=None, data_type=bool
)
self.is_commercial = find_with_default(
form, "IsCommercial", default=None, data_type=bool
)
self.dob = find_with_default(form, "DOB", default=None, data_type=str)
if self.dob:
self.dob = parser.parse(self.dob).strftime("%m-%d-%Y")
self._demographic_info: Optional[DemographicInfo] = None
if hasattr(request_xml, "xmlDoc"):
request_attrs = request_xml.xmlDoc.getchildren()[0]
self.zone_id = request_attrs.get("ZoneId")
@property
def demographic_info(self) -> DemographicInfo:
self.logger.warning(
"OnlineUser.demographic_info has been deprecated. Please use .as_demographic_info() instead."
)
return self.as_demographic_info()
@demographic_info.setter
def demographic_info(self, value: DemographicInfo):
self._demographic_info = value
def as_demographic_info(self) -> DemographicInfo:
if not self._demographic_info:
phone_list = []
if self.home_phone:
phone_list.append(
Phone.build_from_str(self.home_phone, PhoneType.PERSONAL)
)
if self.work_phone:
phone_list.append(
Phone.build_from_str(self.work_phone, PhoneType.BUSINESS)
)
if self.mobile_phone:
phone_list.append(
Phone.build_from_str(self.mobile_phone, PhoneType.CELL)
)
self._demographic_info = DemographicInfo(
self.dob if self.dob else "",
[self.email_address],
phone_list,
[
Address(
self.address1,
self.address2,
self.city,
self.state,
self.postal_code,
)
],
self.first_name,
self.last_name,
self.ssn,
middle_name=self.middle_name,
primary_cif=self.user_primary_cif,
user_role_id=self.user_role_id,
)
return self._demographic_info