import datetime, json, pytz, random, secrets, string, 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, secure_hash_string, verify_secure_hash_string, common_route_params, Common_Route_Params from app.config import settings from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, sql_delete, 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.e_cvent_methods import get_access_token, get_contact_custom_field_list, get_contact_list, get_contact_id from app.methods.person_methods import create_person_kiss, get_person_rec_list, get_person_rec_w_external_id, load_person_obj, update_person_kiss from app.methods.membership_person_methods import create_membership_person_obj, update_membership_person_obj from app.models.response_models import Resp_Body_Base, mk_resp from app.models.person_models import Person_Base router = APIRouter() # ### BEGIN ### API Cvent ### get_person() ### # Updated 2022-01-28 @router.get('/person/{e_person_id}', response_model=Resp_Body_Base) async def get_person( e_person_id: str = Query(..., min_length=36, max_length=36), # UUID v4; actually the Cvent Contact UUID for a person person_id: str = Query(None, min_length=11, max_length=22), commons: Common_Route_Params = Depends(common_route_params), ): log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) # current_datetime = datetime.datetime.now() # log.debug(type(current_datetime)) # log.debug(current_datetime) # expires_in = 3600 # log.debug(type(expires_in)) # log.debug(expires_in) # future_datetime = current_datetime + datetime.timedelta(seconds=3600) # log.debug(type(future_datetime)) # log.debug(future_datetime) # return mk_resp(data=False, status_code=501, response=commons.response, status_message='Testing...') if person_id := redis_lookup_id_random(record_id_random=person_id, table_name='person'): pass elif person_id is None: pass else: return mk_resp(data=None, status_code=404, response=commons.response) # ### SECTION ### Get the Cvent Contact with the Cvent UUID. # if cvent_contact_obj_result := get_contact_id(contact_id=e_person_id): # cvent_contact_obj = cvent_contact_obj_result # else: # log.info(f'Cvent contact not found with ID: {e_person_id}') # return mk_resp(data=None, status_code=404, response=commons.response) # Not Found cvent_contact_obj = {'id': 'E1DB2588-8CFA-439B-82E7-63F65BA9CE90', 'firstName': 'Edward', 'lastName': 'Breslow', 'email': 'ed.breslow@yahoo.com', 'homePhone': '4344268975', 'homeAddress': {'address1': '10566 Ratcliffe Trail', 'city': 'Manassas', 'region': 'Virginia', 'regionCode': 'VA', 'postalCode': '20110', 'country': 'USA', 'countryCode': 'US'}, 'sourceId': '00010745', 'deleted': False, 'type': {'id': 'A01900AB-496A-48A1-9B04-C2874651227E', 'name': 'Member'}, 'created': '2022-01-24T16:43:38.307Z', 'lastModified': '2022-02-01T15:06:55.540Z', 'createdBy': 'ed.breslow@yahoo.com', 'lastModifiedBy': 'ed.breslow@yahoo.com', 'optOut': {'optedOut': False, 'by': 'Cvent Support'}, 'membership': {'joined': '2022-01-24', 'expiration': '2023-01-24'}, 'customFields': [{'id': 'e46d8043-705b-4315-9611-6fba9211b2e2', 'name': 'Non-Binary Gender', 'value': ['Male'], 'type': 'SingleSelect', 'order': 2}, {'id': '656b419b-d974-49a7-85df-cda564a7a06e', 'name': 'Qualifying Degree', 'value': ['R.Ph.'], 'type': 'SingleSelect', 'order': 3}, {'id': 'd6c6eb81-b08f-4970-9853-2fbb799d9bd1', 'name': 'Professional Designations', 'value': ['Rph'], 'type': 'FreeText', 'order': 4}, {'id': '1ef02428-59a1-493a-8535-407ed5d65f6f', 'name': 'Practice Status', 'value': ['Active'], 'type': 'SingleSelect', 'order': 5}, {'id': 'c33feaa8-a135-48b3-96c5-d6b46f894a06', 'name': 'Practice Specialty', 'value': ['Pharmacology, Doctorate'], 'type': 'SingleSelect', 'order': 6}, {'id': 'c1b47d46-d219-4f1a-a8f9-8030c7f55f31', 'name': 'Recovery Programs', 'value': ['AA', 'NA'], 'type': 'MultiSelect', 'order': 7}, {'id': '90ec10c2-59c9-47bd-8c2f-fc253da4975c', 'name': 'Emergency Contact', 'value': ['Jamie Long 434-386-1144'], 'type': 'FreeText', 'order': 8}, {'id': 'd0b462b8-b9f3-420d-836c-6c0c75dcbe12', 'name': 'SO Name Prefix', 'value': ['Mrs'], 'type': 'FreeText', 'order': 9}, {'id': '59284f29-b089-4e49-b980-795861514798', 'name': 'SO First Name', 'value': ['Edward'], 'type': 'FreeText', 'order': 10}, {'id': '65235cc9-5b66-48d4-a7d5-8a8d3f22872a', 'name': 'SO Last Name', 'value': ['Breslow'], 'type': 'FreeText', 'order': 11}, {'id': '84210a3a-43c9-491d-badf-fd5bb19151b7', 'name': 'Paper Mail Opted Out', 'value': ['Yes'], 'type': 'SingleSelect', 'order': 17}, {'id': '3b91172f-3cde-4ffc-822e-6bcc80a1eda7', 'name': 'I am joining IDAA as a', 'value': ['Medical member with a qualifying degree'], 'type': 'SingleSelect', 'order': 21}, {'id': '2245946b-2634-4044-8ad5-3cd897424f32', 'name': 'Sobriety Date', 'value': ['2021-10-15T00:00'], 'type': 'Date', 'order': 22}]} # log.debug(json.dumps(cvent_contact_obj, indent=2, default=str)) # Important variables used more than once. account_id = commons.x_account_id person_external_id = None if custom_field_list := cvent_contact_obj.get('customFields'): # List for custom_field in custom_field_list: # Get the External ID created by OSIT if custom_field.get('id') == '609ab766-7d79-4a9d-a72c-f126412659ee': # person_data['external_id'] = custom_field.get('value')[0] person_external_id = custom_field.get('value')[0] email = cvent_contact_obj.get('email') membership_person_id = None membership_person_type_id = None # Important contact fields: # id (UUID v4) # type { id, name } for contact type (membership type) # membership { expiration, joined, lastRenewal } # email and ccEmail # prefix # designation # title # nickname # firstName # lastName # middleName # company # homePhone # mobilePhone # workPhone # homeAddress { address1, address2, address3, city, country, countryCode, postalCode, region, regionCode } person_data = {} person_data['external_id'] = person_external_id # person_data['pronouns'] = ??? person_data['informal_name'] = cvent_contact_obj.get('nickname') person_data['title_names'] = cvent_contact_obj.get('prefix') person_data['given_name'] = cvent_contact_obj.get('firstName') person_data['middle_name'] = cvent_contact_obj.get('middleName') person_data['family_name'] = cvent_contact_obj.get('lastName') person_data['designations'] = cvent_contact_obj.get('designation') person_data['professional_title'] = cvent_contact_obj.get('title') person_data['affiliations'] = cvent_contact_obj.get('company') person_data['enable'] = True log.debug(person_data) contact_data = {} contact_data['email'] = cvent_contact_obj.get('email') contact_data['cc_email'] = cvent_contact_obj.get('ccEmail') contact_data['phone_mobile'] = cvent_contact_obj.get('mobilePhone') contact_data['phone_home'] = cvent_contact_obj.get('homePhone') contact_data['phone_office'] = cvent_contact_obj.get('workPhone') log.debug(contact_data) address_data = {} address_data['line_1'] = cvent_contact_obj.get('homeAddress').get('address1') address_data['line_2'] = cvent_contact_obj.get('homeAddress').get('address2') address_data['line_3'] = cvent_contact_obj.get('homeAddress').get('address3') address_data['city'] = cvent_contact_obj.get('homeAddress').get('city') address_data['state_province'] = cvent_contact_obj.get('homeAddress').get('region') country_subdivision_code = cvent_contact_obj.get('homeAddress').get('countryCode') +'-'+cvent_contact_obj.get('homeAddress').get('regionCode') address_data['country_subdivision_code'] = country_subdivision_code address_data['postal_code'] = cvent_contact_obj.get('homeAddress').get('postalCode') address_data['country_alpha_2_code'] = cvent_contact_obj.get('homeAddress').get('countryCode') address_data['country'] = cvent_contact_obj.get('homeAddress').get('country') address_data['country_alpha_2_code'] = cvent_contact_obj.get('homeAddress').get('countryCode') log.debug(address_data) contact_data['address'] = address_data person_data['contact'] = contact_data user_data = {} # user_data['name'] = user_data['username'] = cvent_contact_obj.get('email') user_data['email'] = cvent_contact_obj.get('email') random_password_string = secrets.token_urlsafe(8) user_data['password'] = secure_hash_string(string=random_password_string) user_data['email_verified'] = True # Assuming this is True because they came from another system where the email address is required. user_data['enable'] = True user_data['enable_from'] = datetime.datetime.now(datetime.timezone.utc) user_data['enable_to'] = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=365) user_data['super'] = False # This is just not allowed user_data['manager'] = False # This is just not allowed # user_data['administrator'] = False user_data['public'] = False user_data['verified'] = True user_data['notes'] = 'Created by importing list' other_data = {} other_data['temp_password'] = random_password_string user_data['other_json'] = json.dumps(other_data, indent=4) person_data['user'] = user_data # membership_person_data = {} # membership_person_data['first_start_on'] = cvent_contact_obj.get('membership').get('joined') # A trigger should update 'first_start_on' if needed??? # membership_person_data['start_on'] = cvent_contact_obj.get('membership').get('lastRenewal') # A trigger should update 'first_start_on' if needed??? # membership_person_data['end_on'] = cvent_contact_obj.get('membership').get('expiration') # log.debug(json.dumps(membership_person_data, indent=2, default=str)) membership_type_cvent_id = cvent_contact_obj.get('type').get('id') membership_type_cvent_name = cvent_contact_obj.get('type').get('name') log.info(f'Found Cvent Membership Type Named: {membership_type_cvent_name}') # 'id': '5EB898D8-C253-482C-A93A-0B6667C26E04', 'name': 'Al-Anon Member' # 'id': 'A20358C5-0F6C-47AF-9843-BA9483A9D767', 'name': 'Al-Anon Non-Member' # 'id': 'A01900AB-496A-48A1-9B04-C2874651227E', 'name': 'Member' # 'id': '65437A15-39C2-4EB5-9AFE-67AF6FE41C27', 'name': 'Non-Member' # 'id': '03622AEE-F586-4AE5-A191-B8372543A8C8', 'name': 'Student Member' # 'id': 'A69FAF20-BF2A-4222-B15B-7B0C7EFBEAA7', 'name': 'Student Non-member' # 'id': '54127B4D-E531-4046-AF5C-0F0D71DC39D2', 'name': 'Adult Guest Registration' # 'id': 'C9FA7E47-A925-44AB-B94A-9B3003CA2AC4', 'name': 'Attendee' # 'id': '6F06D6B6-2C23-4EF8-986F-73BF0DB2B229', 'name': "Children's Program with Jerry Moe (7-12 years)" # 'id': 'AADABEF0-3C84-45A2-9D9B-E2CF585D4AE5', 'name': 'General Attendee' # 'id': '96D5B3CC-FD4E-4957-BA71-9CEF388095EF', 'name': 'Guest' # 'id': '71D07118-C24D-4B2E-888D-56AC1B941495', 'name': "IDAA 20's Guest Registration" # 'id': 'DA17F721-9924-43E3-A31F-C567BA96DC64', 'name': 'IDAA Teen (13-19 years)' # 'id': 'C49439B3-5AE6-496F-A0AD-4CCB1A9000E3', 'name': 'Spouse/SO Guest Registration' # Currently in use for IDAA: # Member Type = Member Contact Type # Annual Contribution(s) = Attendee # Doctoral Qualifying Member(s) = Member # Student Member(s) = Student Member membership_person_data = {} membership_person_type_data = {} if membership_type_cvent_name == 'Al-Anon Member' or membership_type_cvent_name == 'Al-Anon Members': # membership_person_data['membership_type_id'] = 6 membership_person_type_data['membership_type_id'] = 6 membership_person_type_data['product_id'] = 13 # membership_person_data['level'] = 1 membership_person_type_data['level'] = 1 elif membership_type_cvent_name == 'Annual Contribution' or membership_type_cvent_name == 'Annual Contributions' or membership_type_cvent_name == 'Attendee': # Unsure... making affiliate # membership_person_data['membership_type_id'] = 8 membership_person_type_data['membership_type_id'] = 8 membership_person_type_data['product_id'] = 13 # membership_person_data['level'] = 3 membership_person_type_data['level'] = 3 elif membership_type_cvent_name == 'Doctoral Qualifying Member' or membership_type_cvent_name == 'Doctoral Qualifying Members' or membership_type_cvent_name == 'Member': # membership_person_data['membership_type_id'] = 5 membership_person_type_data['membership_type_id'] = 5 membership_person_type_data['product_id'] = 4 # membership_person_data['level'] = 1 membership_person_type_data['level'] = 1 elif membership_type_cvent_name == 'Student Member' or membership_type_cvent_name == 'Student Members': # membership_person_data['membership_type_id'] = 7 membership_person_type_data['membership_type_id'] = 7 membership_person_type_data['product_id'] = 14 # membership_person_data['level'] = 1 membership_person_type_data['level'] = 1 else: log.error(f'Unknown Membership Type Name from Cvent: {membership_type_cvent_name}') return mk_resp(data=person_data, status_code=400, response=commons.response, status_message='The membership type name from Cvent was unknown.') # Bad Request membership_person_type_data['first_start_on'] = datetime.datetime.strptime(cvent_contact_obj.get('membership').get('joined'), '%Y-%m-%d') if cvent_contact_obj.get('membership').get('lastRenewal'): membership_person_type_data['start_on'] = datetime.datetime.strptime(cvent_contact_obj.get('membership').get('lastRenewal'), '%Y-%m-%d') else: membership_person_type_data['start_on'] = datetime.datetime.strptime(cvent_contact_obj.get('membership').get('joined'), '%Y-%m-%d') membership_person_type_data['end_on'] = datetime.datetime.strptime(cvent_contact_obj.get('membership').get('expiration'), '%Y-%m-%d') membership_person_type_data['last_end_on'] = datetime.datetime.strptime(cvent_contact_obj.get('membership').get('expiration'), '%Y-%m-%d') current_datetime = datetime.datetime.now() if membership_person_type_data['end_on'] >= current_datetime: membership_person_type_data['lu_membership_type_status_id'] = 5 # 5 = active; expiration is > now else: membership_person_type_data['lu_membership_type_status_id'] = 7 # 7 = inactive; expiration is < now membership_person_data['enable'] = True membership_person_type_data['enable'] = True membership_person_data['membership_person_type'] = membership_person_type_data log.debug(json.dumps(membership_person_data, indent=2, default=str)) # ### TESTING BREAK POINT ### # person_data['membership_person'] = membership_person_data # return mk_resp(data=person_data, status_code=501, response=commons.response, status_message='Testing...') # ### TESTING BREAK POINT ### # Try to look up using external ID if not person_id and person_external_id: log.info(f'Looking up person with External ID: {person_external_id}') if result := get_person_rec_w_external_id(account_id=account_id, external_id=person_external_id): log.debug(result) person_id = result.get('person_id') log.info(f'Person ID {person_id} found using external ID.') else: pass # If that fails then try to look up using email address if not person_id and email: log.info(f'Looking up person with Email Address: {email}') if result := get_person_rec_list(for_obj_type='account', for_obj_id=account_id, email=email): log.debug(result[0]) person_id = result[0].get('person_id') log.info(f'Person ID {person_id} found using email address.') else: pass log.debug(f'Person ID: {person_id}; External ID: {person_external_id}; Email: {email}') # ### TESTING BREAK POINT ### # person_data['membership_person'] = membership_person_data # return mk_resp(data=person_data, status_code=501, response=commons.response, status_message='Testing...') # ### TESTING BREAK POINT ### # If there is not an external ID for an existing or new person then one needs to be created. if not person_external_id: N = 8 random_string = ''.join(random.choices(string.ascii_uppercase + string.digits, k=N)) random_string = ''.join(random.SystemRandom().choices(string.ascii_uppercase + string.digits, k=N)) # random_string = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N)) log.info(f'Created random string: {random_string}') email = cvent_contact_obj.get('email') person_external_id = f'{random_string}~{email}' log.info(f'Created new Person External ID: {person_external_id}') person_data['external_id'] = person_external_id # NOTE: Need to update Cvent record!!! if person_id: # ### SECTION ### Load person object to populate the person_data and membership_person_data dicts. person_obj = load_person_obj(person_id=person_id, inc_address=True, inc_contact=True, inc_membership_person=True, inc_membership_person_type=True, inc_user=True) log.debug(person_obj) person_data['id'] = person_id person_data['account_id'] = account_id person_data['contact']['id'] = person_obj.contact.id person_data['contact']['address']['id'] = person_obj.contact.address.id if person_obj.user: person_data['user']['id'] = person_obj.user.id if person_obj.membership_person: membership_person_id = person_obj.membership_person.id if person_obj.membership_person.membership_person_type: membership_person_type_id = person_obj.membership_person.membership_person_type.id if not person_obj.external_id: person_data['external_id'] = person_external_id log.debug(person_data) if update_person_kiss(person_id=person_id, person_dict_obj=person_data): log.info(f'Updated Person ID: {person_id}') else: log.info(f'Did not update Person ID: {person_id}') else: # ### SECTION ### Create new person and related with the person_data and membership_person_data dicts. # person_data['id'] = person_id person_data['account_id'] = account_id # person_data['contact']['id'] = person_obj.contact.id # person_data['contact']['address']['id'] = person_obj.contact.address.id # person_data['user']['id'] = person_obj.user.id log.debug(person_data) if create_person_obj_result := create_person_kiss(account_id=account_id, person_dict_obj=person_data): person_id = create_person_obj_result log.info(f'Created Person ID: {person_id}') else: log.info(f'Did not create Person') # ### TESTING BREAK POINT ### # person_data['membership_person'] = membership_person_data # return mk_resp(data=person_data, status_code=501, response=commons.response, status_message='Testing...') # ### TESTING BREAK POINT ### if membership_person_id: # membership_person_id = person_obj.membership_person.id membership_person_data['id'] = membership_person_id membership_person_data['membership_person_type']['id'] = membership_person_type_id if update_membership_person_obj(membership_person_id=membership_person_id, membership_person_dict_obj=membership_person_data): log.info(f'Updated Membership Person ID: {membership_person_id}') else: log.info(f'Did not update Membership Person ID: {membership_person_id}') # person_data['membership_person'] = membership_person_data # return mk_resp(data=person_data, status_message='Updated and loaded person based on Cvent contact information', response=commons.response) else: # membership_person_id = person_obj.membership_person.id # membership_person_data['id'] = person_obj.membership_person.id # membership_person_data['membership_person_type']['id'] = person_obj.membership_person.membership_person_type.id # membership_person_data['person_id'] = person_id if create_membership_person_obj_result := create_membership_person_obj(account_id=account_id, person_id=person_id, membership_person_dict_obj=membership_person_data): membership_person_id = create_membership_person_obj_result log.info(f'Created Membership Person ID: {membership_person_id}') else: log.info(f'Did not create Membership Person') person_data['membership_person'] = membership_person_data log.debug(json.dumps(person_data, indent=2, default=str)) person_obj = load_person_obj(person_id=person_id, inc_address=True, inc_contact=True, inc_membership_person=True, inc_membership_person_type=True, inc_user=True) log.debug(person_obj) return mk_resp(data=person_obj, status_message='Created/Updated and loaded person based on Cvent contact information', response=commons.response) # return mk_resp(data=person_data, status_message='Got Cvent contact information', response=commons.response)