From 2c132f5f47768f36dbdfbf5c81ad62d394b3ef7d Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 6 Jun 2023 10:24:50 -0500 Subject: [PATCH 1/3] create template copy function --- scripts/clone_organization.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/scripts/clone_organization.py b/scripts/clone_organization.py index d7d8dff..017fd0f 100755 --- a/scripts/clone_organization.py +++ b/scripts/clone_organization.py @@ -101,6 +101,7 @@ from_organization = from_api.get("me")["organizations"][0] to_organization = to_api.get("me")["organizations"][0] +# ask user to confirm changes message = f"Cloning `{from_organization['name']}` ({from_organization['id']}) organization to `{to_organization['name']}` ({to_organization['id']})..." message += '\nData from source organization will be added to the destination organization. No data will be deleted.\n\nContinue?' @@ -108,6 +109,7 @@ if confirmed not in ["yes", "y"]: exit() +# Get lead statuses, remove old ids, then post to new org if args.lead_statuses or args.statuses or args.all: print("\nCopying Lead Statuses") @@ -121,6 +123,7 @@ except APIError as e: print(f"Couldn't add `{status['label']}` because {str(e)}") +# Copy pipelines and associated opportunity statuses if args.opportunity_statuses or args.statuses or args.all: print("\nCopying Opportunity Statuses") to_pipelines = to_api.get_opportunity_pipelines() @@ -256,31 +259,26 @@ def copy_custom_fields(custom_field_type): except APIError as e: print(f"Couldn't add `{role['name']}` because {str(e)}") -if args.templates or args.email_templates or args.all: - print("\nCopying Email Templates") - templates = from_api.get_all_items('email_template') +# function to copy sms or email templates +def copy_templates(template_type): + templates = from_api.get_all_items(template_type) for template in templates: del template["id"] del template["organization_id"] try: - to_api.post("email_template", data=template) + to_api.post(template_type, data=template) print(f'Added `{template["name"]}`') except APIError as e: print(f"Couldn't add `{template['name']}` because {str(e)}") +if args.templates or args.email_templates or args.all: + print("\nCopying Email Templates") + copy_templates('email_template') + if args.templates or args.sms_templates or args.all: print("\nCopying SMS Templates") - templates = from_api.get_all_items('sms_template') - for template in templates: - del template["id"] - del template["organization_id"] - - try: - to_api.post("sms_template", data=template) - print(f'Added `{template["name"]}`') - except APIError as e: - print(f"Couldn't add `{template['name']}` because {str(e)}") + copy_templates('sms_template') # Assumes all the sequence steps (templates) were already transferred over if args.sequences or args.all: From 4534789afa1f9155585650a6d558e09e82b3ac1d Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 7 Jun 2023 11:20:36 -0500 Subject: [PATCH 2/3] Add comments --- scripts/clone_organization.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/scripts/clone_organization.py b/scripts/clone_organization.py index 017fd0f..085d1eb 100755 --- a/scripts/clone_organization.py +++ b/scripts/clone_organization.py @@ -109,7 +109,7 @@ if confirmed not in ["yes", "y"]: exit() -# Get lead statuses, remove old ids, then post to new org +# Copy lead statuses if args.lead_statuses or args.statuses or args.all: print("\nCopying Lead Statuses") @@ -178,6 +178,7 @@ def copy_custom_fields(custom_field_type): try: if from_cf['is_shared']: + # check to see if shared custom field already exists to_cf = next( ( x @@ -186,7 +187,7 @@ def copy_custom_fields(custom_field_type): ), None, ) - + # add shared custom fields that do not already exist if not to_cf: to_cf = to_api.post(f"custom_field/shared", data=from_cf) print(f'Created `{from_cf["name"]}` shared custom field') @@ -210,19 +211,22 @@ def copy_custom_fields(custom_field_type): except APIError as e: print(f"Couldn't add `{from_cf['name']}` because {str(e)}") - +# copy lead custom fields if args.lead_custom_fields or args.custom_fields or args.all: print("\nCopying Lead Custom Fields") copy_custom_fields('lead') +# copy opportunity custom fields if args.opportunity_custom_fields or args.custom_fields or args.all: print("\nCopying Opportunity Custom Fields") copy_custom_fields('opportunity') +# copy contact custom fields if args.contact_custom_fields or args.custom_fields or args.all: print("\nCopying Contact Custom Fields") copy_custom_fields('contact') +# copy integration links if args.integration_links or args.all: print("\nCopying Integration Links") integration_links = from_api.get_all_items('integration_link') @@ -236,6 +240,7 @@ def copy_custom_fields(custom_field_type): except APIError as e: print(f"Couldn't add `{link['name']}` because {str(e)}") +# copy roles if args.roles or args.all: BUILT_IN_ROLES = [ "Admin", @@ -247,6 +252,7 @@ def copy_custom_fields(custom_field_type): print("\nCopying Roles") roles = from_api.get_all_items('role') for role in roles: + # skip built in roles if role["name"] in BUILT_IN_ROLES: continue @@ -272,10 +278,12 @@ def copy_templates(template_type): except APIError as e: print(f"Couldn't add `{template['name']}` because {str(e)}") +# Copy email templates if args.templates or args.email_templates or args.all: print("\nCopying Email Templates") copy_templates('email_template') +# copy sms templates if args.templates or args.sms_templates or args.all: print("\nCopying SMS Templates") copy_templates('sms_template') @@ -283,9 +291,10 @@ def copy_templates(template_type): # Assumes all the sequence steps (templates) were already transferred over if args.sequences or args.all: print("\nCopying Sequences") - + # get existing templates in new org to_email_templates = to_api.get_all_items('email_template') to_sms_templates = to_api.get_all_items('sms_template') + # get sequences in old org from_sequences = from_api.get_all_items('sequence') for sequence in from_sequences: del sequence["id"] @@ -325,6 +334,7 @@ def copy_templates(template_type): except APIError as e: print(f"Couldn't add `{sequence['name']}` because {str(e)}") +# Copy custom activities if args.custom_activities or args.all: print("\nCopying Custom Activities") @@ -378,6 +388,7 @@ def copy_templates(template_type): from_field.pop('organization_id', None) if field["is_shared"]: + # check if field already exists to_field = next( ( x @@ -419,6 +430,7 @@ def copy_templates(template_type): from_field["custom_activity_type_id"] = new_activity_type["id"] to_api.post("custom_field/activity/", data=from_field) +# copy groups if args.groups or args.groups_with_members or args.all: print("\nCopying Groups") groups = from_api.get('group')['data'] @@ -429,6 +441,7 @@ def copy_templates(template_type): new_group = to_api.post('group', data={'name': group['name']}) if args.groups_with_members: + # copy group members for member in group['members']: try: to_api.post(f'group/{new_group["id"]}/member', data={'user_id': member['user_id']}) @@ -440,6 +453,7 @@ def copy_templates(template_type): except APIError as e: print(f"Couldn't add `{group['name']}` because {str(e)}") +# copy smart views if args.smart_views or args.all: def structured_replace(value, replacement_dictionary): @@ -475,6 +489,7 @@ def textual_replace(value, replacement_dictionary): def get_id_mappings(): + # create mapping dictionary of all custom objects accessible by smart views map_from_to_id = {} # Custom Activity Types @@ -609,7 +624,7 @@ def get_smartviews(api): print("\nCopying Smart Views") from_smart_views = get_smartviews(from_api) - # Filter our Smart Views that already exist (by name) + # Filter out Smart Views that already exist (by name) to_smart_views = get_smartviews(to_api) to_smart_view_names = [x['name'] for x in to_smart_views] from_smart_views = [x for x in from_smart_views if x['name'] not in to_smart_view_names] @@ -630,7 +645,7 @@ def get_smartviews(api): def get_memberships(api, organization): resp = api.get(f"organization/{organization['id']}", params={"_fields": "memberships,inactive_memberships"}) return resp["memberships"] + resp["inactive_memberships"] - + # map user access from old org to new org from_memberships = get_memberships(from_api, from_organization) to_memberships = get_memberships(to_api, to_organization) from_to_membership_id = {} @@ -698,6 +713,7 @@ def get_memberships(api, organization): if smart_view['s_query'] != s_query or smart_view['query'] != query: to_api.put(f"saved_search/{smart_view['id']}", data=smart_view) +# copy webhooks if args.webhooks: print("\nCopying Webhooks") webhooks = from_api.get_all_items('webhook') From abc349d1732c97d4e18575ea98352fb5c55cb4bd Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 7 Jun 2023 12:52:34 -0500 Subject: [PATCH 3/3] final cleanup and test --- scripts/clone_organization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/clone_organization.py b/scripts/clone_organization.py index 085d1eb..cec0ba2 100755 --- a/scripts/clone_organization.py +++ b/scripts/clone_organization.py @@ -2,7 +2,7 @@ from closeio_api import APIError -from scripts.CloseApiWrapper import CloseApiWrapper +from CloseApiWrapper import CloseApiWrapper arg_parser = argparse.ArgumentParser( description="Clone one organization to another"