def run(api_key, development, confirmed, limit=100): api = CloseIO_API(api_key, development=development) # loop through existing leads with multiple addresses LEADS_QUERY_WITH_MULTIPLE_ADDRESSES = "addresses > 1 sort:activities" has_more = True while has_more: resp = api.get('lead', data={ 'query': LEADS_QUERY_WITH_MULTIPLE_ADDRESSES, '_fields': 'id,addresses', '_limit': limit, }) leads = resp['data'] for lead in leads: if len(lead['addresses']) < 2: logging.warning("unexpected result: %s", lead) continue # this shouldn't happen based on the search query, but just to be safe... if confirmed: api.put('lead/' + lead['id'], data={'addresses': lead['addresses'][:1]}) logging.info("removed %d extra address(es) for %s\n%s" % (len( lead['addresses'][1:]), lead['id'], lead['addresses'][1:])) has_more = resp['has_more'] time.sleep( 2 ) # give the search indexer some time to catch up with the changes
def export_total_talk_time_per_lead_for_each_org(): """For each api key given, upload a CSV to dropbox of the total talk time per lead per user for an organization. """ global leads global calls_per_lead global user_ids_to_names global api for api_key in os.environ.get('CLOSE_API_KEYS').split(','): ## Initiate Close API leads = [] calls_per_lead = [] user_ids_to_names = {} api = CloseIO_API(api_key.strip()) try: org = api.get('me')['organizations'][0] org_name = org['name'].replace('/', ' ') org_id = org['id'] org_memberships = api.get( 'organization/' + org['id'], params={'_fields': 'memberships,inactive_memberships'}) user_ids_to_names = { k['user_id']: k['user_full_name'] for k in org_memberships['memberships'] + org_memberships['inactive_memberships'] } except APIError as e: print(f'Failed to pull org data because {str(e)} for {api_key}') continue try: name_keys = [ f'{v} Total Talk Time' for v in user_ids_to_names.values() ] name_keys = sorted(name_keys) print(f'Getting calls for {org_name}') final_calls_per_lead = _get_call_duration_per_lead() final_calls_per_lead = sorted(final_calls_per_lead, key=itemgetter('Lead Name')) except Exception as e: print(f'Failed to pull calls for {org_name} because {str(e)}') continue ordered_keys = ['Lead ID', 'Lead Name', 'Total Talk Time'] + name_keys output = io.StringIO() writer = csv.DictWriter(output, ordered_keys) writer.writeheader() writer.writerows(final_calls_per_lead) csv_output = output.getvalue().encode('utf-8') file_name = f"{org_name}/{org_name} Total Talk Time {datetime.today().strftime('%Y-%m-%d')}.csv" upload_to_dropbox(file_name, csv_output)
def room_message_hook(): data = json.loads(request.data) message = data['item']['message']['message'] room_id = data['item']['room']['id'] matched = re.match(LEAD_REGEXP, message) if matched: lead_id = matched.group(1) api = CloseIO_API(get_api_key(tenant.id)) lead = get_lead_info(api, lead_id) if lead: orga = get_orga_info(api, lead['organization_id']) notification = render_template('lead.html', lead=lead, orga=orga) room_client = RoomClient(room_id) room_client.send_notification(notification) return '', 204
args = parser.parse_args() """ Detect duplicate leads and merge them. Duplicate criteria: - Case insensitive exact match by Company Name Priority (how to choose 'Destination lead'): - Prefers leads with Opportunities over ones without. - If both or neither have opportunities, prefer leads with desired_status specified below. """ desired_status = 'open' # capitalization doesn't matter api = CloseIO_API(args.api_key, development=args.development) has_more = True offset = 0 last_lead = None total_merged = 0 while has_more: leads_merged_this_page = 0 # Get a page of leads resp = api.get('lead', data={ 'query': 'sort:display_name', '_skip': offset, '_fields': 'id,display_name,name,status_label,opportunities,custom' }) leads = resp['data']
) parser.add_argument( '--lead-id', '-l', help= 'Use this field if you want to narrow your search to a specific lead_id', ) parser.add_argument( '--user-id', '-u', help= 'Use this field if you want to narrow your search to changes done by a specific user', ) args = parser.parse_args() api = CloseIO_API(args.api_key) org_id = api.get('me')['organizations'][0]['id'] org = api.get( 'organization/' + org_id, params={ '_fields': 'id,name,memberships,inactive_memberships,lead_custom_fields' }, ) org_name = org['name'].replace('/', "") org_memberships = org['memberships'] + org['inactive_memberships'] try: custom_field_name = [ i for i in org['lead_custom_fields'] if i['id'] == args.custom_field ][0]['name'] except IndexError as e:
def run(api_key, confirmed, development=False, use_existing_contact=False, new_contact_name='', phones_custom_field='all phones', emails_custom_field='all emails'): """ After an import from a different CRM, for all leads, move emails and phones that were put in in a lead custom field to the lead's first contact (if--use_existing_contact flag was used) or create a new contact. """ print 'confirmed:', `confirmed` print 'phones_custom_field:', `phones_custom_field` print 'emails_custom_field:', `emails_custom_field` print 'use_existing_contact:', `use_existing_contact` api = CloseIO_API(api_key, development=development) has_more = True offset = 0 while has_more: # Get a page of leads resp = api.get('lead', data={ 'query': '"custom.Source CRM":* not "custom.Migration completed":* sort:created', '_skip': offset, '_fields': 'id,display_name,name,contacts,custom', }) leads = resp['data'] for lead in leads: contacts = lead['contacts'] custom = lead['custom'] company_emails = custom.get(emails_custom_field, '') company_phones = custom.get(phones_custom_field, '') if not company_phones and not company_emails: continue if company_emails : if company_emails.startswith('["'): company_emails = company_emails[2:-2].split('", "') else: company_emails = [company_emails] if company_phones: if company_phones.startswith('["'): company_phones = company_phones[2:-2].split('", "') else: company_phones = [company_phones] if contacts and use_existing_contact: contact = contacts[0] else: contact = { 'lead_id': lead['id'], 'phones': [], 'emails': [] } if new_contact_name: contact['name'] = new_contact_name for pn in company_phones: contact['phones'].append({ 'type': 'office', 'phone': pn }) for e in company_emails: contact['emails'].append({ 'type': 'office', 'email': e }) print 'Lead:', lead['id'], lead['name'].encode('utf8') print 'Emails:', `custom.get(emails_custom_field)`, ' => ', `company_emails` print 'Phones:', `custom.get(phones_custom_field)`, ' => ', `company_phones` try: if contact.get('id'): print 'Updating an existing contact', contact['id'] if confirmed: api.put('contact/%s' % contact['id'], data={ 'phones': contact['phones'], 'emails': contact['emails'], }) else: print 'Creating a new contact' if confirmed: api.post('contact', data=contact) print 'Payload:', contact if confirmed: api.put('lead/%s' % lead['id'], data={ 'custom.Migration completed': 'Yes' }) except APIError as e: print e print 'Payload:', contact if confirmed: api.put('lead/%s' % lead['id'], data={ 'custom.Migration completed': 'skipped' }) print '' if not confirmed: # If we don't actually update the "Migration completed" custom field, # we need to paginate offset += len(leads) has_more = resp['has_more'] print 'Done'
def run_import(api_key, list_type): import_status = '' api = CloseIO_API(api_key) # Try API Key try: test_api_key = api.get('me') except: import_status = 'bad_api_key' return import_status ### Get Close.io Custom Fields print "...Getting Current Custom Fields" current_custom_fields = {} has_more = True offset = 0 while has_more: resp = api.get('custom_fields/lead', params={ '_skip': offset, '_fields': 'id,name' }) for field in resp['data']: current_custom_fields[field['name']] = field['id'] offset += len(resp['data']) has_more = resp['has_more'] ### Create Custom Fields print "...Creating Any Missing Custom Fields" new_custom_fields = [ { "name": "Lead Source (Sample)", "type": "text" }, { "name": "Industry (Sample)", "type": "text" }, { "name": "List Type (Sample)", "type": "text" } ] custom_field_ids = [] for custom_field in new_custom_fields: if custom_field['name'] in current_custom_fields: custom_field_ids.append("custom.{}".format(current_custom_fields[custom_field['name']])) else: try: create_custom_field = api.post('custom_fields/lead', data=custom_field) custom_field_ids.append("custom.{}".format(create_custom_field['id'])) print "... {} Custom Field added".format(custom_field['name']) except: import_status = 'custom_field_error' return import_status break ### Import Sample Leads print "... Creating Sample Leads" sample_lead_data = generate_lead_data(custom_field_ids=custom_field_ids, list_type=list_type) for lead in sample_lead_data: try: lead = api.post('lead', data=lead) print "... Importing {}".format(lead['name']) except: continue ### Get Current Close.io Smart views print "...Getting Current Smart Views" current_smart_views = {} has_more = True offset = 0 while has_more: resp = api.get('saved_search', params={ '_skip': offset, '_fields': 'id,name' }) for query in resp['data']: current_smart_views[query['name']] = query['id'] offset += len(resp['data']) has_more = resp['has_more'] ### Create Smart Views print "...Creating Any Missing Smart Views" smart_view_queries = [ { 'name': '[SAMPLE] {} Leads w/ Phone and Email'.format(list_type), 'query': '"custom.Lead Source (Sample)":"Sample Lead Importer" "custom.List Type (Sample)":"{}" has:phone_numbers has:email_addresses sort:display_name'.format(list_type) }, { 'name': '[SAMPLE] {} Sample Leads'.format(list_type), 'query': '"custom.Lead Source (Sample)":"Sample Lead Importer" "custom.List Type (Sample)":"{}" sort:display_name'.format(list_type) } ] for query in smart_view_queries: if query['name'] not in current_smart_views: try: create_smart_view = api.post('saved_search', data=query) print "... {} Smart View added".format(query['name']) except: import_status = 'smartview_create_error' return import_status break import_status = 'https://app.close.io/search/%22custom.Lead%20Source%20(Sample)%22%3A%22Sample%20Lead%20Importer%22%20has%3Aphone_numbers%20has%3Aemail_addresses/' return import_status