import datetime, pytz, time from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, Response, status from pydantic import BaseModel, EmailStr, Field from typing import Dict, List, Optional, Set, Union from app.lib_general import log, logging from app.config import settings from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, sql_delete, get_id_random, redis_lookup_id_random from app.routers.api_crud import delete_obj_template, get_obj_template, get_obj_li_template, patch_obj_template, post_obj_template # from app.methods.membership_person_methods import load_membership_person_obj from app.methods.person_methods import create_update_person_obj_v4b, email_person_create_url, get_person_rec_list, get_person_rec_w_external_id, load_person_obj, update_person_obj from app.models.person_models import Person_Base from app.models.response_models import Resp_Body_Base, mk_resp router = APIRouter() @router.post('/person', response_model=Resp_Body_Base) async def post_person_obj( obj: Person_Base, x_account_id: str = Header(...), return_obj: Optional[bool] = True, by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) obj_type = 'person' obj_data_dict = obj.dict(by_alias=False, exclude_unset=True) result = post_obj_template( obj_type=obj_type, data=obj_data_dict, return_obj=True, by_alias=True, exclude_unset=True, ) return result @router.patch('/person/{obj_id}', response_model=Resp_Body_Base) async def patch_person_obj( obj: Person_Base, obj_id: str = Query(..., min_length=1, max_length=22), x_account_id: Optional[str] = Header(..., ), return_obj: Optional[bool] = True, by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) obj_type = 'person' obj_data_dict = obj.dict(by_alias=False, exclude_unset=True) obj_data_dict['id'] = redis_lookup_id_random(record_id_random=obj_id, table_name=obj_type) obj_data_dict['id_random'] = obj_id result = patch_obj_template( obj_type=obj_type, data=obj_data_dict, obj_id=obj_id, return_obj=True, by_alias=True, exclude_unset=True, ) return result # ### BEGIN ### API Person ### v3_post_person_obj_new() ### # Using create_update_person_obj_v4b() now # Updated 2021-09-08 @router.post('/v3/person/new', response_model=Resp_Body_Base) async def v3_post_person_obj_new( person_obj: Person_Base, process_contact: bool = False, process_organization: bool = False, process_user: bool = False, create_sub_obj: bool = False, fail_any: bool = True, # Fail if any thing goes wrong for sub objects enabled: str = 'enabled', # For now this covers any included objects or object lists inc_address: bool = False, inc_contact: bool = False, inc_organization: bool = False, inc_user: bool = False, return_obj: bool = True, limit: int = 50, by_alias: bool = True, # include: Optional[list] = [], # exclude: Optional[list] = [], exclude_unset: Optional[bool] = True, # exclude_none: Optional[bool] = True, x_account_id: str = Header(...), response: Response = Response, ): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if create_update_person_obj_result := create_update_person_obj_v4b( account_id = x_account_id, person_dict_obj = person_obj, person_id = None, # process_contact = process_contact, # process_organization = process_organization, # process_user = process_user, ): pass else: return mk_resp(data=False, status_code=400, response=response, status_message='The person was not created. Check the field names and data types.') if isinstance(create_update_person_obj_result, int): person_id = create_update_person_obj_result if return_obj: if load_person_obj_result := load_person_obj( person_id = person_id, enabled = enabled, inc_address = inc_address, inc_contact = inc_contact, inc_organization = inc_organization, inc_user = inc_user, ): data = load_person_obj_result else: data = False return mk_resp(data=data, response=response, status_message='The person was probably created, but there was a problem returning the data.') else: person_id = create_update_person_obj_result person_id_random = get_id_random(record_id=person_id, table_name='person') data = {} data['person_id'] = person_id data['person_id_random'] = person_id_random return mk_resp(data=data, response=response, status_message='The person was created.') else: return mk_resp(data=False, status_code=400, response=response, status_message='The result from trying to create a person was unexpected.') # ### BEGIN ### API Person ### v3_post_person_obj_new() ### # ### BEGIN ### API Person ### v3_patch_person_obj_exist ### # Using create_update_person_obj_v4b() now # Updated 2021-09-08 @router.patch('/v3/person/{person_id}/exist', response_model=Resp_Body_Base) async def v3_patch_person_obj_exist( person_obj: Person_Base, person_id: str = Query(..., min_length=11, max_length=22), process_contact: bool = False, process_organization: bool = False, process_user: bool = False, create_sub_obj: bool = False, fail_any: bool = True, # Fail if any thing goes wrong for sub objects enabled: str = 'enabled', # For now this covers any included objects or object lists inc_address: bool = False, inc_contact: bool = False, inc_organization: bool = False, inc_user: bool = False, return_obj: bool = True, limit: int = 50, by_alias: bool = True, # include: Optional[list] = [], # exclude: Optional[list] = [], exclude_unset: Optional[bool] = True, # exclude_none: Optional[bool] = True, x_account_id: str = Header(...), response: Response = Response, ): 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 mk_resp(data=None, status_code=404, response=response, status_message='The person was not updated. The person ID was invalid or not found.') if create_update_person_obj_result := create_update_person_obj_v4b( account_id = x_account_id, person_dict_obj = person_obj, person_id = person_id, # process_contact = process_contact, # process_organization = process_organization, # process_user = process_user, ): pass else: return mk_resp(data=False, status_code=400, response=response, status_message='The person was not updated. Check the field names and data types.') if isinstance(create_update_person_obj_result, int): person_id = create_update_person_obj_result if return_obj: if load_person_obj_result := load_person_obj( person_id = person_id, enabled = enabled, inc_address = inc_address, inc_contact = inc_contact, inc_organization = inc_organization, inc_user = inc_user, ): data = load_person_obj_result else: data = False return mk_resp(data=data, response=response, status_message='The person was probably updated, but there was a problem returning the data.') else: person_id = create_update_person_obj_result person_id_random = get_id_random(record_id=person_id, table_name='person') data = {} data['person_id'] = person_id data['person_id_random'] = person_id_random return mk_resp(data=data, response=response, status_message='The person was updated.') else: return mk_resp(data=False, status_code=400, response=response, status_message='The result from trying to update the person was unexpected.') # ### END ### API Person ### v3_patch_person_obj_exist ### # ### BEGIN ### API Person ### post_person_json() ### # Updated 2021-09-08 @router.post('/person/json', response_model=Resp_Body_Base) async def post_person_json( person_obj: Person_Base, # person_id: str = Query(..., min_length=1, max_length=22), # create_sub_obj: bool = False, process_contact: bool = False, process_organization: bool = False, process_user: bool = False, return_obj: Optional[bool] = True, by_alias: Optional[bool] = True, # include: Optional[list] = [], # exclude: Optional[list] = [], exclude_unset: Optional[bool] = True, # exclude_none: Optional[bool] = True, x_account_id: Optional[str] = Header(..., ), response: Response = Response, ): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if person_obj_in_result := create_update_person_obj_v4b( account_id = x_account_id, person_dict_obj = person_obj, person_id = None, process_contact = process_contact, process_organization = process_organization, process_user = process_user, ): log.debug(person_obj_in_result) if return_obj: person_obj = load_person_obj(person_id=person_obj_in_result) person_dict = person_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) return mk_resp(data=person_dict) else: return mk_resp(data=person_obj_in_result) else: return mk_resp(data=False, status_code=400, response=response) # Bad Request # ### END ### API Person ### post_person_json() ### # ### BEGIN ### API Person ### patch_person_json() ### # Updated 2021-06-25 @router.patch('/person/{person_id}/json', response_model=Resp_Body_Base) async def patch_person_json( person_obj: Person_Base, person_id: str = Query(..., min_length=11, max_length=22), process_contact: bool = False, process_organization: bool = False, process_user: bool = False, x_account_id: Optional[str] = Header(..., ), return_obj: Optional[bool] = True, by_alias: Optional[bool] = True, include: Optional[list] = [], exclude: Optional[list] = [], exclude_unset: Optional[bool] = True, exclude_none: Optional[bool] = True, response: Response = Response, ): 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 mk_resp(data=None, status_code=404, response=response) if person_obj_up_result := create_update_person_obj_v4b( account_id = x_account_id, person_dict_obj = person_obj, process_contact = process_contact, process_organization = process_organization, process_user = process_user, ): log.debug(person_obj_up_result) if return_obj: person_obj = load_person_obj(person_id=person_id) person_dict = person_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) return mk_resp(data=person_dict) else: return mk_resp(data=person_obj_up_result) else: return mk_resp(data=False, status_code=400, response=response) # Bad Request # ### END ### API Person ### patch_person_json() ### @router.get('/person/list', response_model=Resp_Body_Base) async def get_person_obj_li( for_obj_type: Optional[str] = Query(None, min_length=2, max_length=50), for_obj_id: Optional[str] = Query(None, min_length=1, max_length=22), x_account_id: str = Header(...), by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) obj_type = 'person' result = get_obj_li_template( obj_type=obj_type, for_obj_type=for_obj_type, for_obj_id=for_obj_id, by_alias=True, exclude_unset=True, ) return result # ### BEGIN ### API Person ### person_obj_external_id() ### # NOTE: This endpoint is a little different. It will likely be used to help log in the person/user coming from an external service (Cvent). # Updated 2021-08-19 @router.get('/person/external_id', response_model=Resp_Body_Base) async def person_obj_external_id( account_id: str = Query(..., min_length=1, max_length=22), external_id: str = Query(..., min_length=1, max_length=75), limit: int = 500, enabled: str = 'enabled', inc_address: bool = False, inc_contact: bool = False, # inc_person: bool = False, inc_user: bool = False, inc_user_role_list: bool = False, x_account_id: str = Header(...), by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): 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 mk_resp(data=None, status_code=404, response=response) # account_id = 99 # WARNING!!!! Get rid of 99! if person_data := get_person_rec_w_external_id(account_id=account_id, external_id=external_id): pass else: return mk_resp(data=None, status_code=404, response=response) person_id = person_data.get('person_id') if person_dict := load_person_obj( person_id = person_id, limit = limit, model_as_dict = True, # NOTE: returning model as a dict enabled = enabled, inc_address = inc_address, inc_contact = inc_contact, inc_user = inc_user, inc_user_role_list = inc_user_role_list, ): if isinstance(person_dict, dict): response_data = person_dict else: response_data = person_dict else: return mk_resp(data=False, status_code=400, response=response) # Bad Request return mk_resp(data=response_data) # ### END ### API Person ### person_obj_external_id() ### # ### BEGIN ### API Person ### lookup_email() ### # Updated 2021-12-01 @router.get('/person/lookup_email', response_model=Resp_Body_Base) async def lookup_email( email: str = Query(..., min_length=5, max_length=75), inc_address: bool = False, inc_contact: bool = False, inc_user: bool = False, inc_user_role_list: bool = False, enabled: str = 'enabled', limit: int = 50, by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, x_account_id: str = Header(...), response: Response = Response, ): log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if account_id := redis_lookup_id_random(record_id_random=x_account_id, table_name='account'): pass else: return mk_resp(data=None, status_code=404, response=response) # import time # time.sleep(1) if person_rec_list_result := get_person_rec_list( #account_id = account_id, for_obj_type = 'account', for_obj_id = account_id, email = email, enabled = enabled, limit = limit, ): person_result_list = [] for person_rec in person_rec_list_result: if load_person_result := load_person_obj( person_id = person_rec.get('person_id', None), inc_address = inc_address, inc_contact = inc_contact, inc_user = inc_user, inc_user_role_list = inc_user_role_list, enabled = enabled, limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, # model_as_dict = model_as_dict, ): person_result_list.append(load_person_result) else: person_result_list.append(None) response_data = person_result_list elif person_rec_list_result is None: log.info('No results') return mk_resp(data=None, status_code=404, response=response) # Not Found else: log.warning('Likely bad request') return mk_resp(data=False, status_code=400, response=response) # Bad Request return mk_resp(data=response_data) # ### END ### API Person ### lookup_email() ### # ### BEGIN ### API Person ### email_create_url() ### # Updated 2021-12-03 # @router.get('/person/email_create_url', response_model=Resp_Body_Base) @router.get('/person/{person_id}/email_create_url', response_model=Resp_Body_Base) async def email_create_url( person_id: Optional[str] = Query(None, min_length=11, max_length=22), root_url: Optional[str] = Query(None, min_length=10, max_length=100), # Absolute min = 7 x_account_id: Optional[str] = Header(..., ), return_obj: bool = False, by_alias: bool = True, exclude_unset: bool = True, response: Response = Response, ): log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if account_id := redis_lookup_id_random(record_id_random=x_account_id, table_name='account'): pass else: return mk_resp(data=False, status_code=404, response=response) # Not Found if person_id := redis_lookup_id_random(record_id_random=person_id, table_name='person'): pass else: return mk_resp(data=False, status_code=404, response=response) # Not Found if result := email_person_create_url( account_id = account_id, person_id = person_id, root_url = root_url, ): log.info('Email with create URL was sent.') return mk_resp(data=True, response=response) else: log.warning('Email with create URL was not sent.') return mk_resp(data=False, status_code=500, response=response) # ### END ### API Person ### email_create_url() ### # ### BEGIN ### API Person ### get_person_obj() ### # Working well as of 2021-06-25. Using as a template for other routes. @router.get('/person/{person_id}', response_model=Resp_Body_Base) async def get_person_obj( person_id: str = Query(..., min_length=1, max_length=22), limit: int = 500, # For now this covers any included objects or object lists enabled: str = 'enabled', # For now this covers any included objects or object lists inc_address: bool = False, # Priority l1 # inc_archive_list: bool = False, # Priority l3 inc_contact: bool = False, # Priority l1 inc_event_list: bool = False, # Priority l1 # inc_hosted_file_list: bool = False, # Priority l3 inc_journal_list: bool = False, # Priority l2 # inc_journal_entry_list: bool = False, # Priority l3 # inc_membership_group: bool = False, # The primary membership group # inc_membership_group_person: bool = False, inc_membership_group_list: bool = False, # The list of all membership groups a person is a part of inc_membership_group_person_list: bool = False, inc_membership_person: bool = False, # Priority l2 inc_membership_person_profile: bool = False, # Priority l2 inc_membership_type: bool = False, # The primary membership type inc_membership_type_person: bool = False, # inc_membership_type_list: bool = False, # The list of all membership types a person is a part of # inc_membership_type_person_list: bool = False, inc_order_closed_count: bool = False, # NEW Priority l1 inc_order_line_list: bool = False, # Priority l1 inc_order_list: bool = False, # Priority l1 inc_order_cart: bool = False, # NEW Priority l1 # inc_order_cart_list: bool = False, # Priority l1 inc_organization: bool = False, # Priority l1 # inc_organization_list: bool = False, inc_post_list: bool = False, # Priority l2 inc_post_comment_list: bool = False, # Priority l3 inc_user: bool = False, # Priority l1 x_account_id: str = Header(...), by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): 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 mk_resp(data=None, status_code=404, response=response) if person_dict := load_person_obj( person_id = person_id, limit = limit, exclude_unset = False, model_as_dict = False, # NOTE: returning model as a dict enabled = enabled, inc_address = inc_address, # inc_archive_list = inc_archive_list, inc_contact = inc_contact, inc_event_list = inc_event_list, # inc_hosted_file_list = inc_hosted_file_list, inc_journal_list = inc_journal_list, # inc_journal_entry_list = inc_journal_entry_list, inc_membership_group_list = inc_membership_group_list, inc_membership_group_person_list = inc_membership_group_person_list, inc_membership_person = inc_membership_person, inc_membership_person_profile = inc_membership_person_profile, inc_membership_type = inc_membership_type, inc_membership_type_person = inc_membership_type_person, inc_order_closed_count = inc_order_closed_count, inc_order_line_list = inc_order_line_list, inc_order_list = inc_order_list, inc_order_cart = inc_order_cart, # inc_order_cart_list = inc_order_cart_list, inc_organization = inc_organization, # inc_organization_list = inc_organization_list, inc_post_list = inc_post_list, inc_post_comment_list = inc_post_comment_list, inc_user = inc_user, ): if isinstance(person_dict, dict): response_data = person_dict else: response_data = person_dict else: return mk_resp(data=False, status_code=400, response=response) # Bad Request return mk_resp(data=response_data) # ### END ### API Person ### get_person_obj() ### # ### BEGIN ### API Person ### get_account_obj_person_list() ### # Working well as of 2021-07-09. Using as a template for other routes. @router.get('/account/{account_id}/person/list', response_model=Resp_Body_Base) async def get_account_obj_person_list( account_id: str = Query(..., min_length=1, max_length=22), limit: int = 500, # For now this covers any included objects or object lists enabled: str = 'enabled', # For now this covers any included objects or object lists inc_address: bool = False, inc_contact: bool = False, # inc_membership_group_list: bool = False, # The list of all membership groups a person is a part of inc_membership_person: bool = False, # inc_membership_person_profile: bool = False, # Profile? # inc_membership_person_profile_cust: bool = False, # Extended profile? # inc_membership_type_person: bool = False, # inc_order: bool = False, # inc_organization: bool = False, # inc_product: bool = False, # The product the person actually purchased for a member_type or member_group # inc_product_list: bool = False, # The list of products that give access to a member_type or member_group inc_user: bool = False, x_account_id: str = Header(...), by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, response: Response = Response, ): log.setLevel(logging.WARNING) # 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 mk_resp(data=None, status_code=404, response=response) response_data = None # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL # Updated 2021-06-30 if person_rec_list_result := get_person_rec_list( for_obj_type = 'account', for_obj_id = account_id, limit = limit, enabled = enabled, ): person_result_list = [] for person_rec in person_rec_list_result: if load_person_result := load_person_obj( person_id = person_rec.get('person_id', None), 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_person = inc_membership_person, inc_user = inc_user, ): person_result_list.append(load_person_result) else: person_result_list.append(None) response_data = person_result_list else: return mk_resp(data=False, status_code=400, response=response) # Bad Request return mk_resp(data=response_data) # ### END ### API Person ### get_account_obj_person_list() ### @router.delete('/person/{obj_id}', response_model=Resp_Body_Base) async def delete_person_obj( obj_id: str = Query(..., min_length=1, max_length=22), x_account_id: str = Header(...), response: Response = Response, ): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) obj_type = 'person' result = delete_obj_template( obj_type=obj_type, obj_id=obj_id, ) return result