diff --git a/app/methods/e_cvent_methods.py b/app/methods/e_cvent_methods.py index 3c1181c..36cc8f6 100644 --- a/app/methods/e_cvent_methods.py +++ b/app/methods/e_cvent_methods.py @@ -1,12 +1,12 @@ from __future__ import annotations -import datetime, pprint, pytz, random, requests, time +import datetime, json, pprint, pytz, random, requests, secrets, time from requests.auth import HTTPBasicAuth 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_select, sql_update -from app.lib_general import log, logging, logger_reset +from app.lib_general import log, logging, logger_reset, secure_hash_string, verify_secure_hash_string from app.methods.person_methods import get_person_rec_w_external_id, load_person_obj, update_person_kiss @@ -145,6 +145,42 @@ def get_contact_list(api, external_id: str=False): return response_data +# Updated 2022-02-02 +@logger_reset +def get_recent_contact_list(created_on=None, updated_on=None): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + get_access_token() + + log.warning('Sleeping for 1 second to avoid Cvent rate limit...') + time.sleep(1) + + endpoint = f'/contacts' + uri = api['base_url']+endpoint + + params = {} + if created_on: + created_on_str = created_on.isoformat() + print(f'Created On: {created_on_str}') + params['filter'] = f"deleted eq 'False' and created gt '{created_on_str}Z'" + elif updated_on: + print(f'Updated On: {created_on_str}') + params['filter'] = f"deleted eq 'False' and lastModified gt '{updated_on_str}Z'" + else: + log.warning('Created On or Updated On is requried. Returing False') + return False + + resp = requests.get(url=uri, headers=api['headers'], params=params) + + response_data = resp.json() + log.debug(response_data) + + if 'message' in response_data and response_data['message'] == 'Too Many Requests': return False + + return response_data + + # Updated 2022-02-01 @logger_reset def get_contact_id(contact_id: str): @@ -170,11 +206,6 @@ def get_contact_id(contact_id: str): if 'message' in response_data and response_data['message'] == 'Too Many Requests': return False - # filename = f'contact_{contact_id}.txt' - - # f = open(filename, 'w') - # f.write(pprint.pformat(response_data, indent=2, width=160)) - return response_data @@ -216,3 +247,292 @@ def modify_contact_id(contact_id: str, field_list: list=[], custom_field_id: str if 'message' in response_data and response_data['message'] == 'Too Many Requests': return False return response_data + + +# ### BEGIN ### API External Cvent ### create_update_aether_person() ### +# Updated 2022-02-02 +@logger_reset +def create_update_aether_person( + cvent_contact_id: str, + cvent_contact_obj: dict, + account_id: str, + person_id: str=None, + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + 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 + + 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 = {} + if cvent_contact_obj.get('homeAddress'): + 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') + if cvent_contact_obj.get('homeAddress').get('countryCode') and cvent_contact_obj.get('homeAddress').get('regionCode'): + 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 + + if cvent_contact_obj.get('type'): + 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}') + else: + membership_type_cvent_id = None + membership_type_cvent_name = None + + # '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: + if membership_type_cvent_name == 'Al-Anon Member' or membership_type_cvent_name == 'Al-Anon Members': + membership_person_type_data['membership_type_id'] = 6 + membership_person_type_data['product_id'] = 13 + 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_type_data['membership_type_id'] = 8 + membership_person_type_data['product_id'] = 13 + 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_type_data['membership_type_id'] = 5 + membership_person_type_data['product_id'] = 4 + membership_person_type_data['level'] = 1 + elif membership_type_cvent_name == 'Student Member' or membership_type_cvent_name == 'Student Members': + membership_person_type_data['membership_type_id'] = 7 + membership_person_type_data['product_id'] = 14 + membership_person_type_data['level'] = 1 + else: + log.error(f'Unknown Membership Type Name from Cvent: {membership_type_cvent_name}') + return False + + 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 False + # ### 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 False + # ### 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: Update Cvent Contact + custom_field_id = '609ab766-7d79-4a9d-a72c-f126412659ee' # IDAA Cvent External ID UUID + custom_field_value = [person_external_id] + + log.warning('Sleeping for 1 second to avoid Cvent rate limit...') + time.sleep(1) + + contact_mod_result = modify_contact_id( + contact_id = cvent_person_id, # This is the Cvent Contact UUID for a person + # field_list = field_list, + custom_field_id = custom_field_id, + custom_field_value = custom_field_value, + ) + + 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 False + # ### TESTING BREAK POINT ### + + if membership_type_cvent_id and 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}') + elif membership_type_cvent_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') + else: + membership_person_data = {} + + 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 person_obj # True +# ### END ### API External Cvent ### create_update_aether_person() ### diff --git a/app/routers/e_cvent.py b/app/routers/e_cvent.py index 6ece381..56b2e92 100644 --- a/app/routers/e_cvent.py +++ b/app/routers/e_cvent.py @@ -9,7 +9,7 @@ from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, 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, modify_contact_id +from app.methods.e_cvent_methods import create_update_aether_person, get_access_token, get_contact_custom_field_list, get_contact_list, get_contact_id, modify_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 @@ -22,9 +22,9 @@ router = APIRouter() # ### BEGIN ### API Cvent ### get_person() ### # Updated 2022-02-01 -@router.get('/person/{e_person_id}', response_model=Resp_Body_Base) +@router.get('/person/{cvent_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 + cvent_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), @@ -51,10 +51,10 @@ async def get_person( 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): + if cvent_contact_obj_result := get_contact_id(contact_id=cvent_person_id): cvent_contact_obj = cvent_contact_obj_result else: - log.info(f'Cvent contact not found with ID: {e_person_id}') + log.info(f'Cvent contact not found with ID: {cvent_person_id}') return mk_resp(data=None, status_code=404, response=commons.response) # Not Found # cvent_contact_obj = {'id': '59B1642A-E193-4B47-910B-566029C44620', 'firstName': 'Nicholas', 'lastName': 'Kasnick', 'email': 'kasnick6412@gmail.com', 'homePhone': '3603494072', 'homeAddress': {'address1': '14924 89th ave', 'city': 'Yelm', 'postalCode': '98597', 'country': 'USA', 'countryCode': 'US'}, 'sourceId': '00293862', 'deleted': False, 'type': {'id': '03622AEE-F586-4AE5-A191-B8372543A8C8', 'name': 'Student Member'}, 'created': '2022-01-25T02:43:17.157Z', 'lastModified': '2022-01-26T16:07:43.505Z', 'createdBy': 'kasnick6412@gmail.com', 'lastModifiedBy': 'kasnick6412@gmail.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': ['Student, Qualifying Healthcare Professional'], 'type': 'SingleSelect', 'order': 3}, {'id': '1ef02428-59a1-493a-8535-407ed5d65f6f', 'name': 'Practice Status', 'value': ['In Training'], 'type': 'SingleSelect', 'order': 5}, {'id': 'c1b47d46-d219-4f1a-a8f9-8030c7f55f31', 'name': 'Recovery Programs', 'value': ['AA', 'Al-Anon'], 'type': 'MultiSelect', 'order': 7}, {'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': ['2022-01-08T00:00'], 'type': 'Date', 'order': 22}]} @@ -68,16 +68,29 @@ async def get_person( # 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 + # 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 + + if create_update_aether_person_result := create_update_aether_person( + cvent_contact_id = cvent_person_id, + cvent_contact_obj = cvent_contact_obj, + account_id = account_id, + person_id = person_id + ): + person_obj = create_update_aether_person_result + return mk_resp(data=person_obj, status_message='Created/Updated and loaded person based on Cvent contact information', response=commons.response) + else: + log.info(f'Something went wrong while trying to create or update the person in Aether based on Cvent data. Cvent (Person) Contact ID: {cvent_person_id}') + return mk_resp(data=None, status_code=400, response=commons.response) # Bad Request + # Important contact fields: # id (UUID v4) @@ -296,7 +309,7 @@ async def get_person( time.sleep(1) contact_mod_result = modify_contact_id( - contact_id = e_person_id, # This is the Cvent Contact UUID for a person + contact_id = cvent_person_id, # This is the Cvent Contact UUID for a person # field_list = field_list, custom_field_id = custom_field_id, custom_field_value = custom_field_value,