Files
OSIT-AE-API-FastAPI/app/routers/e_cvent.py
2022-02-04 18:15:39 -05:00

320 lines
23 KiB
Python

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 create_update_aether_person, get_access_token, get_contact_custom_field_list, get_contact_list, get_contact_id, get_recent_contact_list, 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
from app.models.response_models import Resp_Body_Base, mk_resp
from app.models.person_models import Person_Base
router = APIRouter()
# ### BEGIN ### API Cvent ### process_recent_changes() ###
# NOTE: Person in this case really means a Cvent Contact
# Updated 2022-02-01
@router.get('/person/process_recent_changes', response_model=Resp_Body_Base)
async def process_recent_changes(
from_created_on: datetime.datetime = datetime.datetime.utcnow() + datetime.timedelta(minutes=-60), # for Cvent "created"
to_created_on: datetime.datetime = datetime.datetime.utcnow(), # for Cvent "created"
from_updated_on: datetime.datetime = datetime.datetime.utcnow() + datetime.timedelta(minutes=-60), # for Cvent "lastModified"
to_updated_on: datetime.datetime = None, # for Cvent "lastModified"
# type: str = 'created', # created, updated, created_updated
return_detail: bool = False,
commons: Common_Route_Params = Depends(common_route_params),
):
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
account_id = commons.x_account_id
if cvent_contact_list_result := get_recent_contact_list(
from_created_on = from_created_on,
to_created_on = to_created_on,
from_updated_on = from_updated_on,
to_updated_on = to_updated_on,
):
log.debug(cvent_contact_list_result)
cvent_contact_list = cvent_contact_list_result
log.info(f'Found {len(cvent_contact_list)} Cvent contacts')
else:
log.warning(f'Something went wrong while trying to get recently created or updated person in Cvent. From Datetime: {from_created_on}')
return mk_resp(data=None, status_code=400, response=commons.response) # Bad Request
# cvent_contact_list = cvent_contact_list_result.get('data')
# cvent_contact_list = cvent_contact_list_result
# return mk_resp(data=cvent_contact_list, status_message=f'Checked for recent changes in Cvent. Found {len(cvent_contact_list)} Cvent contacts', response=commons.response)
log.warning('Sleeping for 1.1 second to avoid Cvent rate limit...')
time.sleep(1.1)
for cvent_person_contact_obj in cvent_contact_list:
cvent_person_contact_id = cvent_person_contact_obj.get('id')
log.info(f'Processing Cvent (Person) Contact ID: {cvent_person_contact_id}')
if create_update_aether_person_result := create_update_aether_person(
cvent_contact_id = cvent_person_contact_id,
cvent_contact_obj = cvent_person_contact_obj,
account_id = account_id,
person_id = None,
):
# person_obj = create_update_aether_person_result
person_id = create_update_aether_person_result
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_contact_id}')
log.warning('Sleeping for .5 second to avoid Cvent rate limit...')
time.sleep(.5)
if return_detail:
return mk_resp(data=cvent_contact_list_result, status_message=f'Checked for recent changes in Cvent. Created/Updated {len(cvent_contact_list)} Cvent contacts', response=commons.response)
else:
return mk_resp(data=cvent_contact_list, status_message=f'Checked for recent changes in Cvent. Found {len(cvent_contact_list)} Cvent contacts', response=commons.response)
# ### END ### API Cvent ### process_recent_changes() ###
# ### BEGIN ### API Cvent ### get_person() ###
# Updated 2022-02-02
@router.get('/person/{cvent_person_contact_id}', response_model=Resp_Body_Base)
async def get_person(
cvent_person_contact_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.INFO) # 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_person_contact_obj_result := get_contact_id(contact_id=cvent_person_contact_id):
cvent_person_contact_obj = cvent_person_contact_obj_result
else:
log.info(f'Cvent contact not found with ID: {cvent_person_contact_id}')
return mk_resp(data=None, status_code=404, response=commons.response) # Not Found
# cvent_person_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}]}
# cvent_person_contact_obj = {'id': 'C5D6EBB7-8CDE-492E-A953-5243942CB66E', 'firstName': 'C Eve J', 'lastName': 'Kimball', 'email': 'doceve1@gmail.com', 'prefix': 'Dr.', 'homePhone': '6104638775', 'homeAddress': {'address1': '14 GAELSONG LN', 'city': 'WYOMISSING', 'region': 'Pennsylvania', 'regionCode': 'PA', 'postalCode': '19610', 'country': 'USA', 'countryCode': 'US'}, 'sourceId': '00037205', 'deleted': False, 'type': {'id': 'A01900AB-496A-48A1-9B04-C2874651227E', 'name': 'Member'}, 'created': '2022-01-29T14:00:36.103Z', 'lastModified': '2022-01-31T18:36:34.809Z', 'createdBy': 'doceve1@gmail.com', 'lastModifiedBy': 'doceve1@gmail.com', 'optOut': {'optedOut': False, 'by': 'Cvent Support'}, 'membership': {'joined': '2022-01-29', 'expiration': '2023-01-29'}, 'customFields': [{'id': 'e46d8043-705b-4315-9611-6fba9211b2e2', 'name': 'Non-Binary Gender', 'value': ['Female'], 'type': 'SingleSelect', 'order': 2}, {'id': '656b419b-d974-49a7-85df-cda564a7a06e', 'name': 'Qualifying Degree', 'value': ['M.D.'], 'type': 'SingleSelect', 'order': 3}, {'id': 'd6c6eb81-b08f-4970-9853-2fbb799d9bd1', 'name': 'Professional Designations', 'value': ['MD'], 'type': 'FreeText', 'order': 4}, {'id': '1ef02428-59a1-493a-8535-407ed5d65f6f', 'name': 'Practice Status', 'value': ['Retired'], 'type': 'SingleSelect', 'order': 5}, {'id': 'c33feaa8-a135-48b3-96c5-d6b46f894a06', 'name': 'Practice Specialty', 'value': ['Pediatrics'], 'type': 'SingleSelect', 'order': 6}, {'id': 'c1b47d46-d219-4f1a-a8f9-8030c7f55f31', 'name': 'Recovery Programs', 'value': ['AA', 'Al-Anon', 'OA'], 'type': 'MultiSelect', 'order': 7}, {'id': '90ec10c2-59c9-47bd-8c2f-fc253da4975c', 'name': 'Emergency Contact', 'value': ['Dan Kimball - 610-406-7753'], 'type': 'FreeText', 'order': 8}, {'id': 'd0b462b8-b9f3-420d-836c-6c0c75dcbe12', 'name': 'SO Name Prefix', 'value': ['Dr.'], 'type': 'FreeText', 'order': 9}, {'id': '59284f29-b089-4e49-b980-795861514798', 'name': 'SO First Name', 'value': ['Daniel B'], 'type': 'FreeText', 'order': 10}, {'id': '65235cc9-5b66-48d4-a7d5-8a8d3f22872a', 'name': 'SO Last Name', 'value': ['Kimball, Jr.'], 'type': 'FreeText', 'order': 11}, {'id': '50d5efaa-d629-4eeb-8d6e-f51b95eb2eb9', 'name': 'SO Professional Designation', 'value': ['MD'], 'type': 'FreeText', 'order': 12}, {'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': ['1979-03-31T00:00'], 'type': 'Date', 'order': 22}]}
# cvent_person_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_person_contact_obj, indent=2, default=str))
# Important variables used more than once.
account_id = commons.x_account_id
if create_update_aether_person_result := create_update_aether_person(
cvent_contact_id = cvent_person_contact_id,
cvent_contact_obj = cvent_person_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_contact_id}')
return mk_resp(data=None, status_code=400, response=commons.response) # Bad Request
# 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 }
# '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'
# ### END ### API Cvent ### get_person() ###
# ### BEGIN ### API Cvent ### get_person_w_email() ###
# Updated 2022-02-02
@router.get('/person/email/{email}', response_model=Resp_Body_Base)
async def get_person_w_email(
email: str = Query(..., min_length=5, max_length=75),
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())
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)
account_id = commons.x_account_id
# ### SECTION ### Get the Cvent Contact with the email address. There should only be zero or one results.
if cvent_person_contact_list_result := get_contact_list(email=email):
log.debug(cvent_person_contact_list_result)
# cvent_person_contact_list = cvent_person_contact_list_result
else:
log.info(f'Cvent contact not found with Email: {email}')
return mk_resp(data=None, status_code=404, response=commons.response) # Not Found
cvent_contact_list = cvent_person_contact_list_result.get('data')
log.debug(json.dumps(cvent_contact_list, indent=2, default=str))
if len(cvent_contact_list) == 1:
cvent_person_contact_obj = cvent_contact_list[0]
cvent_person_contact_id = cvent_person_contact_obj.get('id')
log.info(f'Processing Cvent (Person) Contact ID: {cvent_person_contact_id}')
if create_update_aether_person_result := create_update_aether_person(
cvent_contact_id = cvent_person_contact_id,
cvent_contact_obj = cvent_person_contact_obj,
account_id = account_id,
person_id = person_id,
):
# person_obj = create_update_aether_person_result
person_id = create_update_aether_person_result
# return mk_resp(data=person_obj, status_message=f'Checked for recent changes in Cvent. Created/Updated {len(cvent_contact_list)} Cvent contacts', 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_contact_id}')
return mk_resp(data=None, status_code=400, response=commons.response,status_message=f'Something went wrong while trying to create or update the person in Aether based on Cvent data. Cvent (Person) Contact ID: {cvent_person_contact_id}') # Bad Request
else:
log.info(f'More than one result was returned with the email address. Email Address: {email}')
return mk_resp(data=None, status_code=400, response=commons.response) # Bad Request
# ### SECTION ### Return successful results
if return_obj:
person_obj = load_person_obj(
person_id = person_id,
inc_address = inc_address,
inc_contact = inc_contact,
# inc_organization = inc_organization,
inc_user = inc_user,
inc_user_role_list = inc_user_role_list,
).dict(by_alias=commons.by_alias, exclude_unset=commons.exclude_unset)
data = person_obj
else:
data = {}
data['person_id'] = person_id
data['person_id_random'] = person_id_random
return mk_resp(data=data, response=commons.response)
# ### END ### API Cvent ### get_person_w_email() ###
# ### BEGIN ### API Cvent ### get_person_w_external_id() ###
# Updated 2022-02-02
@router.get('/person/external_id/{external_id}', response_model=Resp_Body_Base)
async def get_person_w_external_id(
external_id: str = Query(..., min_length=10, max_length=100),
person_id: str = Query(None, min_length=11, max_length=22),
inc_address: bool = False,
inc_contact: bool = False,
inc_user: bool = False,
inc_user_role_list: bool = False,
return_obj: Optional[bool] = True,
commons: Common_Route_Params = Depends(common_route_params),
):
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
elif person_id is None: pass
else: return mk_resp(data=None, status_code=404, response=commons.response)
account_id = commons.x_account_id
# ### SECTION ### Get the Cvent Contact with the external ID. There should only be zero or one results.
if cvent_person_contact_list_result := get_contact_list(external_id=external_id):
log.debug(cvent_person_contact_list_result)
# cvent_person_contact_list = cvent_person_contact_list_result
else:
log.info(f'Cvent contact not found with External ID: {external_id}')
return mk_resp(data=None, status_code=404, response=commons.response) # Not Found
cvent_contact_list = cvent_person_contact_list_result.get('data')
if len(cvent_contact_list) == 1:
cvent_person_contact_obj = cvent_contact_list[0]
cvent_person_contact_id = cvent_person_contact_obj.get('id')
log.info(f'Processing Cvent (Person) Contact ID: {cvent_person_contact_id}')
if create_update_aether_person_result := create_update_aether_person(
cvent_contact_id = cvent_person_contact_id,
cvent_contact_obj = cvent_person_contact_obj,
account_id = account_id,
person_id = person_id,
):
# person_obj = create_update_aether_person_result
person_id = create_update_aether_person_result
# return mk_resp(data=person_obj, status_message=f'Checked for recent changes in Cvent. Created/Updated {len(cvent_contact_list)} Cvent contacts', 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_contact_id}')
return mk_resp(data=None, status_code=400, response=commons.response,status_message=f'Something went wrong while trying to create or update the person in Aether based on Cvent data. Cvent (Person) Contact ID: {cvent_person_contact_id}') # Bad Request
else:
log.info(f'More than one result was returned with the external ID. External ID: {email}')
return mk_resp(data=None, status_code=400, response=commons.response) # Bad Request
# ### SECTION ### Return successful results
if return_obj:
person_obj = load_person_obj(
person_id = person_id,
inc_address = inc_address,
inc_contact = inc_contact,
# inc_organization = inc_organization,
inc_user = inc_user,
inc_user_role_list = inc_user_role_list,
).dict(by_alias=commons.by_alias, exclude_unset=commons.exclude_unset)
data = person_obj
else:
data = {}
data['person_id'] = person_id
data['person_id_random'] = person_id_random
return mk_resp(data=data, response=commons.response)
# ### END ### API Cvent ### get_person_w_external_id() ###