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 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 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)
Ejemplo n.º 7
0
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 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 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
Ejemplo n.º 14
0
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 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 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))
Ejemplo n.º 25
0
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))
Ejemplo n.º 26
0
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 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 handle_string_decoding(s):
    """ Base64 decode strings like EmployeeId if needed. See options. """
    return (b64dec(s) if (options().base64_encoding == "true") else s)
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 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)
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 ]