def resources_to_contacts(): # Get Calendar Resources calendars = calendar_resource(options=options()).get_resource_feed( uri=options().calendar_resource_feed).entry # Select Calendars by options filtered_calendars = filter(lambda cal: \ fnmatch(cal.resource_email, options().select_pattern), calendars) # Fetch all domain users all_users = exhaust( admin(options=options()).users().list, dict(domain=options().domain, maxResults=500), 'users') # Get opt-out lists optout_emails_set = set() if not options().undo else get_optout_set( options().optout_uri) # Select domain users by options filtered_users = filtermap(lambda user: fnmatch(user['primaryEmail'], options().user_pattern) and \ unicode(user['primaryEmail']).lower() not in optout_emails_set, iget('primaryEmail'), all_users) logging.info( 'Starting Calendar Resource to Contacts Group copy operation. Selection is "%s" (%d calendar(s)) and target is "%s" (%d user(s))', options().select_pattern, len(filtered_calendars), options().user_pattern, len(filtered_users)) process_users(filtered_users, filtered_calendars)
def grab(user): if fnmatch(user[u'primaryEmail'], options().user_pattern): target_user_emails.append(user[u'primaryEmail']) if fnmatch(user[u'primaryEmail'], options().select_pattern) and \ (not options().phone or ( \ u'phones' in user and \ any([ u'value' in phone and phone[u'value'] for phone in user[u'phones'] ]) ) \ ) and get_ldap_id_json(user): users_to_copy.append(user)
def json_to_organization_object(json): org_object = gdata.data.Organization() set_rel_or_label(org_object, json) if u'name' in json: org_object.name = gdata.data.OrgName(json[u'name']) elif options().organization_name: org_object.name = gdata.data.OrgName(options().organization_name) if u'title' in json: org_object.title = gdata.data.OrgTitle(json[u'title']) if u'department' in json: org_object.department = gdata.data.OrgDepartment(json[u'department']) if u'symbol' in json: org_object.symbol = gdata.data.OrgSymbol(json[u'symbol']) return org_object
def create_magic_group(contacts_client): logging.info('Creating magic group: {}'.format(options().group)) new_group = GroupEntry() new_group.title = Title(options().group) extprop = ExtendedProperty() extprop.name = options().group_extended_property_name extprop.value = options().group_extended_property_value new_group.extended_property.append(extprop) return contacts_client.create_group(new_group=new_group)
def add_suffix(contact): if contact.name.name_suffix and contact.name.name_suffix.text: old_suffix = contact.name.name_suffix.text + " " else: old_suffix = "" contact.name.name_suffix = gdata.data.NameSuffix(old_suffix + options().rename_suffix) contact.name.full_name = gdata.data.FullName(contact.name.full_name.text + " " + options().rename_suffix) # Add ext prop to signal that this contact has been renamed by the script extprop = ExtendedProperty() extprop.name = options().contact_renamed_extended_property_name extprop.value = options().contact_renamed_extended_property_value contact.extended_property.append(extprop)
def json_to_external_id_object(json): ext_id_object = gdata.contacts.data.ExternalId() ext_id_object.value = json[u'value'] if u'type' in json: if json[u'type'] == u'custom': if u'customType' in json and json[u'customType']: ext_id_object.label = json[u'customType'] else: ext_id_object.rel = options().default_external_id_rel else: ext_id_object.rel = json[u'type'] else: ext_id_object.rel = options().default_external_id_rel return ext_id_object
def main_logging(): if options().delete_old and options().rename_old: sys.exit("Conflicting options detected, aborting") optout_emails_set = get_optout_set(options().optout_uri) users_to_copy, target_user_emails = select_users() target_user_emails = filter(lambda email: email.lower() not in optout_emails_set, target_user_emails) user_to_copy_by_ldap_dict = dict(zip(map(get_ldap_id_json, users_to_copy), users_to_copy)) logging.info('Starting Directory to Contacts Group copy operation. Selection is "%s" (%d user(s)) and target is "%s" (%d user(s))', options().select_pattern, len(users_to_copy), options().user_pattern, len(target_user_emails)) for target_user_email in target_user_emails: process_target_user(target_user_email, users_to_copy, user_to_copy_by_ldap_dict)
def get_group_members(contacts_client, group): if not group: return [] contacts_query = ContactsQuery() contacts_query.group = group.id.text contacts_query.max_results = options().max_contacts return contacts_client.get_contacts(q=contacts_query).entry
def main(): if options().delete_old and options().rename_old: sys.exit("Conflicting options detected, aborting") users_to_copy, target_user_emails = select_users() user_to_copy_by_ldap_dict = dict(zip(map(get_ldap_id_json, users_to_copy), users_to_copy)) logging.info('Starting Directory to Contacts Group copy operation. Selection is "%s" (%d user(s)) and target is "%s" (%d user(s))', options().select_pattern, len(users_to_copy), options().user_pattern, len(target_user_emails)) for target_user_email in target_user_emails: # Gdata api sometimes return invalid XML. IF that happens, we get ParseError # Detect that and continue from next user. The error likely won't happen the next time try: process_target_user(target_user_email, users_to_copy, user_to_copy_by_ldap_dict) except ParseError: logging.exception("Encountered error while processing user {}, continuing".format(target_user_email))
def process_user(target_user, filtered_calendars): filtered_calendar_by_email_dict = dict(zip(map(get('resource_email'), filtered_calendars), filtered_calendars)) contacts_client = contacts(email=target_user, options=options()) if options().undo: undo(contacts_client, target_user, ContactsFeed) return # Get Contacts Groups for user groups = contacts_client.get_groups().entry # Find Contact Group by extended property magic_group = get_magic_group(groups) or create_magic_group(contacts_client) magic_group_members = get_group_members(contacts_client, magic_group) magic_group_emails_set = map(get('address'), flatmap(get('email'), magic_group_members)) # Find "My Contacts" group in Contacts my_contacts_group = next(iter( filter(lambda group: group.system_group and group.system_group.id == options().my_contacts_id, groups)), None) logging.info('%s: Using group called "%s" with %d members and ID %s', target_user, magic_group.title.text, len(magic_group_members), magic_group.id.text) # Add new Calendar Resources as Contacts with closing(Batch(contacts_client, ContactsFeed)) as batch: for cal in filter(lambda x: \ x.resource_email not in magic_group_emails_set, filtered_calendars): new_contact = calendar_resource_to_contact(cal) # Add Contact to the relevant groups new_contact.group_membership_info.append(GroupMembershipInfo(href=magic_group.id.text)) if options().my_contacts and my_contacts_group: new_contact.group_membership_info.append(GroupMembershipInfo(href=my_contacts_group.id.text)) # Set Contact extended property extprop = ExtendedProperty() extprop.name = options().contact_extended_property_name extprop.value = options().contact_extended_property_value new_contact.extended_property.append(extprop) logging.debug('%s: Creating contact "%s"', target_user, new_contact.name.full_name.text) batch.put('add_insert', new_contact) # Sync data for existing Calendar Resources that were added by the script. Remove those that have been deleted with closing(Batch(contacts_client, ContactsFeed)) as batch: for existing_contact in filter(is_script_contact, magic_group_members): calendar_resource_to_copy = get_value_by_contact_email(filtered_calendar_by_email_dict, existing_contact) if calendar_resource_to_copy: calendar_contact = calendar_resource_to_contact(calendar_resource_to_copy) if sync_contact(calendar_contact, existing_contact): logging.info('%s: Modifying contact "%s" with ID %s', target_user, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_update', existing_contact) elif options().delete_old: logging.info('%s: Removing surplus auto-generated contact "%s" with ID %s', target_user, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_delete', existing_contact)
def remove_suffix(contact): contact.name.name_suffix = None contact.name.full_name = gdata.data.FullName(contact.name.given_name.text + " " + contact.name.family_name.text) contact.extended_property = [ extprop for extprop in contact.extended_property if extprop.name != options().contact_renamed_extended_property_name ]
def calendar_resource_to_contact(calendar): """Converts a Calendar Resource to a Contact.""" contact = ContactEntry() contact.name = Name( given_name=GivenName(text=calendar.resource_common_name), family_name=FamilyName(text=options().family_name), full_name=FullName(text=calendar.resource_common_name)) contact.content = Content(text=calendar.resource_description) contact.email.append(Email(address=calendar.resource_email, primary='true', display_name=calendar.resource_common_name, rel=DEFAULT_REL)) return contact
def select_users(): users_to_copy = [] target_user_emails = [] def grab(user): if fnmatch(user[u'primaryEmail'], options().user_pattern): target_user_emails.append(user[u'primaryEmail']) if fnmatch(user[u'primaryEmail'], options().select_pattern) and \ (not options().phone or ( \ u'phones' in user and \ any([ u'value' in phone and phone[u'value'] for phone in user[u'phones'] ]) ) \ ) and get_ldap_id_json(user): users_to_copy.append(user) users = exhaust( admin(options=options()).users().list, dict(domain=options().domain, maxResults=500), 'users') filter(grab, users) return (users_to_copy, target_user_emails)
def main_logging(): if options().delete_old and options().rename_old: sys.exit("Conflicting options detected, aborting") optout_emails_set = get_optout_set(options().optout_uri) users_to_copy, target_user_emails = select_users() target_user_emails = filter( lambda email: email.lower() not in optout_emails_set, target_user_emails) user_to_copy_by_ldap_dict = dict( zip(map(get_ldap_id_json, users_to_copy), users_to_copy)) logging.info( 'Starting Directory to Contacts Group copy operation. Selection is "%s" (%d user(s)) and target is "%s" (%d user(s))', options().select_pattern, len(users_to_copy), options().user_pattern, len(target_user_emails)) for target_user_email in target_user_emails: process_target_user(target_user_email, users_to_copy, user_to_copy_by_ldap_dict)
def calendar_resource_to_contact(calendar): """Converts a Calendar Resource to a Contact.""" contact = ContactEntry() contact.name = Name( given_name=GivenName(text=calendar.resource_common_name), family_name=FamilyName(text=options().family_name), full_name=FullName(text=calendar.resource_common_name)) contact.content = Content(text=calendar.resource_description) contact.email.append( Email(address=calendar.resource_email, primary='true', display_name=calendar.resource_common_name, rel=DEFAULT_REL)) return contact
def resources_to_contacts(): # Get Calendar Resources calendars = calendar_resource(options=options()).get_resource_feed(uri=options().calendar_resource_feed).entry # Select Calendars by options filtered_calendars = filter(lambda cal: \ fnmatch(cal.resource_email, options().select_pattern), calendars) # Fetch all domain users all_users = exhaust(admin(options=options()).users().list, dict(domain=options().domain, maxResults=500), 'users') # Get opt-out lists optout_emails_set = set() if not options().undo else get_optout_set(options().optout_uri) # Select domain users by options filtered_users = filtermap(lambda user: fnmatch(user['primaryEmail'], options().user_pattern) and \ unicode(user['primaryEmail']).lower() not in optout_emails_set, iget('primaryEmail'), all_users) logging.info('Starting Calendar Resource to Contacts Group copy operation. Selection is "%s" (%d calendar(s)) and target is "%s" (%d user(s))', options().select_pattern, len(filtered_calendars), options().user_pattern, len(filtered_users)) process_users(filtered_users, filtered_calendars)
def is_renamed_contact(contact): return any(filter( lambda prop: prop.name == options().contact_renamed_extended_property_name \ and prop.value == options().contact_renamed_extended_property_value, contact.extended_property))
def is_script_group(group): return any(filter( lambda prop: prop.name == options().group_extended_property_name \ and prop.value == options().group_extended_property_value, group.extended_property))
def json_to_contact_object(json): new_contact = ContactEntry() # Set the contact name new_contact.name = gdata.data.Name( given_name=gdata.data.GivenName(text=json[u'name'][u'givenName']), family_name=gdata.data.FamilyName(text=json[u'name'][u'familyName']), full_name=gdata.data.FullName(text=json[u'name'][u'fullName'])) # Set the contact email address new_contact.email.append(gdata.data.Email(address=json[u'primaryEmail'], primary='true', display_name=json[u'name'][u'fullName'], rel=DEFAULT_REL)) # Add aliases if options().add_aliases: if u'aliases' in json: for alias in json[u'aliases']: new_contact.email.append(gdata.data.Email(address=alias, primary='false', display_name=json[u'name'][u'fullName'], rel=DEFAULT_REL)) if u'nonEditableAliases' in json: for alias in json[u'nonEditableAliases']: new_contact.email.append(gdata.data.Email(address=alias, primary='false', display_name=json[u'name'][u'fullName'], rel=DEFAULT_REL)) # Add other emails if options().add_other_emails and u'emails' in json: for json_email in json[u'emails']: if u'address' in json_email: email_object = json_to_email_object(json_email) email_object.display_name = json[u'name'][u'fullName'] email_object.primary = 'false' new_contact.email.append(email_object) # Add organization (job title) info if u'organizations' in json and len(json[u'organizations']) > 0: for json_org in json[u'organizations']: if u'primary' in json_org and json_org[u'primary']: primary_org = json_org break else: primary_org = json[u'organizations'][0] org_object = json_to_organization_object(primary_org) org_object.primary = "true" new_contact.organization = org_object elif options().organization_name: # Add at least our org name org_object = gdata.data.Organization() # the API requires exactly one of 'rel' or 'label' org_object.rel = DEFAULT_REL org_object.name = gdata.data.OrgName(options().organization_name) org_object.primary = "true" new_contact.organization = org_object if u'phones' in json: for json_phone in json[u'phones']: if u'value' in json_phone: new_contact.phone_number.append(json_to_phone_number_object(json_phone)) if u'externalIds' in json: for json_external_id in json[u'externalIds']: if u'value' in json_external_id: new_contact.external_id.append(json_to_external_id_object(json_external_id)) if u'addresses' in json: for json_address in json[u'addresses']: if u'formatted' in json_address and json_address[u'formatted']: new_contact.postal_address.append(json_to_postal_address_object(json_address)) if u'ims' in json: for json_im in json[u'ims']: if u'im' in json_im: new_contact.im.append(json_to_im_object(json_im)) return new_contact
def process_target_user(target_user_email, users_to_copy, user_to_copy_by_ldap_dict): contacts_client = contacts(email=target_user_email, options=options()) if options().undo: undo(contacts_client, target_user_email, ContactsFeed) return users_groups = contacts_client.get_groups().entry # Find group by extended property magic_group = get_magic_group(users_groups) or create_magic_group(contacts_client) magic_group_members = get_group_members(contacts_client, magic_group) magic_group_ldaps_set = lambda: filter(None, [ get_ldap_id_contact(contact) for contact in magic_group_members ]) # Find "My Contacts" group in Contacts my_contacts_group = next(iter( filter(lambda group: group.system_group and group.system_group.id == options().my_contacts_id, users_groups)), None) logging.info('%s: Using group called "%s" with %d members and ID %s', target_user_email, magic_group.title.text, len(magic_group_members), magic_group.id.text) # remove all existing contacts if options().delete_contacts: with closing(Batch(contacts_client, ContactsFeed)) as batch: for existing_contact in filter(is_script_contact, magic_group_members): logging.info('%s: Removing contact "%s" with ID %s', target_user_email, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_delete', existing_contact) # Check dangling entries in scripted group (extended_property only held 'google_apps_sync' Employee ID) with closing(Batch(contacts_client, ContactsFeed)) as batch: for dangling_contact in filter(lambda contact: not is_script_contact(contact), magic_group_members): logging.info('%s: Removing dangling contact "%s" with ID %s', target_user_email, dangling_contact.name.full_name.text, dangling_contact.id.text) batch.put('add_delete', dangling_contact) # Add new users (not already in the group) as contacts with closing(Batch(contacts_client, ContactsFeed)) as batch: for user_to_copy in users_to_copy: if get_ldap_id_json(user_to_copy) not in magic_group_ldaps_set(): new_contact = json_to_contact_object(user_to_copy) # Add the relevant groups new_contact.group_membership_info.append(GroupMembershipInfo(href=magic_group.id.text)) if options().my_contacts and my_contacts_group: new_contact.group_membership_info.append(GroupMembershipInfo(href=my_contacts_group.id.text)) # Set extended properties new_contact.extended_property.append(ExtendedProperty(name=options().contact_id_extended_property_name, value=get_ldap_id_json(user_to_copy))) new_contact.extended_property.append(ExtendedProperty(name=options().contact_extended_property_name, value=options().contact_extended_property_value)) logging.debug('%s: Creating contact "%s"', target_user_email, new_contact.name.full_name.text) batch.put('add_insert', new_contact) # Sync data for existing contacts that were added by the script and remove those that have been deleted with closing(Batch(contacts_client, ContactsFeed)) as batch: for existing_contact in filter(is_script_contact, magic_group_members): if get_ldap_id_contact(existing_contact) in user_to_copy_by_ldap_dict: user_to_copy = user_to_copy_by_ldap_dict[get_ldap_id_contact(existing_contact)] modified = False if options().rename_old and is_renamed_contact(existing_contact): # Remove renamed flag remove_suffix(existing_contact) modified = True # Sync data dir_contact = json_to_contact_object(user_to_copy) modified = sync_contact(dir_contact, existing_contact) or modified if modified: logging.info('%s: Modifying contact "%s" with ID %s', target_user_email, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_update', existing_contact) else: if options().delete_old: logging.info('%s: Removing surplus auto-generated contact "%s" with ID %s', target_user_email, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_delete', existing_contact) elif options().rename_old and not is_renamed_contact(existing_contact): old_name = existing_contact.name.full_name.text add_suffix(existing_contact) logging.info('%s: Renaming surplus auto-generated contact "%s" to "%s" with ID %s', target_user_email, old_name, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_update', existing_contact)
def get_ldap_id_contact(contact): return next(iter([extprop.value for extprop in contact.extended_property \ if extprop.name == options().contact_id_extended_property_name]), None)
def json_to_contact_object(json): new_contact = ContactEntry() # Set the contact name new_contact.name = gdata.data.Name( given_name=gdata.data.GivenName(text=json[u'name'][u'givenName']), family_name=gdata.data.FamilyName(text=json[u'name'][u'familyName']), full_name=gdata.data.FullName(text=json[u'name'][u'fullName'])) # Set the contact email address new_contact.email.append( gdata.data.Email(address=json[u'primaryEmail'], primary='true', display_name=json[u'name'][u'fullName'], rel=DEFAULT_REL)) # Add aliases if options().add_aliases: if u'aliases' in json: for alias in json[u'aliases']: new_contact.email.append( gdata.data.Email(address=alias, primary='false', display_name=json[u'name'][u'fullName'], rel=DEFAULT_REL)) if u'nonEditableAliases' in json: for alias in json[u'nonEditableAliases']: new_contact.email.append( gdata.data.Email(address=alias, primary='false', display_name=json[u'name'][u'fullName'], rel=DEFAULT_REL)) # Add other emails if options().add_other_emails and u'emails' in json: for json_email in json[u'emails']: if u'address' in json_email: email_object = json_to_email_object(json_email) email_object.display_name = json[u'name'][u'fullName'] email_object.primary = 'false' new_contact.email.append(email_object) # Add organization (job title) info if u'organizations' in json and len(json[u'organizations']) > 0: for json_org in json[u'organizations']: if u'primary' in json_org and json_org[u'primary']: primary_org = json_org break else: primary_org = json[u'organizations'][0] org_object = json_to_organization_object(primary_org) org_object.primary = "true" new_contact.organization = org_object elif options().organization_name: # Add at least our org name org_object = gdata.data.Organization() # the API requires exactly one of 'rel' or 'label' org_object.rel = DEFAULT_REL org_object.name = gdata.data.OrgName(options().organization_name) org_object.primary = "true" new_contact.organization = org_object if u'phones' in json: for json_phone in json[u'phones']: if u'value' in json_phone: new_contact.phone_number.append( json_to_phone_number_object(json_phone)) if u'externalIds' in json: for json_external_id in json[u'externalIds']: if u'value' in json_external_id: new_contact.external_id.append( json_to_external_id_object(json_external_id)) if u'addresses' in json: for json_address in json[u'addresses']: if u'formatted' in json_address and json_address[u'formatted']: new_contact.postal_address.append( json_to_postal_address_object(json_address)) if u'ims' in json: for json_im in json[u'ims']: if u'im' in json_im: new_contact.im.append(json_to_im_object(json_im)) return new_contact
def handle_string_decoding(s): """ Base64 decode strings like EmployeeId if needed. See options. """ return (b64dec(s) if (options().base64_encoding == "true") else s)
def process_target_user(target_user_email, users_to_copy, user_to_copy_by_ldap_dict): contacts_client = contacts(email=target_user_email, options=options()) if options().undo: undo(contacts_client, target_user_email, ContactsFeed) return users_groups = contacts_client.get_groups().entry # Find group by extended property magic_group = get_magic_group(users_groups) or create_magic_group( contacts_client) magic_group_members = get_group_members(contacts_client, magic_group) magic_group_ldaps_set = lambda: filter(None, [ get_ldap_id_contact(contact) for contact in magic_group_members ]) # Find "My Contacts" group in Contacts my_contacts_group = next( iter( filter( lambda group: group.system_group and group.system_group.id == options().my_contacts_id, users_groups)), None) logging.info('%s: Using group called "%s" with %d members and ID %s', target_user_email, magic_group.title.text, len(magic_group_members), magic_group.id.text) # remove all existing contacts if options().delete_contacts: with closing(Batch(contacts_client, ContactsFeed)) as batch: for existing_contact in filter(is_script_contact, magic_group_members): logging.info('%s: Removing contact "%s" with ID %s', target_user_email, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_delete', existing_contact) # Check dangling entries in scripted group (extended_property only held 'google_apps_sync' Employee ID) with closing(Batch(contacts_client, ContactsFeed)) as batch: for dangling_contact in filter( lambda contact: not is_script_contact(contact), magic_group_members): logging.info('%s: Removing dangling contact "%s" with ID %s', target_user_email, dangling_contact.name.full_name.text, dangling_contact.id.text) batch.put('add_delete', dangling_contact) # Add new users (not already in the group) as contacts with closing(Batch(contacts_client, ContactsFeed)) as batch: for user_to_copy in users_to_copy: if get_ldap_id_json(user_to_copy) not in magic_group_ldaps_set(): new_contact = json_to_contact_object(user_to_copy) # Add the relevant groups new_contact.group_membership_info.append( GroupMembershipInfo(href=magic_group.id.text)) if options().my_contacts and my_contacts_group: new_contact.group_membership_info.append( GroupMembershipInfo(href=my_contacts_group.id.text)) # Set extended properties new_contact.extended_property.append( ExtendedProperty( name=options().contact_id_extended_property_name, value=get_ldap_id_json(user_to_copy))) new_contact.extended_property.append( ExtendedProperty( name=options().contact_extended_property_name, value=options().contact_extended_property_value)) logging.debug('%s: Creating contact "%s"', target_user_email, new_contact.name.full_name.text) batch.put('add_insert', new_contact) # Sync data for existing contacts that were added by the script and remove those that have been deleted with closing(Batch(contacts_client, ContactsFeed)) as batch: for existing_contact in filter(is_script_contact, magic_group_members): if get_ldap_id_contact( existing_contact) in user_to_copy_by_ldap_dict: user_to_copy = user_to_copy_by_ldap_dict[get_ldap_id_contact( existing_contact)] modified = False if options().rename_old and is_renamed_contact( existing_contact): # Remove renamed flag remove_suffix(existing_contact) modified = True # Sync data dir_contact = json_to_contact_object(user_to_copy) modified = sync_contact(dir_contact, existing_contact) or modified if modified: logging.info('%s: Modifying contact "%s" with ID %s', target_user_email, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_update', existing_contact) else: if options().delete_old: logging.info( '%s: Removing surplus auto-generated contact "%s" with ID %s', target_user_email, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_delete', existing_contact) elif options( ).rename_old and not is_renamed_contact(existing_contact): old_name = existing_contact.name.full_name.text add_suffix(existing_contact) logging.info( '%s: Renaming surplus auto-generated contact "%s" to "%s" with ID %s', target_user_email, old_name, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_update', existing_contact)
def process_user(target_user, filtered_calendars): filtered_calendar_by_email_dict = dict( zip(map(get('resource_email'), filtered_calendars), filtered_calendars)) contacts_client = contacts(email=target_user, options=options()) if options().undo: undo(contacts_client, target_user, ContactsFeed) return # Get Contacts Groups for user groups = contacts_client.get_groups().entry # Find Contact Group by extended property magic_group = get_magic_group(groups) or create_magic_group( contacts_client) magic_group_members = get_group_members(contacts_client, magic_group) magic_group_emails_set = map(get('address'), flatmap(get('email'), magic_group_members)) # Find "My Contacts" group in Contacts my_contacts_group = next( iter( filter( lambda group: group.system_group and group.system_group.id == options().my_contacts_id, groups)), None) logging.info('%s: Using group called "%s" with %d members and ID %s', target_user, magic_group.title.text, len(magic_group_members), magic_group.id.text) # Add new Calendar Resources as Contacts with closing(Batch(contacts_client, ContactsFeed)) as batch: for cal in filter(lambda x: \ x.resource_email not in magic_group_emails_set, filtered_calendars): new_contact = calendar_resource_to_contact(cal) # Add Contact to the relevant groups new_contact.group_membership_info.append( GroupMembershipInfo(href=magic_group.id.text)) if options().my_contacts and my_contacts_group: new_contact.group_membership_info.append( GroupMembershipInfo(href=my_contacts_group.id.text)) # Set Contact extended property extprop = ExtendedProperty() extprop.name = options().contact_extended_property_name extprop.value = options().contact_extended_property_value new_contact.extended_property.append(extprop) logging.debug('%s: Creating contact "%s"', target_user, new_contact.name.full_name.text) batch.put('add_insert', new_contact) # Sync data for existing Calendar Resources that were added by the script. Remove those that have been deleted with closing(Batch(contacts_client, ContactsFeed)) as batch: for existing_contact in filter(is_script_contact, magic_group_members): calendar_resource_to_copy = get_value_by_contact_email( filtered_calendar_by_email_dict, existing_contact) if calendar_resource_to_copy: calendar_contact = calendar_resource_to_contact( calendar_resource_to_copy) if sync_contact(calendar_contact, existing_contact): logging.info('%s: Modifying contact "%s" with ID %s', target_user, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_update', existing_contact) elif options().delete_old: logging.info( '%s: Removing surplus auto-generated contact "%s" with ID %s', target_user, existing_contact.name.full_name.text, existing_contact.id.text) batch.put('add_delete', existing_contact)