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', params={ '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 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
'assigned_to': from_user_id, '_order_by': 'date_created', '_skip': offset, '_fields': 'id' } if not args.all_tasks: payload['is_complete'] = False resp = api.get('task', data=payload) tasks = resp['data'] for task in tasks: if args.confirmed: try: api.put('task/'+task['id'], data={'assigned_to': to_user_id}) except APIError as e: tasks_errors += 1 if not args.continue_on_error: raise e logging.error('task %s skipped with error %s' % (task['id'], e)) logging.info('updated %s' % task['id']) updated_tasks += 1 offset += len(tasks) has_more = resp['has_more'] # opportunities updated_opportunities = 0 if args.opportunities or args.all_opportunities: has_more = True
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'
print 'Status not found: {0}'.format(args.status) sys.exit(1) new_status_id = new_status_id[0] print 'Gathering opportunities for {0}'.format(args.query) has_more = True offset = 0 limit = 50 opp_ids = [] while has_more: resp = api.get('lead', data={'_skip': offset, '_limit': limit, 'query': args.query}) opp_ids.extend([opp['id'] for lead in resp['data'] for opp in lead['opportunities']]) has_more = resp['has_more'] offset += limit ans = raw_input('{0} opportunities found. Do you want to update all of them to {1}? (y/n): '.format(len(opp_ids), args.status)) if ans.lower() != 'y': sys.exit(0) print 'Updating opportunities to {0}'.format(args.status) # Update opps for opp_id in opp_ids: resp = api.put('opportunity/{0}'.format(opp_id), data={'status_id': new_status_id}) print 'Done!'
tag_templates[r['tag'].lower()] = (r['custom_field_name'], r['custom_field_value']) api = CloseIO_API(args.api_key, development=args.development) has_more = True offset = 0 while has_more: resp = api.get('lead', data={ 'query': 'custom.Tags:* sort:created', '_skip': offset, '_fields': 'id,custom' }) leads = resp['data'] for l in leads: if 'Tags' in l['custom'].keys(): tags = [t.strip() for t in l['custom']['Tags'].split(',')] new_fields = {} for t in tags: t_lower = t.lower() if t_lower in tag_templates.keys(): new_fields['custom.' + tag_templates[t_lower][0]] = tag_templates[t_lower][1] print l['id'], 'Tags:', l['custom']['Tags'] print '...', new_fields api.put('lead/'+l['id'], data=new_fields) offset += max(0, len(leads) - 1) has_more = resp['has_more']
resp = api.get('task', params=payload) tasks = resp['data'] for task in tasks: if args.confirmed: full_tasks.append(task['id']) else: logging.info(f'updated {task["id"]}') updated_tasks += 1 offset += len(tasks) has_more = resp['has_more'] for task_id in full_tasks: try: api.put('task/' + task_id, data={'assigned_to': to_user_id}) logging.info(f'updated {task_id}') updated_tasks += 1 except APIError as e: tasks_errors += 1 if not args.continue_on_error: raise e logging.error(f'task {task["id"]} skipped with error {str(e)}') # opportunities updated_opportunities = 0 if args.opportunities or args.all_opportunities: has_more = True offset = 0 while has_more: payload = {
ISO_COUNTRIES[args.new_code])) api = CloseIO_API(args.api_key, development=args.development) has_more = True offset = 0 while has_more: resp = api.get('lead', params={ 'query': LEADS_QUERY, '_skip': offset, '_fields': 'id,addresses' }) leads = resp['data'] for lead in leads: need_update = False for address in lead['addresses']: if address['country'] == args.old_code: address['country'] = args.new_code need_update = True if need_update: if args.confirmed: api.put('lead/' + lead['id'], data={'addresses': lead['addresses']}) logging.info('updated %s' % lead['id']) offset += len(leads) has_more = resp['has_more']
from closeio_api.utils import CsvReader parser = argparse.ArgumentParser(description='Remove email addresses from contacts in CSV file') parser.add_argument('--api-key', '-k', required=True, help='API Key') parser.add_argument('--confirmed', action='store_true', help='Really run this?') parser.add_argument('file', help='Path to the csv file') args = parser.parse_args() reader = CsvReader(args.file) headers = dict([(name, idx,) for idx, name in enumerate(reader.next())]) # skip the 1st line header if any(field not in headers for field in ['contact_id', 'email_address']): print 'contact_id or email_address headers could not be found in your csv file.' sys.exit(-1) api = CloseIO_API(args.api_key, async=False) for row in reader: contact_id = row[headers['contact_id']] email_address = row[headers['email_address']] try: contact = api.get('contact/' + contact_id) if not contact['emails']: continue emails = filter(lambda email: email['email'] != email_address, contact['emails']) if args.confirmed: resp = api.put('contact/' + contact_id, {'emails': emails}) except APIError: pass
'direction': activity['direction'], 'duration': activity['duration'], 'voicemail_duration': activity['voicemail_duration'], 'note': activity['note'], 'source': activity['source'], 'status': activity['status'], 'remote_phone': activity['remote_phone'], 'phone': activity['phone'], 'local_phone': activity['local_phone'], 'recording_url': activity['recording_url'], 'date_created': activity['date_created'], 'date_updated': activity['date_updated'] } if activity['user_id'] in target_user_ids: new_call['user_id'] = activity['user_id'] if activity['transferred_from'] in target_user_ids: new_call['transferred_from'] = activity['transferred_from'] if activity['transferred_to'] in target_user_ids: new_call['transferred_to'] = activity['transferred_to'] target_api.post('activity/call', data=new_call) logging.info( 'target: %s added call: %s duration: %s' % (new_lead['id'], activity['phone'], activity['duration'])) if args.delete: api.put('lead/' + lead['id']) logging.info('deleted source lead %s' % (lead['id'], ))
for row in reader: contact_id = row['contact_id'] email_address = row['email_address'] if args.verbose: print(f'Attempting to remove {email_address} from {contact_id}') try: contact = api.get('contact/' + contact_id) if not contact['emails']: if args.verbose: print( f'Skipping {contact_id} because it has no email addresses') continue emails = list( filter( lambda email: email['email'] != email_address, contact['emails'], )) if args.confirmed: resp = api.put('contact/' + contact_id, {'emails': emails}) if args.verbose: print(f'Removed {email_address} from {contact_id}') except APIError as e: if args.verbose: print( f'Encountered an API error ({e.response.status_code}): {e.response.text}' )
choices_data[field_name].extend(field["choices"]) if action == "create": print "Error: " + field_name + " already exists. Choose another name or use update or replace." sys.exit(1) elif field["type"] != "choices": print "Error: " + field_name + " does not have a choices field type." sys.exit(1) if not field_id and action in ("update", "replace"): print "Error: " + field_name + " not found. Remove --update if you wish to create a new field with this name." sys.exit(1) # Only runs against the org if --confirmed is used. # Uses post for creating and put for update/replace if args.confirmed: if action == "create": api.post( "custom_fields/lead", data={"name": field_name, "type": "choices", "choices": choices_data[field_name]} ) print "Successfully created " + field_name + "!" else: api.put("custom_fields/lead/" + field_id, data={"choices": choices_data[field_name]}) if action == "update": print "Successfully updated existing values for " + field_name + "!" else: print "Successfully replaced existing values for " + field_name + "!" else: print "No problems detected with " + field_name + "!" print "Use --confirmed if you wish to run these changes against your Close.io org."
resp = api.get( "lead", params={ "query": 'company:"%s" sort:created' % r["company"], "_fields": "id,display_name,name,contacts,custom", "limit": 1, }, ) logging.debug("received: %s" % resp) if resp["total_results"]: lead = resp["data"][0] if lead: logging.debug("to sent: %s" % payload) if args.confirmed: api.put("lead/" + lead["id"], data=payload) logging.info( "line %d updated: %s %s" % (c.line_num, lead["id"], lead.get("name") if lead.get("name") else "") ) updated_leads += 1 # new lead elif lead is None and not args.disable_create: logging.debug("to sent: %s" % payload) if args.confirmed: lead = api.post("lead", data=payload) logging.info("line %d new: %s %s" % (c.line_num, lead["id"] if args.confirmed else "X", payload["name"])) new_leads += 1 notes = [r[x] for x in r.keys() if re.match(r"note[0-9]", x) and r[x]] for note in notes: if args.confirmed:
lead = resp else: # first lead in the company resp = api.get('lead', data={ 'query': 'company:"%s" sort:created' % r['company'], '_fields': 'id,display_name,name,contacts,custom', 'limit': 1 }) logging.debug('received: %s' % resp) if resp['total_results']: lead = resp['data'][0] if lead: logging.debug('to sent: %s' % payload) if args.confirmed: api.put('lead/' + lead['id'], data=payload) logging.info('line %d updated: %s %s' % (c.line_num, lead['id'], lead.get('name') if lead.get('name') else '')) updated_leads += 1 # new lead elif lead is None and not args.disable_create: logging.debug('to sent: %s' % payload) if args.confirmed: lead = api.post('lead', data=payload) logging.info('line %d new: %s %s' % (c.line_num, lead['id'] if args.confirmed else 'X', payload['name'])) new_leads += 1 notes = [r[x] for x in r.keys() if re.match(r'note[0-9]', x) and r[x]]
parser.add_argument('--development', action='store_true', help='Use a development server rather than production.') args = parser.parse_args() api = CloseIO_API(args.api_key, development=args.development) skip = 0 has_more = True while has_more: resp = api.get('lead', data={'_skip': skip}) leads = resp['data'] for lead in leads: n_fields_deleted = 0 custom = lead['custom'].copy() for field in DELETE_FIELDS: if custom.get(field): del custom[field] n_fields_deleted += 1 if n_fields_deleted: print "LEAD: %s" % lead['id'] print "\tBEFORE", lead['custom'] print "\tAFTER", custom print api.put('lead/' + lead['id'], data={'custom': custom}) skip += len(leads) has_more = resp['has_more']
'query': 'company:"%s" sort:created' % r['company'].decode('utf-8'), '_fields': 'id,display_name,name,contacts,custom', 'limit': 1 }) logging.debug('received: %s' % resp) if resp['total_results']: lead = resp['data'][0] if lead: logging.debug('to sent: %s' % payload) if args.confirmed: api.put('lead/' + lead['id'], data=payload) logging.info('line %d updated: %s %s' % (c.line_num, lead['id'], lead.get('name') if lead.get('name') else '')) updated_leads += 1 # new lead elif lead is None and not args.disable_create: logging.debug('to sent: %s' % payload) if args.confirmed: lead = api.post('lead', data=payload) logging.info('line %d new: %s %s' % (c.line_num, lead['id'] if args.confirmed else 'X', payload['name'].decode('utf-8'))) new_leads += 1 notes = [r[x] for x in r.keys() if re.match(r'note[0-9]', x) and r[x]]
offset = 0 limit = 50 opp_ids = [] while has_more: resp = api.get('lead', data={ '_skip': offset, '_limit': limit, 'query': args.query }) opp_ids.extend( [opp['id'] for lead in resp['data'] for opp in lead['opportunities']]) has_more = resp['has_more'] offset += limit ans = raw_input( '{0} opportunities found. Do you want to update all of them to {1}? (y/n): ' .format(len(opp_ids), args.status)) if ans.lower() != 'y': sys.exit(0) print 'Updating opportunities to {0}'.format(args.status) # Update opps for opp_id in opp_ids: resp = api.put('opportunity/{0}'.format(opp_id), data={'status_id': new_status_id}) print 'Done!'
help='Use a development server rather than production.') args = parser.parse_args() api = CloseIO_API(args.api_key, development=args.development) skip = 0 has_more = True while has_more: resp = api.get('lead', data={'_skip': skip}) leads = resp['data'] for lead in leads: n_fields_deleted = 0 custom = lead['custom'].copy() for field in DELETE_FIELDS: if custom.get(field): del custom[field] n_fields_deleted += 1 if n_fields_deleted: print "LEAD: %s" % lead['id'] print "\tBEFORE", lead['custom'] print "\tAFTER", custom print api.put('lead/' + lead['id'], data={'custom': custom}) skip += len(leads) has_more = resp['has_more']
logging.info('old country: %s (%s) -> new country: %s (%s) ' % (args.old_code, ISO_COUNTRIES[args.old_code], args.new_code, ISO_COUNTRIES[args.new_code])) api = CloseIO_API(args.api_key, development=args.development) has_more = True offset = 0 while has_more: resp = api.get('lead', data={ 'query': LEADS_QUERY, '_skip': offset, '_fields': 'id,addresses' }) leads = resp['data'] for lead in leads: need_update = False for address in lead['addresses']: if address['country'] == args.old_code: address['country'] = args.new_code need_update = True if need_update: if args.confirmed: api.put('lead/'+lead['id'], data={'addresses': lead['addresses']}) logging.info('updated %s' % lead['id']) offset += len(leads) has_more = resp['has_more']
'sequence_id': sequence['id'] }, ) from_subs += [ i for i in sub_results['data'] if i['sender_email'] == args.from_email and i['status'] in ['active', 'paused', 'error', 'goal'] ] offset += len(sub_results['data']) has_more = sub_results['has_more'] print(offset) print(f"Total subscriptions: {len(from_subs)}") print("Updating subscriptions") count = 0 for sub in from_subs: try: api.put( f"sequence_subscription/{sub['id']}", data={ 'sender_name': args.sender_name, 'sender_account_id': args.sender_account_id, 'sender_email': args.to_email, }, ) count += 1 print(f"{count}: {sub['id']}") except APIError as e: print(f"Can't update sequence {sub['id']} because {str(e)}")
parser.add_argument('--to-email', '-t', required=True, help='Email address you want to use to send sequence') parser.add_argument('--sender-account-id', '-s', required=True, help='Email account id you want to use to send sequence') parser.add_argument('--sender-name', '-n', required=True, help='Sender name you want to use to send sequence') args = parser.parse_args() api = CloseIO_API(args.api_key) has_more = True offset = 0 from_subs = [] count = 0 print "Getting sequence subscriptions" while has_more: sub_results = api.get('sequence_subscription', params={ '_skip': offset, 'fields': 'id,sender_email,sender_name,sender_account_id,status' }) from_subs += [i for i in sub_results['data'] if i['sender_email'] == args.from_email and i['status'] in ['active', 'paused']] offset += len(sub_results['data']) print offset has_more = sub_results['has_more'] print "Total subscriptions: %s" % len(from_subs) print "Updating subscriptions" for sub in from_subs: try: api.put('sequence_subscription/' + sub['id'], data={ 'sender_name': args.sender_name, 'sender_account_id': args.sender_account_id, 'sender_email': args.to_email }) count += 1 print "%s: %s" % (count, sub['id']) except APIError as e: print "Can't update sequence %s because %s" % (sub['id'], str(e))