from __future__ import annotations import datetime from typing import Dict, List, Optional, Set, Union from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator from app.db_sql import redis_lookup_id_random, sql_insert, sql_insert_or_update, sql_select, sql_update from app.lib_general import log, logging # from app.methods.address_methods import load_address_obj from app.methods.contact_methods import create_contact_obj, create_update_contact_obj, load_contact_obj, update_contact_obj from app.methods.order_methods import load_order_obj, get_order_rec_list from app.methods.organization_methods import create_update_organization_obj, load_organization_obj, update_organization_obj # from app.methods.user_methods import create_user_obj, load_user_obj, update_user_obj from app.models.common_field_schema import default_num_bytes from app.models.person_models import Person_Base # ### BEGIN ### API Person Methods ### load_person_obj() ### def load_person_obj( person_id: int|str, limit: int = 1000, by_alias: bool = True, exclude_unset: bool = True, model_as_dict: bool = False, enabled: str = 'enabled', # enabled, disabled, all inc_address: bool = False, # Under contact inc_contact: bool = False, inc_event_list: bool = False, inc_journal_list: bool = False, inc_journal_entry_list: bool = False, inc_membership_cfg: bool = False, inc_membership_group: bool = False, inc_membership_person_group: bool = False, inc_membership_group_list: bool = False, inc_membership_group_person_list: bool = False, inc_membership_person: bool = False, # NOTE: Same as inc_membership_person_list inc_membership_person_list: bool = False, # NOTE: Same as inc_membership_person inc_membership_person_profile: bool = False, inc_membership_person_profile_cust: bool = False, inc_membership_type: bool = False, inc_membership_type_person: bool = False, inc_membership_type_list: bool = False, inc_membership_type_person_list: bool = False, inc_order_cfg: bool = False, inc_order_line_list: bool = False, inc_order_list: bool = False, inc_order_cart_list: bool = False, inc_organization: bool = False, inc_post_list: bool = False, inc_post_comment_list: bool = False, inc_product: bool = False, inc_user: bool = False, inc_user_role_list: bool = False, ) -> Person_Base|dict|bool: log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if person_id := redis_lookup_id_random(record_id_random=person_id, table_name='person'): pass else: return False if person_rec := sql_select(table_name='v_person', record_id=person_id): pass else: return False log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(person_rec) try: person_obj = Person_Base(**person_rec) log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(person_obj) except ValidationError as e: log.error(e.json()) return False # Updated 2021-07-09 if inc_contact: log.info('Need to include contact data...') contact_id = person_rec.get('contact_id', None) log.debug(contact_id) if contact_result := load_contact_obj( contact_id = contact_id, limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, enabled = enabled, inc_address = inc_address, ): person_obj.contact = contact_result else: person_obj.contact = None # Updated 2021-07-09 # if inc_membership_group_list: # from app.methods.membership_group_methods import get_membership_group_rec_list, load_membership_group_obj # if membership_group_rec_list_result := get_membership_group_rec_list( # for_obj_type = 'person', # for_obj_id = person_id, # limit = limit, # enabled = enabled, # ): # membership_group_result_list = [] # for membership_group_rec in membership_group_rec_list_result: # if membership_group_result := load_membership_group_obj( # membership_group_id = membership_group_rec.get('membership_group_id', None), # limit = limit, # by_alias = by_alias, # exclude_unset = exclude_unset, # model_as_dict = model_as_dict, # enabled = enabled, # inc_membership_group_profile = inc_membership_group_profile, # inc_membership_group_profile_cust = inc_membership_group_profile_cust, # inc_membership_type = inc_membership_type, # inc_product = inc_product, # ): # membership_group_result_list.append(membership_group_result) # else: # membership_group_result_list.append(None) # person_obj.membership_group_list = membership_group_result_list # else: person_obj.membership_group_list = [] # Updated 2021-07-09 if inc_membership_person: log.info('Need to include membership person data...') from app.methods.membership_person_methods import load_membership_person_obj membership_person_id = person_rec.get('membership_person_id', None) log.debug(membership_person_id) if membership_person_result := load_membership_person_obj( membership_person_id = membership_person_id, limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, enabled = enabled, inc_address = inc_address, inc_contact = inc_contact, inc_membership_cfg = inc_membership_cfg, inc_membership_group = inc_membership_group, # The primary membership group, if there is one. inc_membership_group_list = inc_membership_group_list, # All membership groups they are a part of. inc_membership_person_profile = inc_membership_person_profile, inc_membership_person_profile_cust = inc_membership_person_profile_cust, inc_membership_type = inc_membership_type, # The primary membership type, if there is one. inc_membership_type_list = inc_membership_type_list, # All the membership types they are a part of. # inc_person = inc_person, inc_product = inc_product, # inc_product_list = inc_product_list, # inc_user = inc_user, ): person_obj.membership_person = membership_person_result else: person_obj.membership_person = None # Updated 2021-07-09 # if inc_membership_type or inc_membership_type_list: # Technically should this be inc_membership_type_list??? # from app.methods.membership_type_methods import get_membership_type_rec_list, load_membership_type_obj # if membership_type_rec_list_result := get_membership_type_rec_list( # for_obj_type = 'person', # for_obj_id = person_id, # limit = limit, # enabled = enabled, # ): # membership_type_result_list = [] # for membership_type_rec in membership_type_rec_list_result: # if membership_type_result := load_membership_type_obj( # membership_type_id = membership_type_rec.get('membership_type_id', None), # limit = limit, # by_alias = by_alias, # exclude_unset = exclude_unset, # model_as_dict = model_as_dict, # enabled = enabled, # inc_membership_type_profile = inc_membership_type_profile, # inc_membership_type_profile_cust = inc_membership_type_profile_cust, # inc_membership_type = inc_membership_type, # inc_product = inc_product, # ): # membership_type_result_list.append(membership_type_result) # else: # membership_type_result_list.append(None) # type_obj.membership_type_list = membership_type_result_list # else: type_obj.membership_type_list = [] # Updated 2021-06-18 if inc_order_list: log.info('Need to include order list data...') if order_rec_list_result := get_order_rec_list( for_obj_type = 'person', for_obj_id = person_id, limit = limit, enabled = enabled, ): order_result_list = [] for order_rec in order_rec_list_result: order_result_list.append( load_order_obj( order_id = order_rec.get('order_id', None), limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, enabled = enabled, inc_order_cfg = inc_order_cfg, inc_order_line_list = inc_order_line_list, ) ) person_obj.order_list = order_result_list else: person_obj.order_list = [] # Updated 2021-06-18 if inc_organization: log.info('Need to include organization data...') organization_id = person_rec.get('organization_id', None) log.debug(organization_id) if organization_dict := load_organization_obj( organization_id = organization_id, limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, enabled = enabled, inc_address = inc_address, inc_contact = inc_contact, ): person_obj.organization = organization_dict else: person_obj.organization = None # Updated 2021-08-19 if inc_user: log.info('Need to include user data...') user_id = person_rec.get('user_id', None) log.debug(user_id) from app.methods.user_methods import load_user_obj if user_result := load_user_obj( user_id = user_id, limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, enabled = enabled, inc_user_role_list = inc_user_role_list, ): person_obj.user = user_result else: person_obj.user = None if model_as_dict: return person_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) # pylint: disable=no-member else: return person_obj # ### END ### API Person Methods ### load_person_obj() ### # ### BEGIN ### API Person Methods ### get_person_rec_list() ### def get_person_rec_list( for_obj_type: str, for_obj_id: str, limit: int = 1000, enabled: str = 'enabled', # enabled, disabled, all ) -> list|bool: log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if for_obj_id := redis_lookup_id_random(record_id_random=for_obj_id, table_name=for_obj_type): pass else: return False data = {} data[f'{for_obj_type}_id'] = for_obj_id # data['for_obj_type'] = for_obj_type sql_obj_type_id = f'`tbl`.{for_obj_type}_id = :{for_obj_type}_id' # if enabled in ['enabled', 'disabled', 'all']: # if enabled == 'enabled': # data['enable'] = True # sql_enabled = f'AND `tbl`.enable = :enable' # elif enabled == 'disabled': # data['enable'] = False # sql_enabled = f'AND `tbl`.enable = :enable' # elif enabled == 'all': # sql_enabled = '' sql_enabled = '' if limit: data['limit'] = limit sql_limit = f'LIMIT :limit' else: sql_limit = '' sql = f""" SELECT `tbl`.id AS 'person_id', `tbl`.id_random AS 'person_id_random' FROM `person` AS `tbl` WHERE {sql_obj_type_id} {sql_enabled} ORDER BY `tbl`.created_on DESC, `tbl`.updated_on DESC {sql_limit}; """ if person_rec_li_result := sql_select(data=data, sql=sql, as_list=True): person_rec_li = person_rec_li_result else: person_rec_li = [] log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(person_rec_li_result) return person_rec_li # ### END ### API Person Methods ### get_person_rec_list() ### # ### BEGIN ### API Person Methods ### get_person_rec_w_external_id() ### # Updated 2021-08-19 def get_person_rec_w_external_id( account_id: str, external_id: str, enabled: str = 'enabled', # enabled, disabled, all ) -> dict|bool: log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if account_id := redis_lookup_id_random(record_id_random=account_id, table_name='account'): pass else: return False # account_id = 99 data = {} data['account_id'] = account_id data['external_id'] = external_id sql = f""" SELECT `person`.id AS 'person_id', `person`.id_random AS 'person_id_random' FROM `person` AS `person` WHERE person.account_id = :account_id AND person.external_id = :external_id LIMIT 1; """ if person_rec_result := sql_select(data=data, sql=sql): person_rec = person_rec_result else: person_rec = None log.debug(person_rec_result) return person_rec # ### END ### API Person Methods ### get_person_rec_w_external_id() ### # ### BEGIN ### API Person Methods ### create_person_obj() ### # NOTE: This will create a person and then also create a linked contact if person_obj.contact data is passed. The create_contact_obj will create a contact and then also create a linked address if person_obj.contact.address data is passed. # Reviewed and updated 2021-08-10 def create_person_obj(person_obj_new:Person_Base): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if not person_obj_new: return False person_obj_data = person_obj_new.dict(by_alias=False, exclude_defaults=False, exclude_unset=True, exclude={'contact', 'organization', 'created_on', 'updated_on'}) if person_obj_in_result := sql_insert(data=person_obj_data, table_name='person', rm_id_random=True, id_random_length=8): pass else: return False #log.setLevel(logging.DEBUG) log.debug(person_obj_in_result) person_id = person_obj_in_result if person_obj_new.contact: contact_obj_new = person_obj_new.contact contact_obj_new.for_type = 'person' contact_obj_new.for_id = person_id create_contact_obj_result = create_contact_obj(contact_obj_new=contact_obj_new) if isinstance(create_contact_obj_result, int): contact_id = create_contact_obj_result log.debug(f'Update person with new contact_id: {contact_id}') # NOTE: This last update should no longer be needed now that the person.contact_id is not supposed to be used. # Need to update the person with the new contact_id person_obj_up = {} # REMOVE person_obj_up['id'] = person_id # REMOVE person_obj_up['contact_id_old'] = contact_id # REMOVE if person_obj_up_result := sql_update(data=person_obj_up, table_name='person'): pass # REMOVE else: # REMOVE return False # REMOVE log.debug(person_obj_up_result) # REMOVE else: log.debug(f'No contact_id was returned when tyring to create_contact_obj(): {create_contact_obj_result}') contact_id = None log.debug(f'Returning the new person_id: {person_id}') return person_id # ### END ### API Person Methods ### create_person_obj() ### # ### BEGIN ### API Person Methods ### update_person_obj() ### # NOTE: This will update a person and then also create or update a linked contact if person_obj.contact data is passed. The create_contact_obj will create a contact and then also create a linked address if person_obj.contact.address data is passed. The update_contact_obj will update a contact and then also create or update a linked address if person_obj.contact.address data is passed. # Reviewed and updated 2021-08-10 def update_person_obj( person_id: int|str, # Ideally the int ID should be passed. This allows for updating of the id_random value. person_obj_up: Person_Base, create_missing_obj: bool = False, ) -> bool: log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if person_id := redis_lookup_id_random(record_id_random=person_id, table_name='person'): pass else: return False person_obj_up.id = person_id log.debug(person_obj_up) # log.debug(person_obj_up.dict(by_alias=True, exclude_unset=True)) log.debug(person_obj_up.dict(by_alias=False, exclude_unset=True)) # log.debug(person_obj_up.dict(by_alias=False, exclude_unset=False)) if person_obj_up.contact_id and person_obj_up.contact: contact_id = person_obj_up.contact_id contact_obj_up = person_obj_up.contact log.debug(contact_id) log.debug(contact_obj_up) if contact_obj_up_result := update_contact_obj( contact_id = contact_id, contact_obj_up = contact_obj_up, create_missing_obj = create_missing_obj, ): log.debug(contact_obj_up_result) else: log.debug(contact_obj_up_result) return False elif person_obj_up.contact and not person_obj_up.contact.id: # NOTE: This will blindly create a new contact even if there was one associated but the person.contact_id was not found. contact_obj_in = person_obj_up.contact log.debug(contact_obj_in) if contact_obj_in_result := create_contact_obj(contact_obj_new=contact_obj_in): # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(contact_obj_in_result) # NOTE: This last update should no longer be needed now that the person.contact_id is not supposed to be used. # Need to update the person with the new contact_id person_obj_up.contact_id = contact_obj_in_result # REMOVE else: # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(contact_obj_in_result) return False if person_obj_up.organization_id and person_obj_up.organization: organization_id = person_obj_up.organization_id organization_obj_up = person_obj_up.organization log.debug(organization_id) log.debug(organization_obj_up) if organization_obj_up_result := update_organization_obj( organization_id = organization_id, organization_obj_up = organization_obj_up, create_missing_obj = create_missing_obj, ): log.debug(organization_obj_up_result) else: log.debug(organization_obj_up_result) return False elif person_obj_up.organization and not person_obj_up.organization.id: # NOTE: This will blindly create a new organization even if there was one associated but the person.organization_id was not found. organization_obj_in = person_obj_up.organization log.debug(organization_obj_in) if organization_obj_in_result := create_organization_obj(organization_obj_new=organization_obj_in): # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(organization_obj_in_result) # Need to update the person with the new organization_id person_obj_up.organization_id = organization_obj_in_result else: # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(organization_obj_in_result) return False if person_obj_up.user_id and person_obj_up.user: log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL user_id = person_obj_up.user_id user_obj_up = person_obj_up.user log.debug(user_id) log.debug(user_obj_up) if user_obj_up_result := update_user_obj( user_id=user_id, user_obj_up=user_obj_up, create_missing_obj=create_missing_obj, ): log.debug(user_obj_up_result) else: log.debug(user_obj_up_result) return False elif person_obj_up.user and not person_obj_up.user.id: # NOTE: This will blindly create a new user even if there was one associated but the person.user_id was not found. user_obj_in = person_obj_up.user log.debug(user_obj_in) if user_obj_in_result := create_user_obj(user_obj_new=user_obj_in): # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(user_obj_in_result) # Need to update the person with the new user_id person_obj_up.user_id = user_obj_in_result else: # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(user_obj_in_result) return False person_dict_up = person_obj_up.dict(by_alias=False, exclude_unset=True, exclude={'contact', 'organization', 'user'}) log.debug(person_dict_up) if person_obj_up_result := sql_update(data=person_dict_up, table_name='person', rm_id_random=True): log.debug(person_obj_up_result) return True else: log.debug(person_obj_up_result) return False # ### END ### API Person Methods ### update_person_obj() ### # ### BEGIN ### API Person Methods ### create_update_person_obj() ### def create_update_person_obj( person_id: int|str|None, # Ideally the int ID should be passed. This allows for updating of the id_random value. person_obj: Person_Base, process_contact: bool = False, process_organization: bool = False, ) -> bool: log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if person_id: if person_id := redis_lookup_id_random(record_id_random=person_id, table_name='person'): pass else: return False person_obj.id = person_id else: # Insert record now and update later person_dict_in = person_obj.dict(by_alias=False, exclude_unset=True, exclude={'contact_id', 'contact_id_random', 'contact', 'organization', 'user'}) log.debug(person_dict_in) person_in_result = sql_insert( data = person_dict_in, table_name = 'person', rm_id_random = True, id_random_length = default_num_bytes, ) log.debug(person_in_result) if isinstance(person_in_result, bool) and person_in_result is True: return person_in_result elif isinstance(person_in_result, int): person_id = person_in_result person_obj.id = person_id else: return False # This should not happen. # Process contact data if process_contact and person_obj.contact: contact_obj = person_obj.contact contact_obj.for_type = 'person' contact_obj.for_id = person_id contact_id = person_obj.contact_id_random contact_result = create_update_contact_obj( contact_id = contact_id, contact_obj = contact_obj, process_address = True, # Setting to True under the assumption that if there is contact information then there is probably an address. ) log.debug(contact_result) if isinstance(contact_result, bool) and contact_result is True: pass # Do not need to update person object. elif isinstance(contact_result, bool) and contact_result is False: pass # Do not need to update person object. elif isinstance(contact_result, int): person_obj.contact_id = contact_result # pass # Do not need to update person object. else: log.warning('Something may have gone wrong while trying to create or update a contact.') # Process organization data if process_organization and person_obj.organization: organization_obj = person_obj.organization organization_id = person_obj.organization_id_random organization_result = create_update_organization_obj( organization_id = organization_id, organization_obj = organization_obj, process_contact = True, # Setting to True under the assumption that if there is organization information then there is probably a contact (and address). ) log.debug(organization_result) if isinstance(organization_result, bool) and organization_result is True: pass # Do not need to update person object. elif isinstance(organization_result, bool) and organization_result is False: pass # Do not need to update person object. elif isinstance(organization_result, int): person_obj.organization_id = organization_result else: log.warning('Something may have gone wrong while trying to create or update a organization.') # Process person data person_dict_up = person_obj.dict(by_alias=False, exclude_unset=True, exclude={'contact_id', 'contact_id_random', 'contact', 'organization', 'user'}) # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(person_obj) log.debug(person_dict_up) # Update record person_up_result = sql_update( data = person_dict_up, table_name = 'person', rm_id_random = True, ) log.debug(person_up_result) if isinstance(person_up_result, bool) and person_up_result is True: return person_id elif isinstance(person_up_result, bool) and person_up_result is False: return False elif isinstance(person_up_result, int): return person_up_result else: return False # ### END ### API Person Methods ### create_update_person_obj() ###