faset over fra Z3950 til google books
This commit is contained in:
740
python/gdata/contacts/__init__.py
Normal file
740
python/gdata/contacts/__init__.py
Normal file
@@ -0,0 +1,740 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2009 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Contains extensions to ElementWrapper objects used with Google Contacts."""
|
||||
|
||||
__author__ = 'dbrattli (Dag Brattli)'
|
||||
|
||||
|
||||
import atom
|
||||
import gdata
|
||||
|
||||
|
||||
## Constants from http://code.google.com/apis/gdata/elements.html ##
|
||||
REL_HOME = 'http://schemas.google.com/g/2005#home'
|
||||
REL_WORK = 'http://schemas.google.com/g/2005#work'
|
||||
REL_OTHER = 'http://schemas.google.com/g/2005#other'
|
||||
|
||||
# AOL Instant Messenger protocol
|
||||
IM_AIM = 'http://schemas.google.com/g/2005#AIM'
|
||||
IM_MSN = 'http://schemas.google.com/g/2005#MSN' # MSN Messenger protocol
|
||||
IM_YAHOO = 'http://schemas.google.com/g/2005#YAHOO' # Yahoo Messenger protocol
|
||||
IM_SKYPE = 'http://schemas.google.com/g/2005#SKYPE' # Skype protocol
|
||||
IM_QQ = 'http://schemas.google.com/g/2005#QQ' # QQ protocol
|
||||
# Google Talk protocol
|
||||
IM_GOOGLE_TALK = 'http://schemas.google.com/g/2005#GOOGLE_TALK'
|
||||
IM_ICQ = 'http://schemas.google.com/g/2005#ICQ' # ICQ protocol
|
||||
IM_JABBER = 'http://schemas.google.com/g/2005#JABBER' # Jabber protocol
|
||||
IM_NETMEETING = 'http://schemas.google.com/g/2005#netmeeting' # NetMeeting
|
||||
|
||||
PHOTO_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#photo'
|
||||
PHOTO_EDIT_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#edit-photo'
|
||||
|
||||
# Different phone types, for more info see:
|
||||
# http://code.google.com/apis/gdata/docs/2.0/elements.html#gdPhoneNumber
|
||||
PHONE_CAR = 'http://schemas.google.com/g/2005#car'
|
||||
PHONE_FAX = 'http://schemas.google.com/g/2005#fax'
|
||||
PHONE_GENERAL = 'http://schemas.google.com/g/2005#general'
|
||||
PHONE_HOME = REL_HOME
|
||||
PHONE_HOME_FAX = 'http://schemas.google.com/g/2005#home_fax'
|
||||
PHONE_INTERNAL = 'http://schemas.google.com/g/2005#internal-extension'
|
||||
PHONE_MOBILE = 'http://schemas.google.com/g/2005#mobile'
|
||||
PHONE_OTHER = REL_OTHER
|
||||
PHONE_PAGER = 'http://schemas.google.com/g/2005#pager'
|
||||
PHONE_SATELLITE = 'http://schemas.google.com/g/2005#satellite'
|
||||
PHONE_VOIP = 'http://schemas.google.com/g/2005#voip'
|
||||
PHONE_WORK = REL_WORK
|
||||
PHONE_WORK_FAX = 'http://schemas.google.com/g/2005#work_fax'
|
||||
PHONE_WORK_MOBILE = 'http://schemas.google.com/g/2005#work_mobile'
|
||||
PHONE_WORK_PAGER = 'http://schemas.google.com/g/2005#work_pager'
|
||||
PHONE_MAIN = 'http://schemas.google.com/g/2005#main'
|
||||
PHONE_ASSISTANT = 'http://schemas.google.com/g/2005#assistant'
|
||||
PHONE_CALLBACK = 'http://schemas.google.com/g/2005#callback'
|
||||
PHONE_COMPANY_MAIN = 'http://schemas.google.com/g/2005#company_main'
|
||||
PHONE_ISDN = 'http://schemas.google.com/g/2005#isdn'
|
||||
PHONE_OTHER_FAX = 'http://schemas.google.com/g/2005#other_fax'
|
||||
PHONE_RADIO = 'http://schemas.google.com/g/2005#radio'
|
||||
PHONE_TELEX = 'http://schemas.google.com/g/2005#telex'
|
||||
PHONE_TTY_TDD = 'http://schemas.google.com/g/2005#tty_tdd'
|
||||
|
||||
EXTERNAL_ID_ORGANIZATION = 'organization'
|
||||
|
||||
RELATION_MANAGER = 'manager'
|
||||
|
||||
CONTACTS_NAMESPACE = 'http://schemas.google.com/contact/2008'
|
||||
|
||||
|
||||
class GDataBase(atom.AtomBase):
|
||||
"""The Google Contacts intermediate class from atom.AtomBase."""
|
||||
|
||||
_namespace = gdata.GDATA_NAMESPACE
|
||||
_children = atom.AtomBase._children.copy()
|
||||
_attributes = atom.AtomBase._attributes.copy()
|
||||
|
||||
def __init__(self, text=None,
|
||||
extension_elements=None, extension_attributes=None):
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
|
||||
class ContactsBase(GDataBase):
|
||||
"""The Google Contacts intermediate class for Contacts namespace."""
|
||||
|
||||
_namespace = CONTACTS_NAMESPACE
|
||||
|
||||
|
||||
class OrgName(GDataBase):
|
||||
"""The Google Contacts OrgName element."""
|
||||
|
||||
_tag = 'orgName'
|
||||
|
||||
|
||||
class OrgTitle(GDataBase):
|
||||
"""The Google Contacts OrgTitle element."""
|
||||
|
||||
_tag = 'orgTitle'
|
||||
|
||||
|
||||
class OrgDepartment(GDataBase):
|
||||
"""The Google Contacts OrgDepartment element."""
|
||||
|
||||
_tag = 'orgDepartment'
|
||||
|
||||
|
||||
class OrgJobDescription(GDataBase):
|
||||
"""The Google Contacts OrgJobDescription element."""
|
||||
|
||||
_tag = 'orgJobDescription'
|
||||
|
||||
|
||||
class Where(GDataBase):
|
||||
"""The Google Contacts Where element."""
|
||||
|
||||
_tag = 'where'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['rel'] = 'rel'
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['valueString'] = 'value_string'
|
||||
|
||||
def __init__(self, value_string=None, rel=None, label=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.rel = rel
|
||||
self.label = label
|
||||
self.value_string = value_string
|
||||
|
||||
|
||||
class When(GDataBase):
|
||||
"""The Google Contacts When element."""
|
||||
|
||||
_tag = 'when'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['startTime'] = 'start_time'
|
||||
_attributes['endTime'] = 'end_time'
|
||||
_attributes['label'] = 'label'
|
||||
|
||||
def __init__(self, start_time=None, end_time=None, label=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.start_time = start_time
|
||||
self.end_time = end_time
|
||||
self.label = label
|
||||
|
||||
|
||||
class Organization(GDataBase):
|
||||
"""The Google Contacts Organization element."""
|
||||
|
||||
_tag = 'organization'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['rel'] = 'rel'
|
||||
_attributes['primary'] = 'primary'
|
||||
_children['{%s}orgName' % GDataBase._namespace] = (
|
||||
'org_name', OrgName)
|
||||
_children['{%s}orgTitle' % GDataBase._namespace] = (
|
||||
'org_title', OrgTitle)
|
||||
_children['{%s}orgDepartment' % GDataBase._namespace] = (
|
||||
'org_department', OrgDepartment)
|
||||
_children['{%s}orgJobDescription' % GDataBase._namespace] = (
|
||||
'org_job_description', OrgJobDescription)
|
||||
#_children['{%s}where' % GDataBase._namespace] = ('where', Where)
|
||||
|
||||
def __init__(self, label=None, rel=None, primary='false', org_name=None,
|
||||
org_title=None, org_department=None, org_job_description=None,
|
||||
where=None, text=None,
|
||||
extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.label = label
|
||||
self.rel = rel or REL_OTHER
|
||||
self.primary = primary
|
||||
self.org_name = org_name
|
||||
self.org_title = org_title
|
||||
self.org_department = org_department
|
||||
self.org_job_description = org_job_description
|
||||
self.where = where
|
||||
|
||||
|
||||
class PostalAddress(GDataBase):
|
||||
"""The Google Contacts PostalAddress element."""
|
||||
|
||||
_tag = 'postalAddress'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['rel'] = 'rel'
|
||||
_attributes['primary'] = 'primary'
|
||||
|
||||
def __init__(self, primary=None, rel=None, text=None,
|
||||
extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.rel = rel or REL_OTHER
|
||||
self.primary = primary
|
||||
|
||||
|
||||
class FormattedAddress(GDataBase):
|
||||
"""The Google Contacts FormattedAddress element."""
|
||||
|
||||
_tag = 'formattedAddress'
|
||||
|
||||
|
||||
class StructuredPostalAddress(GDataBase):
|
||||
"""The Google Contacts StructuredPostalAddress element."""
|
||||
|
||||
_tag = 'structuredPostalAddress'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['rel'] = 'rel'
|
||||
_attributes['primary'] = 'primary'
|
||||
_children['{%s}formattedAddress' % GDataBase._namespace] = (
|
||||
'formatted_address', FormattedAddress)
|
||||
|
||||
def __init__(self, rel=None, primary=None,
|
||||
formatted_address=None, text=None,
|
||||
extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.rel = rel or REL_OTHER
|
||||
self.primary = primary
|
||||
self.formatted_address = formatted_address
|
||||
|
||||
|
||||
class IM(GDataBase):
|
||||
"""The Google Contacts IM element."""
|
||||
|
||||
_tag = 'im'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['address'] = 'address'
|
||||
_attributes['primary'] = 'primary'
|
||||
_attributes['protocol'] = 'protocol'
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['rel'] = 'rel'
|
||||
|
||||
def __init__(self, primary='false', rel=None, address=None, protocol=None,
|
||||
label=None, text=None,
|
||||
extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.protocol = protocol
|
||||
self.address = address
|
||||
self.primary = primary
|
||||
self.rel = rel or REL_OTHER
|
||||
self.label = label
|
||||
|
||||
|
||||
class Email(GDataBase):
|
||||
"""The Google Contacts Email element."""
|
||||
|
||||
_tag = 'email'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['address'] = 'address'
|
||||
_attributes['primary'] = 'primary'
|
||||
_attributes['rel'] = 'rel'
|
||||
_attributes['label'] = 'label'
|
||||
|
||||
def __init__(self, label=None, rel=None, address=None, primary='false',
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.label = label
|
||||
self.rel = rel or REL_OTHER
|
||||
self.address = address
|
||||
self.primary = primary
|
||||
|
||||
|
||||
class PhoneNumber(GDataBase):
|
||||
"""The Google Contacts PhoneNumber element."""
|
||||
|
||||
_tag = 'phoneNumber'
|
||||
_children = GDataBase._children.copy()
|
||||
_attributes = GDataBase._attributes.copy()
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['rel'] = 'rel'
|
||||
_attributes['uri'] = 'uri'
|
||||
_attributes['primary'] = 'primary'
|
||||
|
||||
def __init__(self, label=None, rel=None, uri=None, primary='false',
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
GDataBase.__init__(self, text=text, extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.label = label
|
||||
self.rel = rel or REL_OTHER
|
||||
self.uri = uri
|
||||
self.primary = primary
|
||||
|
||||
|
||||
class Nickname(ContactsBase):
|
||||
"""The Google Contacts Nickname element."""
|
||||
|
||||
_tag = 'nickname'
|
||||
|
||||
|
||||
class Occupation(ContactsBase):
|
||||
"""The Google Contacts Occupation element."""
|
||||
|
||||
_tag = 'occupation'
|
||||
|
||||
|
||||
class Gender(ContactsBase):
|
||||
"""The Google Contacts Gender element."""
|
||||
|
||||
_tag = 'gender'
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['value'] = 'value'
|
||||
|
||||
def __init__(self, value=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.value = value
|
||||
|
||||
|
||||
class Birthday(ContactsBase):
|
||||
"""The Google Contacts Birthday element."""
|
||||
|
||||
_tag = 'birthday'
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['when'] = 'when'
|
||||
|
||||
def __init__(self, when=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.when = when
|
||||
|
||||
|
||||
class Relation(ContactsBase):
|
||||
"""The Google Contacts Relation element."""
|
||||
|
||||
_tag = 'relation'
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['rel'] = 'rel'
|
||||
|
||||
def __init__(self, label=None, rel=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.label = label
|
||||
self.rel = rel
|
||||
|
||||
|
||||
def RelationFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(Relation, xml_string)
|
||||
|
||||
|
||||
class UserDefinedField(ContactsBase):
|
||||
"""The Google Contacts UserDefinedField element."""
|
||||
|
||||
_tag = 'userDefinedField'
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['key'] = 'key'
|
||||
_attributes['value'] = 'value'
|
||||
|
||||
def __init__(self, key=None, value=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
|
||||
def UserDefinedFieldFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(UserDefinedField, xml_string)
|
||||
|
||||
|
||||
class Website(ContactsBase):
|
||||
"""The Google Contacts Website element."""
|
||||
|
||||
_tag = 'website'
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['href'] = 'href'
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['primary'] = 'primary'
|
||||
_attributes['rel'] = 'rel'
|
||||
|
||||
def __init__(self, href=None, label=None, primary='false', rel=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.href = href
|
||||
self.label = label
|
||||
self.primary = primary
|
||||
self.rel = rel
|
||||
|
||||
|
||||
def WebsiteFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(Website, xml_string)
|
||||
|
||||
|
||||
class ExternalId(ContactsBase):
|
||||
"""The Google Contacts ExternalId element."""
|
||||
|
||||
_tag = 'externalId'
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['rel'] = 'rel'
|
||||
_attributes['value'] = 'value'
|
||||
|
||||
def __init__(self, label=None, rel=None, value=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.label = label
|
||||
self.rel = rel
|
||||
self.value = value
|
||||
|
||||
|
||||
def ExternalIdFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(ExternalId, xml_string)
|
||||
|
||||
|
||||
class Event(ContactsBase):
|
||||
"""The Google Contacts Event element."""
|
||||
|
||||
_tag = 'event'
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['label'] = 'label'
|
||||
_attributes['rel'] = 'rel'
|
||||
_children['{%s}when' % ContactsBase._namespace] = ('when', When)
|
||||
|
||||
def __init__(self, label=None, rel=None, when=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.label = label
|
||||
self.rel = rel
|
||||
self.when = when
|
||||
|
||||
|
||||
def EventFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(Event, xml_string)
|
||||
|
||||
|
||||
class Deleted(GDataBase):
|
||||
"""The Google Contacts Deleted element."""
|
||||
|
||||
_tag = 'deleted'
|
||||
|
||||
|
||||
class GroupMembershipInfo(ContactsBase):
|
||||
"""The Google Contacts GroupMembershipInfo element."""
|
||||
|
||||
_tag = 'groupMembershipInfo'
|
||||
|
||||
_children = ContactsBase._children.copy()
|
||||
_attributes = ContactsBase._attributes.copy()
|
||||
_attributes['deleted'] = 'deleted'
|
||||
_attributes['href'] = 'href'
|
||||
|
||||
def __init__(self, deleted=None, href=None, text=None,
|
||||
extension_elements=None, extension_attributes=None):
|
||||
ContactsBase.__init__(self, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes)
|
||||
self.deleted = deleted
|
||||
self.href = href
|
||||
|
||||
|
||||
class PersonEntry(gdata.BatchEntry):
|
||||
"""Base class for ContactEntry and ProfileEntry."""
|
||||
|
||||
_children = gdata.BatchEntry._children.copy()
|
||||
_children['{%s}organization' % gdata.GDATA_NAMESPACE] = (
|
||||
'organization', [Organization])
|
||||
_children['{%s}phoneNumber' % gdata.GDATA_NAMESPACE] = (
|
||||
'phone_number', [PhoneNumber])
|
||||
_children['{%s}nickname' % CONTACTS_NAMESPACE] = ('nickname', Nickname)
|
||||
_children['{%s}occupation' % CONTACTS_NAMESPACE] = ('occupation', Occupation)
|
||||
_children['{%s}gender' % CONTACTS_NAMESPACE] = ('gender', Gender)
|
||||
_children['{%s}birthday' % CONTACTS_NAMESPACE] = ('birthday', Birthday)
|
||||
_children['{%s}postalAddress' % gdata.GDATA_NAMESPACE] = ('postal_address',
|
||||
[PostalAddress])
|
||||
_children['{%s}structuredPostalAddress' % gdata.GDATA_NAMESPACE] = (
|
||||
'structured_postal_address', [StructuredPostalAddress])
|
||||
_children['{%s}email' % gdata.GDATA_NAMESPACE] = ('email', [Email])
|
||||
_children['{%s}im' % gdata.GDATA_NAMESPACE] = ('im', [IM])
|
||||
_children['{%s}relation' % CONTACTS_NAMESPACE] = ('relation', [Relation])
|
||||
_children['{%s}userDefinedField' % CONTACTS_NAMESPACE] = (
|
||||
'user_defined_field', [UserDefinedField])
|
||||
_children['{%s}website' % CONTACTS_NAMESPACE] = ('website', [Website])
|
||||
_children['{%s}externalId' % CONTACTS_NAMESPACE] = (
|
||||
'external_id', [ExternalId])
|
||||
_children['{%s}event' % CONTACTS_NAMESPACE] = ('event', [Event])
|
||||
# The following line should be removed once the Python support
|
||||
# for GData 2.0 is mature.
|
||||
_attributes = gdata.BatchEntry._attributes.copy()
|
||||
_attributes['{%s}etag' % gdata.GDATA_NAMESPACE] = 'etag'
|
||||
|
||||
def __init__(self, author=None, category=None, content=None,
|
||||
atom_id=None, link=None, published=None,
|
||||
title=None, updated=None, organization=None, phone_number=None,
|
||||
nickname=None, occupation=None, gender=None, birthday=None,
|
||||
postal_address=None, structured_postal_address=None, email=None,
|
||||
im=None, relation=None, user_defined_field=None, website=None,
|
||||
external_id=None, event=None, batch_operation=None,
|
||||
batch_id=None, batch_status=None, text=None,
|
||||
extension_elements=None, extension_attributes=None, etag=None):
|
||||
gdata.BatchEntry.__init__(self, author=author, category=category,
|
||||
content=content, atom_id=atom_id, link=link,
|
||||
published=published,
|
||||
batch_operation=batch_operation,
|
||||
batch_id=batch_id, batch_status=batch_status,
|
||||
title=title, updated=updated)
|
||||
self.organization = organization or []
|
||||
self.phone_number = phone_number or []
|
||||
self.nickname = nickname
|
||||
self.occupation = occupation
|
||||
self.gender = gender
|
||||
self.birthday = birthday
|
||||
self.postal_address = postal_address or []
|
||||
self.structured_postal_address = structured_postal_address or []
|
||||
self.email = email or []
|
||||
self.im = im or []
|
||||
self.relation = relation or []
|
||||
self.user_defined_field = user_defined_field or []
|
||||
self.website = website or []
|
||||
self.external_id = external_id or []
|
||||
self.event = event or []
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
# The following line should be removed once the Python support
|
||||
# for GData 2.0 is mature.
|
||||
self.etag = etag
|
||||
|
||||
|
||||
class ContactEntry(PersonEntry):
|
||||
"""A Google Contact flavor of an Atom Entry."""
|
||||
|
||||
_children = PersonEntry._children.copy()
|
||||
|
||||
_children['{%s}deleted' % gdata.GDATA_NAMESPACE] = ('deleted', Deleted)
|
||||
_children['{%s}groupMembershipInfo' % CONTACTS_NAMESPACE] = (
|
||||
'group_membership_info', [GroupMembershipInfo])
|
||||
_children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = (
|
||||
'extended_property', [gdata.ExtendedProperty])
|
||||
# Overwrite the organization rule in PersonEntry so that a ContactEntry
|
||||
# may only contain one <gd:organization> element.
|
||||
_children['{%s}organization' % gdata.GDATA_NAMESPACE] = (
|
||||
'organization', Organization)
|
||||
|
||||
def __init__(self, author=None, category=None, content=None,
|
||||
atom_id=None, link=None, published=None,
|
||||
title=None, updated=None, organization=None, phone_number=None,
|
||||
nickname=None, occupation=None, gender=None, birthday=None,
|
||||
postal_address=None, structured_postal_address=None, email=None,
|
||||
im=None, relation=None, user_defined_field=None, website=None,
|
||||
external_id=None, event=None, batch_operation=None,
|
||||
batch_id=None, batch_status=None, text=None,
|
||||
extension_elements=None, extension_attributes=None, etag=None,
|
||||
deleted=None, extended_property=None,
|
||||
group_membership_info=None):
|
||||
PersonEntry.__init__(self, author=author, category=category,
|
||||
content=content, atom_id=atom_id, link=link,
|
||||
published=published, title=title, updated=updated,
|
||||
organization=organization, phone_number=phone_number,
|
||||
nickname=nickname, occupation=occupation,
|
||||
gender=gender, birthday=birthday,
|
||||
postal_address=postal_address,
|
||||
structured_postal_address=structured_postal_address,
|
||||
email=email, im=im, relation=relation,
|
||||
user_defined_field=user_defined_field,
|
||||
website=website, external_id=external_id, event=event,
|
||||
batch_operation=batch_operation, batch_id=batch_id,
|
||||
batch_status=batch_status, text=text,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes, etag=etag)
|
||||
self.deleted = deleted
|
||||
self.extended_property = extended_property or []
|
||||
self.group_membership_info = group_membership_info or []
|
||||
|
||||
def GetPhotoLink(self):
|
||||
for a_link in self.link:
|
||||
if a_link.rel == PHOTO_LINK_REL:
|
||||
return a_link
|
||||
return None
|
||||
|
||||
def GetPhotoEditLink(self):
|
||||
for a_link in self.link:
|
||||
if a_link.rel == PHOTO_EDIT_LINK_REL:
|
||||
return a_link
|
||||
return None
|
||||
|
||||
|
||||
def ContactEntryFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(ContactEntry, xml_string)
|
||||
|
||||
|
||||
class ContactsFeed(gdata.BatchFeed, gdata.LinkFinder):
|
||||
"""A Google Contacts feed flavor of an Atom Feed."""
|
||||
|
||||
_children = gdata.BatchFeed._children.copy()
|
||||
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ContactEntry])
|
||||
|
||||
def __init__(self, author=None, category=None, contributor=None,
|
||||
generator=None, icon=None, atom_id=None, link=None, logo=None,
|
||||
rights=None, subtitle=None, title=None, updated=None,
|
||||
entry=None, total_results=None, start_index=None,
|
||||
items_per_page=None, extension_elements=None,
|
||||
extension_attributes=None, text=None):
|
||||
gdata.BatchFeed.__init__(self, author=author, category=category,
|
||||
contributor=contributor, generator=generator,
|
||||
icon=icon, atom_id=atom_id, link=link,
|
||||
logo=logo, rights=rights, subtitle=subtitle,
|
||||
title=title, updated=updated, entry=entry,
|
||||
total_results=total_results,
|
||||
start_index=start_index,
|
||||
items_per_page=items_per_page,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes,
|
||||
text=text)
|
||||
|
||||
|
||||
def ContactsFeedFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(ContactsFeed, xml_string)
|
||||
|
||||
|
||||
class GroupEntry(gdata.BatchEntry):
|
||||
"""Represents a contact group."""
|
||||
_children = gdata.BatchEntry._children.copy()
|
||||
_children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = (
|
||||
'extended_property', [gdata.ExtendedProperty])
|
||||
|
||||
def __init__(self, author=None, category=None, content=None,
|
||||
contributor=None, atom_id=None, link=None, published=None,
|
||||
rights=None, source=None, summary=None, control=None,
|
||||
title=None, updated=None,
|
||||
extended_property=None, batch_operation=None, batch_id=None,
|
||||
batch_status=None,
|
||||
extension_elements=None, extension_attributes=None, text=None):
|
||||
gdata.BatchEntry.__init__(self, author=author, category=category,
|
||||
content=content,
|
||||
atom_id=atom_id, link=link, published=published,
|
||||
batch_operation=batch_operation,
|
||||
batch_id=batch_id, batch_status=batch_status,
|
||||
title=title, updated=updated)
|
||||
self.extended_property = extended_property or []
|
||||
|
||||
|
||||
def GroupEntryFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(GroupEntry, xml_string)
|
||||
|
||||
|
||||
class GroupsFeed(gdata.BatchFeed):
|
||||
"""A Google contact groups feed flavor of an Atom Feed."""
|
||||
_children = gdata.BatchFeed._children.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GroupEntry])
|
||||
|
||||
|
||||
def GroupsFeedFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(GroupsFeed, xml_string)
|
||||
|
||||
|
||||
class ProfileEntry(PersonEntry):
|
||||
"""A Google Profiles flavor of an Atom Entry."""
|
||||
|
||||
|
||||
def ProfileEntryFromString(xml_string):
|
||||
"""Converts an XML string into a ProfileEntry object.
|
||||
|
||||
Args:
|
||||
xml_string: string The XML describing a Profile entry.
|
||||
|
||||
Returns:
|
||||
A ProfileEntry object corresponding to the given XML.
|
||||
"""
|
||||
return atom.CreateClassFromXMLString(ProfileEntry, xml_string)
|
||||
|
||||
|
||||
class ProfilesFeed(gdata.BatchFeed, gdata.LinkFinder):
|
||||
"""A Google Profiles feed flavor of an Atom Feed."""
|
||||
|
||||
_children = gdata.BatchFeed._children.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ProfileEntry])
|
||||
|
||||
def __init__(self, author=None, category=None, contributor=None,
|
||||
generator=None, icon=None, atom_id=None, link=None, logo=None,
|
||||
rights=None, subtitle=None, title=None, updated=None,
|
||||
entry=None, total_results=None, start_index=None,
|
||||
items_per_page=None, extension_elements=None,
|
||||
extension_attributes=None, text=None):
|
||||
gdata.BatchFeed.__init__(self, author=author, category=category,
|
||||
contributor=contributor, generator=generator,
|
||||
icon=icon, atom_id=atom_id, link=link,
|
||||
logo=logo, rights=rights, subtitle=subtitle,
|
||||
title=title, updated=updated, entry=entry,
|
||||
total_results=total_results,
|
||||
start_index=start_index,
|
||||
items_per_page=items_per_page,
|
||||
extension_elements=extension_elements,
|
||||
extension_attributes=extension_attributes,
|
||||
text=text)
|
||||
|
||||
|
||||
def ProfilesFeedFromString(xml_string):
|
||||
"""Converts an XML string into a ProfilesFeed object.
|
||||
|
||||
Args:
|
||||
xml_string: string The XML describing a Profiles feed.
|
||||
|
||||
Returns:
|
||||
A ProfilesFeed object corresponding to the given XML.
|
||||
"""
|
||||
return atom.CreateClassFromXMLString(ProfilesFeed, xml_string)
|
495
python/gdata/contacts/client.py
Normal file
495
python/gdata/contacts/client.py
Normal file
@@ -0,0 +1,495 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2009 Google Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from types import ListType, DictionaryType
|
||||
|
||||
|
||||
"""Contains a client to communicate with the Contacts servers.
|
||||
|
||||
For documentation on the Contacts API, see:
|
||||
http://code.google.com/apis/contatcs/
|
||||
"""
|
||||
|
||||
__author__ = 'vinces1979@gmail.com (Vince Spicer)'
|
||||
|
||||
|
||||
import gdata.client
|
||||
import gdata.contacts.data
|
||||
import atom.data
|
||||
import atom.http_core
|
||||
import gdata.gauth
|
||||
|
||||
|
||||
class ContactsClient(gdata.client.GDClient):
|
||||
api_version = '3'
|
||||
auth_service = 'cp'
|
||||
server = "www.google.com"
|
||||
contact_list = "default"
|
||||
auth_scopes = gdata.gauth.AUTH_SCOPES['cp']
|
||||
|
||||
|
||||
def __init__(self, domain=None, auth_token=None, **kwargs):
|
||||
"""Constructs a new client for the Email Settings API.
|
||||
|
||||
Args:
|
||||
domain: string The Google Apps domain (if any).
|
||||
kwargs: The other parameters to pass to the gdata.client.GDClient
|
||||
constructor.
|
||||
"""
|
||||
gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs)
|
||||
self.domain = domain
|
||||
|
||||
def get_feed_uri(self, kind='contacts', contact_list=None, projection='full',
|
||||
scheme="http"):
|
||||
"""Builds a feed URI.
|
||||
|
||||
Args:
|
||||
kind: The type of feed to return, typically 'groups' or 'contacts'.
|
||||
Default value: 'contacts'.
|
||||
contact_list: The contact list to return a feed for.
|
||||
Default value: self.contact_list.
|
||||
projection: The projection to apply to the feed contents, for example
|
||||
'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'.
|
||||
scheme: The URL scheme such as 'http' or 'https', None to return a
|
||||
relative URI without hostname.
|
||||
|
||||
Returns:
|
||||
A feed URI using the given kind, contact list, and projection.
|
||||
Example: '/m8/feeds/contacts/default/full'.
|
||||
"""
|
||||
contact_list = contact_list or self.contact_list
|
||||
if kind == 'profiles':
|
||||
contact_list = 'domain/%s' % self.domain
|
||||
prefix = scheme and '%s://%s' % (scheme, self.server) or ''
|
||||
return '%s/m8/feeds/%s/%s/%s' % (prefix, kind, contact_list, projection)
|
||||
|
||||
GetFeedUri = get_feed_uri
|
||||
|
||||
def get_contact(self, uri, desired_class=gdata.contacts.data.ContactEntry,
|
||||
auth_token=None, **kwargs):
|
||||
return self.get_feed(uri, auth_token=auth_token,
|
||||
desired_class=desired_class, **kwargs)
|
||||
|
||||
|
||||
GetContact = get_contact
|
||||
|
||||
|
||||
def create_contact(self, new_contact, insert_uri=None, auth_token=None, **kwargs):
|
||||
"""Adds an new contact to Google Contacts.
|
||||
|
||||
Args:
|
||||
new_contact: atom.Entry or subclass A new contact which is to be added to
|
||||
Google Contacts.
|
||||
insert_uri: the URL to post new contacts to the feed
|
||||
url_params: dict (optional) Additional URL parameters to be included
|
||||
in the insertion request.
|
||||
escape_params: boolean (optional) If true, the url_parameters will be
|
||||
escaped before they are included in the request.
|
||||
|
||||
Returns:
|
||||
On successful insert, an entry containing the contact created
|
||||
On failure, a RequestError is raised of the form:
|
||||
{'status': HTTP status code from server,
|
||||
'reason': HTTP reason from the server,
|
||||
'body': HTTP body of the server's response}
|
||||
"""
|
||||
insert_uri = insert_uri or self.GetFeedUri()
|
||||
return self.Post(new_contact, insert_uri,
|
||||
auth_token=auth_token, **kwargs)
|
||||
|
||||
CreateContact = create_contact
|
||||
|
||||
def add_contact(self, new_contact, insert_uri=None, auth_token=None,
|
||||
billing_information=None, birthday=None, calendar_link=None, **kwargs):
|
||||
"""Adds an new contact to Google Contacts.
|
||||
|
||||
Args:
|
||||
new_contact: atom.Entry or subclass A new contact which is to be added to
|
||||
Google Contacts.
|
||||
insert_uri: the URL to post new contacts to the feed
|
||||
url_params: dict (optional) Additional URL parameters to be included
|
||||
in the insertion request.
|
||||
escape_params: boolean (optional) If true, the url_parameters will be
|
||||
escaped before they are included in the request.
|
||||
|
||||
Returns:
|
||||
On successful insert, an entry containing the contact created
|
||||
On failure, a RequestError is raised of the form:
|
||||
{'status': HTTP status code from server,
|
||||
'reason': HTTP reason from the server,
|
||||
'body': HTTP body of the server's response}
|
||||
"""
|
||||
|
||||
contact = gdata.contacts.data.ContactEntry()
|
||||
|
||||
if billing_information is not None:
|
||||
if not isinstance(billing_information, gdata.contacts.data.BillingInformation):
|
||||
billing_information = gdata.contacts.data.BillingInformation(text=billing_information)
|
||||
|
||||
contact.billing_information = billing_information
|
||||
|
||||
if birthday is not None:
|
||||
if not isinstance(birthday, gdata.contacts.data.Birthday):
|
||||
birthday = gdata.contacts.data.Birthday(when=birthday)
|
||||
|
||||
contact.birthday = birthday
|
||||
|
||||
if calendar_link is not None:
|
||||
if type(calendar_link) is not ListType:
|
||||
calendar_link = [calendar_link]
|
||||
|
||||
for link in calendar_link:
|
||||
if not isinstance(link, gdata.contacts.data.CalendarLink):
|
||||
if type(link) is not DictionaryType:
|
||||
raise TypeError, "calendar_link Requires dictionary not %s" % type(link)
|
||||
|
||||
link = gdata.contacts.data.CalendarLink(
|
||||
rel=link.get("rel", None),
|
||||
label=link.get("label", None),
|
||||
primary=link.get("primary", None),
|
||||
href=link.get("href", None),
|
||||
)
|
||||
|
||||
contact.calendar_link.append(link)
|
||||
|
||||
insert_uri = insert_uri or self.GetFeedUri()
|
||||
return self.Post(contact, insert_uri,
|
||||
auth_token=auth_token, **kwargs)
|
||||
|
||||
AddContact = add_contact
|
||||
|
||||
def get_contacts(self, desired_class=gdata.contacts.data.ContactsFeed,
|
||||
auth_token=None, **kwargs):
|
||||
"""Obtains a feed with the contacts belonging to the current user.
|
||||
|
||||
Args:
|
||||
auth_token: An object which sets the Authorization HTTP header in its
|
||||
modify_request method. Recommended classes include
|
||||
gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken
|
||||
among others. Represents the current user. Defaults to None
|
||||
and if None, this method will look for a value in the
|
||||
auth_token member of SpreadsheetsClient.
|
||||
desired_class: class descended from atom.core.XmlElement to which a
|
||||
successful response should be converted. If there is no
|
||||
converter function specified (desired_class=None) then the
|
||||
desired_class will be used in calling the
|
||||
atom.core.parse function. If neither
|
||||
the desired_class nor the converter is specified, an
|
||||
HTTP reponse object will be returned. Defaults to
|
||||
gdata.spreadsheets.data.SpreadsheetsFeed.
|
||||
"""
|
||||
return self.get_feed(self.GetFeedUri(), auth_token=auth_token,
|
||||
desired_class=desired_class, **kwargs)
|
||||
|
||||
GetContacts = get_contacts
|
||||
|
||||
def get_group(self, uri=None, desired_class=gdata.contacts.data.GroupEntry,
|
||||
auth_token=None, **kwargs):
|
||||
""" Get a single groups details
|
||||
Args:
|
||||
uri: the group uri or id
|
||||
"""
|
||||
return self.get(uri, desired_class=desired_class, auth_token=auth_token, **kwargs)
|
||||
|
||||
GetGroup = get_group
|
||||
|
||||
def get_groups(self, uri=None, desired_class=gdata.contacts.data.GroupsFeed,
|
||||
auth_token=None, **kwargs):
|
||||
uri = uri or self.GetFeedUri('groups')
|
||||
return self.get_feed(uri, desired_class=desired_class, auth_token=auth_token, **kwargs)
|
||||
|
||||
GetGroups = get_groups
|
||||
|
||||
def create_group(self, new_group, insert_uri=None, url_params=None,
|
||||
desired_class=None):
|
||||
insert_uri = insert_uri or self.GetFeedUri('groups')
|
||||
return self.Post(new_group, insert_uri, url_params=url_params,
|
||||
desired_class=desired_class)
|
||||
|
||||
CreateGroup = create_group
|
||||
|
||||
def update_group(self, edit_uri, updated_group, url_params=None,
|
||||
escape_params=True, desired_class=None):
|
||||
return self.Put(updated_group, self._CleanUri(edit_uri),
|
||||
url_params=url_params,
|
||||
escape_params=escape_params,
|
||||
desired_class=desired_class)
|
||||
|
||||
UpdateGroup = update_group
|
||||
|
||||
def delete_group(self, group_object, auth_token=None, force=False, **kws):
|
||||
return self.Delete(group_object, auth_token=auth_token, force=force, **kws )
|
||||
|
||||
DeleteGroup = delete_group
|
||||
|
||||
def change_photo(self, media, contact_entry_or_url, content_type=None,
|
||||
content_length=None):
|
||||
"""Change the photo for the contact by uploading a new photo.
|
||||
|
||||
Performs a PUT against the photo edit URL to send the binary data for the
|
||||
photo.
|
||||
|
||||
Args:
|
||||
media: filename, file-like-object, or a gdata.MediaSource object to send.
|
||||
contact_entry_or_url: ContactEntry or str If it is a ContactEntry, this
|
||||
method will search for an edit photo link URL and
|
||||
perform a PUT to the URL.
|
||||
content_type: str (optional) the mime type for the photo data. This is
|
||||
necessary if media is a file or file name, but if media
|
||||
is a MediaSource object then the media object can contain
|
||||
the mime type. If media_type is set, it will override the
|
||||
mime type in the media object.
|
||||
content_length: int or str (optional) Specifying the content length is
|
||||
only required if media is a file-like object. If media
|
||||
is a filename, the length is determined using
|
||||
os.path.getsize. If media is a MediaSource object, it is
|
||||
assumed that it already contains the content length.
|
||||
"""
|
||||
if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry):
|
||||
url = contact_entry_or_url.GetPhotoEditLink().href
|
||||
else:
|
||||
url = contact_entry_or_url
|
||||
if isinstance(media, gdata.MediaSource):
|
||||
payload = media
|
||||
# If the media object is a file-like object, then use it as the file
|
||||
# handle in the in the MediaSource.
|
||||
elif hasattr(media, 'read'):
|
||||
payload = gdata.MediaSource(file_handle=media,
|
||||
content_type=content_type, content_length=content_length)
|
||||
# Assume that the media object is a file name.
|
||||
else:
|
||||
payload = gdata.MediaSource(content_type=content_type,
|
||||
content_length=content_length, file_path=media)
|
||||
return self.Put(payload, url)
|
||||
|
||||
ChangePhoto = change_photo
|
||||
|
||||
def get_photo(self, contact_entry_or_url):
|
||||
"""Retrives the binary data for the contact's profile photo as a string.
|
||||
|
||||
Args:
|
||||
contact_entry_or_url: a gdata.contacts.ContactEntry objecr or a string
|
||||
containing the photo link's URL. If the contact entry does not
|
||||
contain a photo link, the image will not be fetched and this method
|
||||
will return None.
|
||||
"""
|
||||
# TODO: add the ability to write out the binary image data to a file,
|
||||
# reading and writing a chunk at a time to avoid potentially using up
|
||||
# large amounts of memory.
|
||||
url = None
|
||||
if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry):
|
||||
photo_link = contact_entry_or_url.GetPhotoLink()
|
||||
if photo_link:
|
||||
url = photo_link.href
|
||||
else:
|
||||
url = contact_entry_or_url
|
||||
if url:
|
||||
return self.Get(url).read()
|
||||
else:
|
||||
return None
|
||||
|
||||
GetPhoto = get_photo
|
||||
|
||||
def delete_photo(self, contact_entry_or_url):
|
||||
url = None
|
||||
if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry):
|
||||
url = contact_entry_or_url.GetPhotoEditLink().href
|
||||
else:
|
||||
url = contact_entry_or_url
|
||||
if url:
|
||||
self.Delete(url)
|
||||
|
||||
DeletePhoto = delete_photo
|
||||
|
||||
def get_profiles_feed(self, uri=None):
|
||||
"""Retrieves a feed containing all domain's profiles.
|
||||
|
||||
Args:
|
||||
uri: string (optional) the URL to retrieve the profiles feed,
|
||||
for example /m8/feeds/profiles/default/full
|
||||
|
||||
Returns:
|
||||
On success, a ProfilesFeed containing the profiles.
|
||||
On failure, raises a RequestError.
|
||||
"""
|
||||
|
||||
uri = uri or self.GetFeedUri('profiles')
|
||||
return self.Get(uri,
|
||||
desired_class=gdata.contacts.data.ProfilesFeed)
|
||||
|
||||
GetProfilesFeed = get_profiles_feed
|
||||
|
||||
def get_profile(self, uri):
|
||||
"""Retrieves a domain's profile for the user.
|
||||
|
||||
Args:
|
||||
uri: string the URL to retrieve the profiles feed,
|
||||
for example /m8/feeds/profiles/default/full/username
|
||||
|
||||
Returns:
|
||||
On success, a ProfileEntry containing the profile for the user.
|
||||
On failure, raises a RequestError
|
||||
"""
|
||||
return self.Get(uri,
|
||||
desired_class=gdata.contacts.data.ProfileEntry)
|
||||
|
||||
GetProfile = get_profile
|
||||
|
||||
def update_profile(self, updated_profile, auth_token=None, force=False, **kwargs):
|
||||
"""Updates an existing profile.
|
||||
|
||||
Args:
|
||||
updated_profile: atom.Entry or subclass containing
|
||||
the Atom Entry which will replace the profile which is
|
||||
stored at the edit_url.
|
||||
auth_token: An object which sets the Authorization HTTP header in its
|
||||
modify_request method. Recommended classes include
|
||||
gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken
|
||||
among others. Represents the current user. Defaults to None
|
||||
and if None, this method will look for a value in the
|
||||
auth_token member of ContactsClient.
|
||||
force: boolean stating whether an update should be forced. Defaults to
|
||||
False. Normally, if a change has been made since the passed in
|
||||
entry was obtained, the server will not overwrite the entry since
|
||||
the changes were based on an obsolete version of the entry.
|
||||
Setting force to True will cause the update to silently
|
||||
overwrite whatever version is present.
|
||||
url_params: dict (optional) Additional URL parameters to be included
|
||||
in the insertion request.
|
||||
escape_params: boolean (optional) If true, the url_parameters will be
|
||||
escaped before they are included in the request.
|
||||
|
||||
Returns:
|
||||
On successful update, a httplib.HTTPResponse containing the server's
|
||||
response to the PUT request.
|
||||
On failure, raises a RequestError.
|
||||
"""
|
||||
return self.Update(updated_profile, auth_token=auth_token, force=force, **kwargs)
|
||||
|
||||
UpdateProfile = update_profile
|
||||
|
||||
def execute_batch(self, batch_feed, url, desired_class=None):
|
||||
"""Sends a batch request feed to the server.
|
||||
|
||||
Args:
|
||||
batch_feed: gdata.contacts.ContactFeed A feed containing batch
|
||||
request entries. Each entry contains the operation to be performed
|
||||
on the data contained in the entry. For example an entry with an
|
||||
operation type of insert will be used as if the individual entry
|
||||
had been inserted.
|
||||
url: str The batch URL to which these operations should be applied.
|
||||
converter: Function (optional) The function used to convert the server's
|
||||
response to an object.
|
||||
|
||||
Returns:
|
||||
The results of the batch request's execution on the server. If the
|
||||
default converter is used, this is stored in a ContactsFeed.
|
||||
"""
|
||||
return self.Post(batch_feed, url, desired_class=desired_class)
|
||||
|
||||
ExecuteBatch = execute_batch
|
||||
|
||||
def execute_batch_profiles(self, batch_feed, url,
|
||||
desired_class=gdata.contacts.data.ProfilesFeed):
|
||||
"""Sends a batch request feed to the server.
|
||||
|
||||
Args:
|
||||
batch_feed: gdata.profiles.ProfilesFeed A feed containing batch
|
||||
request entries. Each entry contains the operation to be performed
|
||||
on the data contained in the entry. For example an entry with an
|
||||
operation type of insert will be used as if the individual entry
|
||||
had been inserted.
|
||||
url: string The batch URL to which these operations should be applied.
|
||||
converter: Function (optional) The function used to convert the server's
|
||||
response to an object. The default value is
|
||||
gdata.profiles.ProfilesFeedFromString.
|
||||
|
||||
Returns:
|
||||
The results of the batch request's execution on the server. If the
|
||||
default converter is used, this is stored in a ProfilesFeed.
|
||||
"""
|
||||
return self.Post(batch_feed, url, desired_class=desired_class)
|
||||
|
||||
ExecuteBatchProfiles = execute_batch_profiles
|
||||
|
||||
def _CleanUri(self, uri):
|
||||
"""Sanitizes a feed URI.
|
||||
|
||||
Args:
|
||||
uri: The URI to sanitize, can be relative or absolute.
|
||||
|
||||
Returns:
|
||||
The given URI without its http://server prefix, if any.
|
||||
Keeps the leading slash of the URI.
|
||||
"""
|
||||
url_prefix = 'http://%s' % self.server
|
||||
if uri.startswith(url_prefix):
|
||||
uri = uri[len(url_prefix):]
|
||||
return uri
|
||||
|
||||
class ContactsQuery(gdata.client.Query):
|
||||
"""
|
||||
Create a custom Contacts Query
|
||||
|
||||
Full specs can be found at: U{Contacts query parameters reference
|
||||
<http://code.google.com/apis/contacts/docs/3.0/reference.html#Parameters>}
|
||||
"""
|
||||
|
||||
def __init__(self, feed=None, group=None, orderby=None, showdeleted=None,
|
||||
sortorder=None, requirealldeleted=None, **kwargs):
|
||||
"""
|
||||
@param max_results: The maximum number of entries to return. If you want
|
||||
to receive all of the contacts, rather than only the default maximum, you
|
||||
can specify a very large number for max-results.
|
||||
@param start-index: The 1-based index of the first result to be retrieved.
|
||||
@param updated-min: The lower bound on entry update dates.
|
||||
@param group: Constrains the results to only the contacts belonging to the
|
||||
group specified. Value of this parameter specifies group ID
|
||||
@param orderby: Sorting criterion. The only supported value is
|
||||
lastmodified.
|
||||
@param showdeleted: Include deleted contacts in the returned contacts feed
|
||||
@pram sortorder: Sorting order direction. Can be either ascending or
|
||||
descending.
|
||||
@param requirealldeleted: Only relevant if showdeleted and updated-min
|
||||
are also provided. It dictates the behavior of the server in case it
|
||||
detects that placeholders of some entries deleted since the point in
|
||||
time specified as updated-min may have been lost.
|
||||
"""
|
||||
gdata.client.Query.__init__(self, **kwargs)
|
||||
self.group = group
|
||||
self.orderby = orderby
|
||||
self.sortorder = sortorder
|
||||
self.showdeleted = showdeleted
|
||||
|
||||
def modify_request(self, http_request):
|
||||
if self.group:
|
||||
gdata.client._add_query_param('group', self.group, http_request)
|
||||
if self.orderby:
|
||||
gdata.client._add_query_param('orderby', self.orderby, http_request)
|
||||
if self.sortorder:
|
||||
gdata.client._add_query_param('sortorder', self.sortorder, http_request)
|
||||
if self.showdeleted:
|
||||
gdata.client._add_query_param('showdeleted', self.showdeleted, http_request)
|
||||
gdata.client.Query.modify_request(self, http_request)
|
||||
|
||||
ModifyRequest = modify_request
|
||||
|
||||
|
||||
class ProfilesQuery(gdata.client.Query):
|
||||
def __init__(self, feed=None):
|
||||
self.feed = feed or 'http://www.google.com/m8/feeds/profiles/default/full'
|
||||
|
||||
|
||||
|
474
python/gdata/contacts/data.py
Normal file
474
python/gdata/contacts/data.py
Normal file
@@ -0,0 +1,474 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2009 Google Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Data model classes for parsing and generating XML for the Contacts API."""
|
||||
|
||||
|
||||
__author__ = 'vinces1979@gmail.com (Vince Spicer)'
|
||||
|
||||
|
||||
import atom.core
|
||||
import gdata
|
||||
import gdata.data
|
||||
|
||||
|
||||
PHOTO_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#photo'
|
||||
PHOTO_EDIT_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#edit-photo'
|
||||
|
||||
EXTERNAL_ID_ORGANIZATION = 'organization'
|
||||
|
||||
RELATION_MANAGER = 'manager'
|
||||
|
||||
CONTACTS_NAMESPACE = 'http://schemas.google.com/contact/2008'
|
||||
CONTACTS_TEMPLATE = '{%s}%%s' % CONTACTS_NAMESPACE
|
||||
|
||||
|
||||
class BillingInformation(atom.core.XmlElement):
|
||||
"""
|
||||
gContact:billingInformation
|
||||
Specifies billing information of the entity represented by the contact. The element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'billingInformation'
|
||||
|
||||
|
||||
class Birthday(atom.core.XmlElement):
|
||||
"""
|
||||
Stores birthday date of the person represented by the contact. The element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'birthday'
|
||||
when = 'when'
|
||||
|
||||
|
||||
class CalendarLink(atom.core.XmlElement):
|
||||
"""
|
||||
Storage for URL of the contact's calendar. The element can be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'calendarLink'
|
||||
rel = 'rel'
|
||||
label = 'label'
|
||||
primary = 'primary'
|
||||
href = 'href'
|
||||
|
||||
|
||||
class DirectoryServer(atom.core.XmlElement):
|
||||
"""
|
||||
A directory server associated with this contact.
|
||||
May not be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'directoryServer'
|
||||
|
||||
|
||||
class Event(atom.core.XmlElement):
|
||||
"""
|
||||
These elements describe events associated with a contact.
|
||||
They may be repeated
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'event'
|
||||
label = 'label'
|
||||
rel = 'rel'
|
||||
when = gdata.data.When
|
||||
|
||||
|
||||
class ExternalId(atom.core.XmlElement):
|
||||
"""
|
||||
Describes an ID of the contact in an external system of some kind.
|
||||
This element may be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'externalId'
|
||||
|
||||
|
||||
def ExternalIdFromString(xml_string):
|
||||
return atom.core.parse(ExternalId, xml_string)
|
||||
|
||||
|
||||
class Gender(atom.core.XmlElement):
|
||||
"""
|
||||
Specifies the gender of the person represented by the contact.
|
||||
The element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'directoryServer'
|
||||
value = 'value'
|
||||
|
||||
|
||||
class Hobby(atom.core.XmlElement):
|
||||
"""
|
||||
Describes an ID of the contact in an external system of some kind.
|
||||
This element may be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'hobby'
|
||||
|
||||
|
||||
class Initials(atom.core.XmlElement):
|
||||
""" Specifies the initials of the person represented by the contact. The
|
||||
element cannot be repeated. """
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'initials'
|
||||
|
||||
|
||||
class Jot(atom.core.XmlElement):
|
||||
"""
|
||||
Storage for arbitrary pieces of information about the contact. Each jot
|
||||
has a type specified by the rel attribute and a text value.
|
||||
The element can be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'jot'
|
||||
rel = 'rel'
|
||||
|
||||
|
||||
class Language(atom.core.XmlElement):
|
||||
"""
|
||||
Specifies the preferred languages of the contact.
|
||||
The element can be repeated.
|
||||
|
||||
The language must be specified using one of two mutually exclusive methods:
|
||||
using the freeform @label attribute, or using the @code attribute, whose value
|
||||
must conform to the IETF BCP 47 specification.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'language'
|
||||
code = 'code'
|
||||
label = 'label'
|
||||
|
||||
|
||||
class MaidenName(atom.core.XmlElement):
|
||||
"""
|
||||
Specifies maiden name of the person represented by the contact.
|
||||
The element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'maidenName'
|
||||
|
||||
|
||||
class Mileage(atom.core.XmlElement):
|
||||
"""
|
||||
Specifies the mileage for the entity represented by the contact.
|
||||
Can be used for example to document distance needed for reimbursement
|
||||
purposes. The value is not interpreted. The element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'mileage'
|
||||
|
||||
|
||||
class NickName(atom.core.XmlElement):
|
||||
"""
|
||||
Specifies the nickname of the person represented by the contact.
|
||||
The element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'nickname'
|
||||
|
||||
|
||||
class Occupation(atom.core.XmlElement):
|
||||
"""
|
||||
Specifies the occupation/profession of the person specified by the contact.
|
||||
The element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'occupation'
|
||||
|
||||
|
||||
class Priority(atom.core.XmlElement):
|
||||
"""
|
||||
Classifies importance of the contact into 3 categories:
|
||||
* Low
|
||||
* Normal
|
||||
* High
|
||||
|
||||
The priority element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'priority'
|
||||
|
||||
|
||||
class Relation(atom.core.XmlElement):
|
||||
"""
|
||||
This element describe another entity (usually a person) that is in a
|
||||
relation of some kind with the contact.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'relation'
|
||||
rel = 'rel'
|
||||
label = 'label'
|
||||
|
||||
|
||||
class Sensitivity(atom.core.XmlElement):
|
||||
"""
|
||||
Classifies sensitivity of the contact into the following categories:
|
||||
* Confidential
|
||||
* Normal
|
||||
* Personal
|
||||
* Private
|
||||
|
||||
The sensitivity element cannot be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'sensitivity'
|
||||
rel = 'rel'
|
||||
|
||||
|
||||
class UserDefinedField(atom.core.XmlElement):
|
||||
"""
|
||||
Represents an arbitrary key-value pair attached to the contact.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'userDefinedField'
|
||||
key = 'key'
|
||||
value = 'value'
|
||||
|
||||
|
||||
def UserDefinedFieldFromString(xml_string):
|
||||
return atom.core.parse(UserDefinedField, xml_string)
|
||||
|
||||
|
||||
class Website(atom.core.XmlElement):
|
||||
"""
|
||||
Describes websites associated with the contact, including links.
|
||||
May be repeated.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'website'
|
||||
|
||||
href = 'href'
|
||||
label = 'label'
|
||||
primary = 'primary'
|
||||
rel = 'rel'
|
||||
|
||||
|
||||
def WebsiteFromString(xml_string):
|
||||
return atom.core.parse(Website, xml_string)
|
||||
|
||||
|
||||
class HouseName(atom.core.XmlElement):
|
||||
"""
|
||||
Used in places where houses or buildings have names (and
|
||||
not necessarily numbers), eg. "The Pillars".
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'housename'
|
||||
|
||||
|
||||
class Street(atom.core.XmlElement):
|
||||
"""
|
||||
Can be street, avenue, road, etc. This element also includes the house
|
||||
number and room/apartment/flat/floor number.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'street'
|
||||
|
||||
|
||||
class POBox(atom.core.XmlElement):
|
||||
"""
|
||||
Covers actual P.O. boxes, drawers, locked bags, etc. This is usually but not
|
||||
always mutually exclusive with street
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'pobox'
|
||||
|
||||
|
||||
class Neighborhood(atom.core.XmlElement):
|
||||
"""
|
||||
This is used to disambiguate a street address when a city contains more than
|
||||
one street with the same name, or to specify a small place whose mail is
|
||||
routed through a larger postal town. In China it could be a county or a
|
||||
minor city.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'neighborhood'
|
||||
|
||||
|
||||
class City(atom.core.XmlElement):
|
||||
"""
|
||||
Can be city, village, town, borough, etc. This is the postal town and not
|
||||
necessarily the place of residence or place of business.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'city'
|
||||
|
||||
|
||||
class SubRegion(atom.core.XmlElement):
|
||||
"""
|
||||
Handles administrative districts such as U.S. or U.K. counties that are not
|
||||
used for mail addressing purposes. Subregion is not intended for
|
||||
delivery addresses.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'subregion'
|
||||
|
||||
|
||||
class Region(atom.core.XmlElement):
|
||||
"""
|
||||
A state, province, county (in Ireland), Land (in Germany),
|
||||
departement (in France), etc.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'region'
|
||||
|
||||
|
||||
class PostalCode(atom.core.XmlElement):
|
||||
"""
|
||||
Postal code. Usually country-wide, but sometimes specific to the
|
||||
city (e.g. "2" in "Dublin 2, Ireland" addresses).
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'postcode'
|
||||
|
||||
|
||||
class Country(atom.core.XmlElement):
|
||||
""" The name or code of the country. """
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'country'
|
||||
|
||||
|
||||
class PersonEntry(gdata.data.BatchEntry):
|
||||
"""Represents a google contact"""
|
||||
|
||||
billing_information = BillingInformation
|
||||
birthday = Birthday
|
||||
calendar_link = [CalendarLink]
|
||||
directory_server = DirectoryServer
|
||||
event = [Event]
|
||||
external_id = [ExternalId]
|
||||
gender = Gender
|
||||
hobby = [Hobby]
|
||||
initals = Initials
|
||||
jot = [Jot]
|
||||
language= [Language]
|
||||
maiden_name = MaidenName
|
||||
mileage = Mileage
|
||||
nickname = NickName
|
||||
occupation = Occupation
|
||||
priority = Priority
|
||||
relation = [Relation]
|
||||
sensitivity = Sensitivity
|
||||
user_defined_field = [UserDefinedField]
|
||||
website = [Website]
|
||||
|
||||
name = gdata.data.Name
|
||||
phone_number = [gdata.data.PhoneNumber]
|
||||
organization = gdata.data.Organization
|
||||
postal_address = [gdata.data.PostalAddress]
|
||||
email = [gdata.data.Email]
|
||||
im = [gdata.data.Im]
|
||||
structured_postal_address = [gdata.data.StructuredPostalAddress]
|
||||
extended_property = [gdata.data.ExtendedProperty]
|
||||
|
||||
|
||||
class Deleted(atom.core.XmlElement):
|
||||
"""If present, indicates that this contact has been deleted."""
|
||||
_qname = gdata.GDATA_TEMPLATE % 'deleted'
|
||||
|
||||
|
||||
class GroupMembershipInfo(atom.core.XmlElement):
|
||||
"""
|
||||
Identifies the group to which the contact belongs or belonged.
|
||||
The group is referenced by its id.
|
||||
"""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'groupMembershipInfo'
|
||||
|
||||
href = 'href'
|
||||
deleted = 'deleted'
|
||||
|
||||
|
||||
class ContactEntry(PersonEntry):
|
||||
"""A Google Contacts flavor of an Atom Entry."""
|
||||
|
||||
deleted = Deleted
|
||||
group_membership_info = [GroupMembershipInfo]
|
||||
organization = gdata.data.Organization
|
||||
|
||||
def GetPhotoLink(self):
|
||||
for a_link in self.link:
|
||||
if a_link.rel == PHOTO_LINK_REL:
|
||||
return a_link
|
||||
return None
|
||||
|
||||
def GetPhotoEditLink(self):
|
||||
for a_link in self.link:
|
||||
if a_link.rel == PHOTO_EDIT_LINK_REL:
|
||||
return a_link
|
||||
return None
|
||||
|
||||
|
||||
class ContactsFeed(gdata.data.BatchFeed):
|
||||
"""A collection of Contacts."""
|
||||
entry = [ContactEntry]
|
||||
|
||||
|
||||
class SystemGroup(atom.core.XmlElement):
|
||||
"""The contacts systemGroup element.
|
||||
|
||||
When used within a contact group entry, indicates that the group in
|
||||
question is one of the predefined system groups."""
|
||||
|
||||
_qname = CONTACTS_TEMPLATE % 'systemGroup'
|
||||
id = 'id'
|
||||
|
||||
|
||||
class GroupEntry(gdata.data.BatchEntry):
|
||||
"""Represents a contact group."""
|
||||
extended_property = [gdata.data.ExtendedProperty]
|
||||
system_group = SystemGroup
|
||||
|
||||
|
||||
class GroupsFeed(gdata.data.BatchFeed):
|
||||
"""A Google contact groups feed flavor of an Atom Feed."""
|
||||
entry = [GroupEntry]
|
||||
|
||||
|
||||
class ProfileEntry(PersonEntry):
|
||||
"""A Google Profiles flavor of an Atom Entry."""
|
||||
|
||||
|
||||
def ProfileEntryFromString(xml_string):
|
||||
"""Converts an XML string into a ProfileEntry object.
|
||||
|
||||
Args:
|
||||
xml_string: string The XML describing a Profile entry.
|
||||
|
||||
Returns:
|
||||
A ProfileEntry object corresponding to the given XML.
|
||||
"""
|
||||
return atom.core.parse(ProfileEntry, xml_string)
|
||||
|
||||
|
||||
class ProfilesFeed(gdata.data.BatchFeed):
|
||||
"""A Google Profiles feed flavor of an Atom Feed."""
|
||||
_qname = atom.data.ATOM_TEMPLATE % 'feed'
|
||||
entry = [ProfileEntry]
|
||||
|
||||
|
||||
def ProfilesFeedFromString(xml_string):
|
||||
"""Converts an XML string into a ProfilesFeed object.
|
||||
|
||||
Args:
|
||||
xml_string: string The XML describing a Profiles feed.
|
||||
|
||||
Returns:
|
||||
A ProfilesFeed object corresponding to the given XML.
|
||||
"""
|
||||
return atom.core.parse(ProfilesFeed, xml_string)
|
||||
|
||||
|
427
python/gdata/contacts/service.py
Normal file
427
python/gdata/contacts/service.py
Normal file
@@ -0,0 +1,427 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2009 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""ContactsService extends the GDataService for Google Contacts operations.
|
||||
|
||||
ContactsService: Provides methods to query feeds and manipulate items.
|
||||
Extends GDataService.
|
||||
|
||||
DictionaryToParamList: Function which converts a dictionary into a list of
|
||||
URL arguments (represented as strings). This is a
|
||||
utility function used in CRUD operations.
|
||||
"""
|
||||
|
||||
__author__ = 'dbrattli (Dag Brattli)'
|
||||
|
||||
|
||||
import gdata
|
||||
import gdata.calendar
|
||||
import gdata.service
|
||||
|
||||
|
||||
DEFAULT_BATCH_URL = ('http://www.google.com/m8/feeds/contacts/default/full'
|
||||
'/batch')
|
||||
DEFAULT_PROFILES_BATCH_URL = ('http://www.google.com'
|
||||
'/m8/feeds/profiles/default/full/batch')
|
||||
|
||||
GDATA_VER_HEADER = 'GData-Version'
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RequestError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class ContactsService(gdata.service.GDataService):
|
||||
"""Client for the Google Contacts service."""
|
||||
|
||||
def __init__(self, email=None, password=None, source=None,
|
||||
server='www.google.com', additional_headers=None,
|
||||
contact_list='default', **kwargs):
|
||||
"""Creates a client for the Contacts service.
|
||||
|
||||
Args:
|
||||
email: string (optional) The user's email address, used for
|
||||
authentication.
|
||||
password: string (optional) The user's password.
|
||||
source: string (optional) The name of the user's application.
|
||||
server: string (optional) The name of the server to which a connection
|
||||
will be opened. Default value: 'www.google.com'.
|
||||
contact_list: string (optional) The name of the default contact list to
|
||||
use when no URI is specified to the methods of the service.
|
||||
Default value: 'default' (the logged in user's contact list).
|
||||
**kwargs: The other parameters to pass to gdata.service.GDataService
|
||||
constructor.
|
||||
"""
|
||||
|
||||
self.contact_list = contact_list
|
||||
gdata.service.GDataService.__init__(
|
||||
self, email=email, password=password, service='cp', source=source,
|
||||
server=server, additional_headers=additional_headers, **kwargs)
|
||||
|
||||
def GetFeedUri(self, kind='contacts', contact_list=None, projection='full',
|
||||
scheme=None):
|
||||
"""Builds a feed URI.
|
||||
|
||||
Args:
|
||||
kind: The type of feed to return, typically 'groups' or 'contacts'.
|
||||
Default value: 'contacts'.
|
||||
contact_list: The contact list to return a feed for.
|
||||
Default value: self.contact_list.
|
||||
projection: The projection to apply to the feed contents, for example
|
||||
'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'.
|
||||
scheme: The URL scheme such as 'http' or 'https', None to return a
|
||||
relative URI without hostname.
|
||||
|
||||
Returns:
|
||||
A feed URI using the given kind, contact list, and projection.
|
||||
Example: '/m8/feeds/contacts/default/full'.
|
||||
"""
|
||||
contact_list = contact_list or self.contact_list
|
||||
if kind == 'profiles':
|
||||
contact_list = 'domain/%s' % contact_list
|
||||
prefix = scheme and '%s://%s' % (scheme, self.server) or ''
|
||||
return '%s/m8/feeds/%s/%s/%s' % (prefix, kind, contact_list, projection)
|
||||
|
||||
def GetContactsFeed(self, uri=None):
|
||||
uri = uri or self.GetFeedUri()
|
||||
return self.Get(uri, converter=gdata.contacts.ContactsFeedFromString)
|
||||
|
||||
def GetContact(self, uri):
|
||||
return self.Get(uri, converter=gdata.contacts.ContactEntryFromString)
|
||||
|
||||
def CreateContact(self, new_contact, insert_uri=None, url_params=None,
|
||||
escape_params=True):
|
||||
"""Adds an new contact to Google Contacts.
|
||||
|
||||
Args:
|
||||
new_contact: atom.Entry or subclass A new contact which is to be added to
|
||||
Google Contacts.
|
||||
insert_uri: the URL to post new contacts to the feed
|
||||
url_params: dict (optional) Additional URL parameters to be included
|
||||
in the insertion request.
|
||||
escape_params: boolean (optional) If true, the url_parameters will be
|
||||
escaped before they are included in the request.
|
||||
|
||||
Returns:
|
||||
On successful insert, an entry containing the contact created
|
||||
On failure, a RequestError is raised of the form:
|
||||
{'status': HTTP status code from server,
|
||||
'reason': HTTP reason from the server,
|
||||
'body': HTTP body of the server's response}
|
||||
"""
|
||||
insert_uri = insert_uri or self.GetFeedUri()
|
||||
return self.Post(new_contact, insert_uri, url_params=url_params,
|
||||
escape_params=escape_params,
|
||||
converter=gdata.contacts.ContactEntryFromString)
|
||||
|
||||
def UpdateContact(self, edit_uri, updated_contact, url_params=None,
|
||||
escape_params=True):
|
||||
"""Updates an existing contact.
|
||||
|
||||
Args:
|
||||
edit_uri: string The edit link URI for the element being updated
|
||||
updated_contact: string, atom.Entry or subclass containing
|
||||
the Atom Entry which will replace the contact which is
|
||||
stored at the edit_url
|
||||
url_params: dict (optional) Additional URL parameters to be included
|
||||
in the update request.
|
||||
escape_params: boolean (optional) If true, the url_parameters will be
|
||||
escaped before they are included in the request.
|
||||
|
||||
Returns:
|
||||
On successful update, a httplib.HTTPResponse containing the server's
|
||||
response to the PUT request.
|
||||
On failure, a RequestError is raised of the form:
|
||||
{'status': HTTP status code from server,
|
||||
'reason': HTTP reason from the server,
|
||||
'body': HTTP body of the server's response}
|
||||
"""
|
||||
return self.Put(updated_contact, self._CleanUri(edit_uri),
|
||||
url_params=url_params,
|
||||
escape_params=escape_params,
|
||||
converter=gdata.contacts.ContactEntryFromString)
|
||||
|
||||
def DeleteContact(self, edit_uri, extra_headers=None,
|
||||
url_params=None, escape_params=True):
|
||||
"""Removes an contact with the specified ID from Google Contacts.
|
||||
|
||||
Args:
|
||||
edit_uri: string The edit URL of the entry to be deleted. Example:
|
||||
'/m8/feeds/contacts/default/full/xxx/yyy'
|
||||
url_params: dict (optional) Additional URL parameters to be included
|
||||
in the deletion request.
|
||||
escape_params: boolean (optional) If true, the url_parameters will be
|
||||
escaped before they are included in the request.
|
||||
|
||||
Returns:
|
||||
On successful delete, a httplib.HTTPResponse containing the server's
|
||||
response to the DELETE request.
|
||||
On failure, a RequestError is raised of the form:
|
||||
{'status': HTTP status code from server,
|
||||
'reason': HTTP reason from the server,
|
||||
'body': HTTP body of the server's response}
|
||||
"""
|
||||
return self.Delete(self._CleanUri(edit_uri),
|
||||
url_params=url_params, escape_params=escape_params)
|
||||
|
||||
def GetGroupsFeed(self, uri=None):
|
||||
uri = uri or self.GetFeedUri('groups')
|
||||
return self.Get(uri, converter=gdata.contacts.GroupsFeedFromString)
|
||||
|
||||
def CreateGroup(self, new_group, insert_uri=None, url_params=None,
|
||||
escape_params=True):
|
||||
insert_uri = insert_uri or self.GetFeedUri('groups')
|
||||
return self.Post(new_group, insert_uri, url_params=url_params,
|
||||
escape_params=escape_params,
|
||||
converter=gdata.contacts.GroupEntryFromString)
|
||||
|
||||
def UpdateGroup(self, edit_uri, updated_group, url_params=None,
|
||||
escape_params=True):
|
||||
return self.Put(updated_group, self._CleanUri(edit_uri),
|
||||
url_params=url_params,
|
||||
escape_params=escape_params,
|
||||
converter=gdata.contacts.GroupEntryFromString)
|
||||
|
||||
def DeleteGroup(self, edit_uri, extra_headers=None,
|
||||
url_params=None, escape_params=True):
|
||||
return self.Delete(self._CleanUri(edit_uri),
|
||||
url_params=url_params, escape_params=escape_params)
|
||||
|
||||
def ChangePhoto(self, media, contact_entry_or_url, content_type=None,
|
||||
content_length=None):
|
||||
"""Change the photo for the contact by uploading a new photo.
|
||||
|
||||
Performs a PUT against the photo edit URL to send the binary data for the
|
||||
photo.
|
||||
|
||||
Args:
|
||||
media: filename, file-like-object, or a gdata.MediaSource object to send.
|
||||
contact_entry_or_url: ContactEntry or str If it is a ContactEntry, this
|
||||
method will search for an edit photo link URL and
|
||||
perform a PUT to the URL.
|
||||
content_type: str (optional) the mime type for the photo data. This is
|
||||
necessary if media is a file or file name, but if media
|
||||
is a MediaSource object then the media object can contain
|
||||
the mime type. If media_type is set, it will override the
|
||||
mime type in the media object.
|
||||
content_length: int or str (optional) Specifying the content length is
|
||||
only required if media is a file-like object. If media
|
||||
is a filename, the length is determined using
|
||||
os.path.getsize. If media is a MediaSource object, it is
|
||||
assumed that it already contains the content length.
|
||||
"""
|
||||
if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry):
|
||||
url = contact_entry_or_url.GetPhotoEditLink().href
|
||||
else:
|
||||
url = contact_entry_or_url
|
||||
if isinstance(media, gdata.MediaSource):
|
||||
payload = media
|
||||
# If the media object is a file-like object, then use it as the file
|
||||
# handle in the in the MediaSource.
|
||||
elif hasattr(media, 'read'):
|
||||
payload = gdata.MediaSource(file_handle=media,
|
||||
content_type=content_type, content_length=content_length)
|
||||
# Assume that the media object is a file name.
|
||||
else:
|
||||
payload = gdata.MediaSource(content_type=content_type,
|
||||
content_length=content_length, file_path=media)
|
||||
return self.Put(payload, url)
|
||||
|
||||
def GetPhoto(self, contact_entry_or_url):
|
||||
"""Retrives the binary data for the contact's profile photo as a string.
|
||||
|
||||
Args:
|
||||
contact_entry_or_url: a gdata.contacts.ContactEntry objecr or a string
|
||||
containing the photo link's URL. If the contact entry does not
|
||||
contain a photo link, the image will not be fetched and this method
|
||||
will return None.
|
||||
"""
|
||||
# TODO: add the ability to write out the binary image data to a file,
|
||||
# reading and writing a chunk at a time to avoid potentially using up
|
||||
# large amounts of memory.
|
||||
url = None
|
||||
if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry):
|
||||
photo_link = contact_entry_or_url.GetPhotoLink()
|
||||
if photo_link:
|
||||
url = photo_link.href
|
||||
else:
|
||||
url = contact_entry_or_url
|
||||
if url:
|
||||
return self.Get(url, converter=str)
|
||||
else:
|
||||
return None
|
||||
|
||||
def DeletePhoto(self, contact_entry_or_url):
|
||||
url = None
|
||||
if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry):
|
||||
url = contact_entry_or_url.GetPhotoEditLink().href
|
||||
else:
|
||||
url = contact_entry_or_url
|
||||
if url:
|
||||
self.Delete(url)
|
||||
|
||||
def GetProfilesFeed(self, uri=None):
|
||||
"""Retrieves a feed containing all domain's profiles.
|
||||
|
||||
Args:
|
||||
uri: string (optional) the URL to retrieve the profiles feed,
|
||||
for example /m8/feeds/profiles/default/full
|
||||
|
||||
Returns:
|
||||
On success, a ProfilesFeed containing the profiles.
|
||||
On failure, raises a RequestError.
|
||||
"""
|
||||
|
||||
uri = uri or self.GetFeedUri('profiles')
|
||||
return self.Get(uri,
|
||||
converter=gdata.contacts.ProfilesFeedFromString)
|
||||
|
||||
def GetProfile(self, uri):
|
||||
"""Retrieves a domain's profile for the user.
|
||||
|
||||
Args:
|
||||
uri: string the URL to retrieve the profiles feed,
|
||||
for example /m8/feeds/profiles/default/full/username
|
||||
|
||||
Returns:
|
||||
On success, a ProfileEntry containing the profile for the user.
|
||||
On failure, raises a RequestError
|
||||
"""
|
||||
return self.Get(uri,
|
||||
converter=gdata.contacts.ProfileEntryFromString)
|
||||
|
||||
def UpdateProfile(self, edit_uri, updated_profile, url_params=None,
|
||||
escape_params=True):
|
||||
"""Updates an existing profile.
|
||||
|
||||
Args:
|
||||
edit_uri: string The edit link URI for the element being updated
|
||||
updated_profile: string atom.Entry or subclass containing
|
||||
the Atom Entry which will replace the profile which is
|
||||
stored at the edit_url.
|
||||
url_params: dict (optional) Additional URL parameters to be included
|
||||
in the update request.
|
||||
escape_params: boolean (optional) If true, the url_params will be
|
||||
escaped before they are included in the request.
|
||||
|
||||
Returns:
|
||||
On successful update, a httplib.HTTPResponse containing the server's
|
||||
response to the PUT request.
|
||||
On failure, raises a RequestError.
|
||||
"""
|
||||
return self.Put(updated_profile, self._CleanUri(edit_uri),
|
||||
url_params=url_params, escape_params=escape_params,
|
||||
converter=gdata.contacts.ProfileEntryFromString)
|
||||
|
||||
def ExecuteBatch(self, batch_feed, url,
|
||||
converter=gdata.contacts.ContactsFeedFromString):
|
||||
"""Sends a batch request feed to the server.
|
||||
|
||||
Args:
|
||||
batch_feed: gdata.contacts.ContactFeed A feed containing batch
|
||||
request entries. Each entry contains the operation to be performed
|
||||
on the data contained in the entry. For example an entry with an
|
||||
operation type of insert will be used as if the individual entry
|
||||
had been inserted.
|
||||
url: str The batch URL to which these operations should be applied.
|
||||
converter: Function (optional) The function used to convert the server's
|
||||
response to an object. The default value is ContactsFeedFromString.
|
||||
|
||||
Returns:
|
||||
The results of the batch request's execution on the server. If the
|
||||
default converter is used, this is stored in a ContactsFeed.
|
||||
"""
|
||||
return self.Post(batch_feed, url, converter=converter)
|
||||
|
||||
def ExecuteBatchProfiles(self, batch_feed, url,
|
||||
converter=gdata.contacts.ProfilesFeedFromString):
|
||||
"""Sends a batch request feed to the server.
|
||||
|
||||
Args:
|
||||
batch_feed: gdata.profiles.ProfilesFeed A feed containing batch
|
||||
request entries. Each entry contains the operation to be performed
|
||||
on the data contained in the entry. For example an entry with an
|
||||
operation type of insert will be used as if the individual entry
|
||||
had been inserted.
|
||||
url: string The batch URL to which these operations should be applied.
|
||||
converter: Function (optional) The function used to convert the server's
|
||||
response to an object. The default value is
|
||||
gdata.profiles.ProfilesFeedFromString.
|
||||
|
||||
Returns:
|
||||
The results of the batch request's execution on the server. If the
|
||||
default converter is used, this is stored in a ProfilesFeed.
|
||||
"""
|
||||
return self.Post(batch_feed, url, converter=converter)
|
||||
|
||||
def _CleanUri(self, uri):
|
||||
"""Sanitizes a feed URI.
|
||||
|
||||
Args:
|
||||
uri: The URI to sanitize, can be relative or absolute.
|
||||
|
||||
Returns:
|
||||
The given URI without its http://server prefix, if any.
|
||||
Keeps the leading slash of the URI.
|
||||
"""
|
||||
url_prefix = 'http://%s' % self.server
|
||||
if uri.startswith(url_prefix):
|
||||
uri = uri[len(url_prefix):]
|
||||
return uri
|
||||
|
||||
class ContactsQuery(gdata.service.Query):
|
||||
|
||||
def __init__(self, feed=None, text_query=None, params=None,
|
||||
categories=None, group=None):
|
||||
self.feed = feed or '/m8/feeds/contacts/default/full'
|
||||
if group:
|
||||
self._SetGroup(group)
|
||||
gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query,
|
||||
params=params, categories=categories)
|
||||
|
||||
def _GetGroup(self):
|
||||
if 'group' in self:
|
||||
return self['group']
|
||||
else:
|
||||
return None
|
||||
|
||||
def _SetGroup(self, group_id):
|
||||
self['group'] = group_id
|
||||
|
||||
group = property(_GetGroup, _SetGroup,
|
||||
doc='The group query parameter to find only contacts in this group')
|
||||
|
||||
class GroupsQuery(gdata.service.Query):
|
||||
|
||||
def __init__(self, feed=None, text_query=None, params=None,
|
||||
categories=None):
|
||||
self.feed = feed or '/m8/feeds/groups/default/full'
|
||||
gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query,
|
||||
params=params, categories=categories)
|
||||
|
||||
|
||||
class ProfilesQuery(gdata.service.Query):
|
||||
"""Constructs a query object for the profiles feed."""
|
||||
|
||||
def __init__(self, feed=None, text_query=None, params=None,
|
||||
categories=None):
|
||||
self.feed = feed or '/m8/feeds/profiles/default/full'
|
||||
gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query,
|
||||
params=params, categories=categories)
|
Reference in New Issue
Block a user