/gdata/photos/__init__.py
http://radioappz.googlecode.com/ · Python · 1112 lines · 980 code · 51 blank · 81 comment · 48 complexity · 4c35c94c27b474ca0088f8b0c41d238f MD5 · raw file
- # -*-*- encoding: utf-8 -*-*-
- #
- # This is the base file for the PicasaWeb python client.
- # It is used for lower level operations.
- #
- # $Id: __init__.py 148 2007-10-28 15:09:19Z havard.gulldahl $
- #
- # Copyright 2007 H?vard Gulldahl
- # Portions (C) 2006 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.
- """This module provides a pythonic, gdata-centric interface to Google Photos
- (a.k.a. Picasa Web Services.
- It is modelled after the gdata/* interfaces from the gdata-python-client
- project[1] by Google.
- You'll find the user-friendly api in photos.service. Please see the
- documentation or live help() system for available methods.
- [1]: http://gdata-python-client.googlecode.com/
- """
- __author__ = u'havard@gulldahl.no'# (H?vard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__
- __license__ = 'Apache License v2'
- __version__ = '$Revision: 164 $'[11:-2]
- import re
- try:
- from xml.etree import cElementTree as ElementTree
- except ImportError:
- try:
- import cElementTree as ElementTree
- except ImportError:
- try:
- from xml.etree import ElementTree
- except ImportError:
- from elementtree import ElementTree
- import atom
- import gdata
- # importing google photo submodules
- import gdata.media as Media, gdata.exif as Exif, gdata.geo as Geo
- # XML namespaces which are often used in Google Photo elements
- PHOTOS_NAMESPACE = 'http://schemas.google.com/photos/2007'
- MEDIA_NAMESPACE = 'http://search.yahoo.com/mrss/'
- EXIF_NAMESPACE = 'http://schemas.google.com/photos/exif/2007'
- OPENSEARCH_NAMESPACE = 'http://a9.com/-/spec/opensearchrss/1.0/'
- GEO_NAMESPACE = 'http://www.w3.org/2003/01/geo/wgs84_pos#'
- GML_NAMESPACE = 'http://www.opengis.net/gml'
- GEORSS_NAMESPACE = 'http://www.georss.org/georss'
- PHEED_NAMESPACE = 'http://www.pheed.com/pheed/'
- BATCH_NAMESPACE = 'http://schemas.google.com/gdata/batch'
- class PhotosBaseElement(atom.AtomBase):
- """Base class for elements in the PHOTO_NAMESPACE. To add new elements,
- you only need to add the element tag name to self._tag
- """
-
- _tag = ''
- _namespace = PHOTOS_NAMESPACE
- _children = atom.AtomBase._children.copy()
- _attributes = atom.AtomBase._attributes.copy()
- def __init__(self, name=None, extension_elements=None,
- extension_attributes=None, text=None):
- self.name = name
- self.text = text
- self.extension_elements = extension_elements or []
- self.extension_attributes = extension_attributes or {}
- #def __str__(self):
- #return str(self.text)
- #def __unicode__(self):
- #return unicode(self.text)
- def __int__(self):
- return int(self.text)
- def bool(self):
- return self.text == 'true'
- class GPhotosBaseFeed(gdata.GDataFeed, gdata.LinkFinder):
- "Base class for all Feeds in gdata.photos"
- _tag = 'feed'
- _namespace = atom.ATOM_NAMESPACE
- _attributes = gdata.GDataFeed._attributes.copy()
- _children = gdata.GDataFeed._children.copy()
- # We deal with Entry elements ourselves
- del _children['{%s}entry' % atom.ATOM_NAMESPACE]
-
- 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.GDataFeed.__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 kind(self):
- "(string) Returns the kind"
- try:
- return self.category[0].term.split('#')[1]
- except IndexError:
- return None
-
- def _feedUri(self, kind):
- "Convenience method to return a uri to a feed of a special kind"
- assert(kind in ('album', 'tag', 'photo', 'comment', 'user'))
- here_href = self.GetSelfLink().href
- if 'kind=%s' % kind in here_href:
- return here_href
- if not 'kind=' in here_href:
- sep = '?'
- if '?' in here_href: sep = '&'
- return here_href + "%skind=%s" % (sep, kind)
- rx = re.match('.*(kind=)(album|tag|photo|comment)', here_href)
- return here_href[:rx.end(1)] + kind + here_href[rx.end(2):]
-
- def _ConvertElementTreeToMember(self, child_tree):
- """Re-implementing the method from AtomBase, since we deal with
- Entry elements specially"""
- category = child_tree.find('{%s}category' % atom.ATOM_NAMESPACE)
- if category is None:
- return atom.AtomBase._ConvertElementTreeToMember(self, child_tree)
- namespace, kind = category.get('term').split('#')
- if namespace != PHOTOS_NAMESPACE:
- return atom.AtomBase._ConvertElementTreeToMember(self, child_tree)
- ## TODO: is it safe to use getattr on gdata.photos?
- entry_class = getattr(gdata.photos, '%sEntry' % kind.title())
- if not hasattr(self, 'entry') or self.entry is None:
- self.entry = []
- self.entry.append(atom._CreateClassFromElementTree(
- entry_class, child_tree))
- class GPhotosBaseEntry(gdata.GDataEntry, gdata.LinkFinder):
- "Base class for all Entry elements in gdata.photos"
- _tag = 'entry'
- _kind = ''
- _namespace = atom.ATOM_NAMESPACE
- _children = gdata.GDataEntry._children.copy()
- _attributes = gdata.GDataEntry._attributes.copy()
-
- def __init__(self, author=None, category=None, content=None,
- atom_id=None, link=None, published=None,
- title=None, updated=None,
- extended_property=None,
- extension_elements=None, extension_attributes=None, text=None):
- gdata.GDataEntry.__init__(self, author=author, category=category,
- content=content, atom_id=atom_id, link=link,
- published=published, title=title,
- updated=updated, text=text,
- extension_elements=extension_elements,
- extension_attributes=extension_attributes)
- self.category.append(
- atom.Category(scheme='http://schemas.google.com/g/2005#kind',
- term = 'http://schemas.google.com/photos/2007#%s' % self._kind))
- def kind(self):
- "(string) Returns the kind"
- try:
- return self.category[0].term.split('#')[1]
- except IndexError:
- return None
-
- def _feedUri(self, kind):
- "Convenience method to get the uri to this entry's feed of the some kind"
- try:
- href = self.GetFeedLink().href
- except AttributeError:
- return None
- sep = '?'
- if '?' in href: sep = '&'
- return '%s%skind=%s' % (href, sep, kind)
- class PhotosBaseEntry(GPhotosBaseEntry):
- pass
- class PhotosBaseFeed(GPhotosBaseFeed):
- pass
- class GPhotosBaseData(object):
- pass
- class Access(PhotosBaseElement):
- """The Google Photo `Access' element.
- The album's access level. Valid values are `public' or `private'.
- In documentation, access level is also referred to as `visibility.'"""
-
- _tag = 'access'
- def AccessFromString(xml_string):
- return atom.CreateClassFromXMLString(Access, xml_string)
- class Albumid(PhotosBaseElement):
- "The Google Photo `Albumid' element"
-
- _tag = 'albumid'
- def AlbumidFromString(xml_string):
- return atom.CreateClassFromXMLString(Albumid, xml_string)
- class BytesUsed(PhotosBaseElement):
- "The Google Photo `BytesUsed' element"
-
- _tag = 'bytesUsed'
- def BytesUsedFromString(xml_string):
- return atom.CreateClassFromXMLString(BytesUsed, xml_string)
- class Client(PhotosBaseElement):
- "The Google Photo `Client' element"
-
- _tag = 'client'
- def ClientFromString(xml_string):
- return atom.CreateClassFromXMLString(Client, xml_string)
- class Checksum(PhotosBaseElement):
- "The Google Photo `Checksum' element"
-
- _tag = 'checksum'
- def ChecksumFromString(xml_string):
- return atom.CreateClassFromXMLString(Checksum, xml_string)
- class CommentCount(PhotosBaseElement):
- "The Google Photo `CommentCount' element"
-
- _tag = 'commentCount'
- def CommentCountFromString(xml_string):
- return atom.CreateClassFromXMLString(CommentCount, xml_string)
- class CommentingEnabled(PhotosBaseElement):
- "The Google Photo `CommentingEnabled' element"
-
- _tag = 'commentingEnabled'
- def CommentingEnabledFromString(xml_string):
- return atom.CreateClassFromXMLString(CommentingEnabled, xml_string)
- class Height(PhotosBaseElement):
- "The Google Photo `Height' element"
-
- _tag = 'height'
- def HeightFromString(xml_string):
- return atom.CreateClassFromXMLString(Height, xml_string)
- class Id(PhotosBaseElement):
- "The Google Photo `Id' element"
-
- _tag = 'id'
- def IdFromString(xml_string):
- return atom.CreateClassFromXMLString(Id, xml_string)
- class Location(PhotosBaseElement):
- "The Google Photo `Location' element"
-
- _tag = 'location'
- def LocationFromString(xml_string):
- return atom.CreateClassFromXMLString(Location, xml_string)
- class MaxPhotosPerAlbum(PhotosBaseElement):
- "The Google Photo `MaxPhotosPerAlbum' element"
-
- _tag = 'maxPhotosPerAlbum'
- def MaxPhotosPerAlbumFromString(xml_string):
- return atom.CreateClassFromXMLString(MaxPhotosPerAlbum, xml_string)
- class Name(PhotosBaseElement):
- "The Google Photo `Name' element"
-
- _tag = 'name'
- def NameFromString(xml_string):
- return atom.CreateClassFromXMLString(Name, xml_string)
- class Nickname(PhotosBaseElement):
- "The Google Photo `Nickname' element"
-
- _tag = 'nickname'
- def NicknameFromString(xml_string):
- return atom.CreateClassFromXMLString(Nickname, xml_string)
- class Numphotos(PhotosBaseElement):
- "The Google Photo `Numphotos' element"
-
- _tag = 'numphotos'
- def NumphotosFromString(xml_string):
- return atom.CreateClassFromXMLString(Numphotos, xml_string)
- class Numphotosremaining(PhotosBaseElement):
- "The Google Photo `Numphotosremaining' element"
-
- _tag = 'numphotosremaining'
- def NumphotosremainingFromString(xml_string):
- return atom.CreateClassFromXMLString(Numphotosremaining, xml_string)
- class Position(PhotosBaseElement):
- "The Google Photo `Position' element"
-
- _tag = 'position'
- def PositionFromString(xml_string):
- return atom.CreateClassFromXMLString(Position, xml_string)
- class Photoid(PhotosBaseElement):
- "The Google Photo `Photoid' element"
-
- _tag = 'photoid'
- def PhotoidFromString(xml_string):
- return atom.CreateClassFromXMLString(Photoid, xml_string)
- class Quotacurrent(PhotosBaseElement):
- "The Google Photo `Quotacurrent' element"
-
- _tag = 'quotacurrent'
- def QuotacurrentFromString(xml_string):
- return atom.CreateClassFromXMLString(Quotacurrent, xml_string)
- class Quotalimit(PhotosBaseElement):
- "The Google Photo `Quotalimit' element"
-
- _tag = 'quotalimit'
- def QuotalimitFromString(xml_string):
- return atom.CreateClassFromXMLString(Quotalimit, xml_string)
- class Rotation(PhotosBaseElement):
- "The Google Photo `Rotation' element"
-
- _tag = 'rotation'
- def RotationFromString(xml_string):
- return atom.CreateClassFromXMLString(Rotation, xml_string)
- class Size(PhotosBaseElement):
- "The Google Photo `Size' element"
-
- _tag = 'size'
- def SizeFromString(xml_string):
- return atom.CreateClassFromXMLString(Size, xml_string)
- class Snippet(PhotosBaseElement):
- """The Google Photo `snippet' element.
- When searching, the snippet element will contain a
- string with the word you're looking for, highlighted in html markup
- E.g. when your query is `hafjell', this element may contain:
- `... here at <b>Hafjell</b>.'
- You'll find this element in searches -- that is, feeds that combine the
- `kind=photo' and `q=yoursearch' parameters in the request.
- See also gphoto:truncated and gphoto:snippettype.
-
- """
-
- _tag = 'snippet'
- def SnippetFromString(xml_string):
- return atom.CreateClassFromXMLString(Snippet, xml_string)
- class Snippettype(PhotosBaseElement):
- """The Google Photo `Snippettype' element
- When searching, this element will tell you the type of element that matches.
- You'll find this element in searches -- that is, feeds that combine the
- `kind=photo' and `q=yoursearch' parameters in the request.
- See also gphoto:snippet and gphoto:truncated.
-
- Possible values and their interpretation:
- o ALBUM_TITLE - The album title matches
- o PHOTO_TAGS - The match is a tag/keyword
- o PHOTO_DESCRIPTION - The match is in the photo's description
- If you discover a value not listed here, please submit a patch to update this docstring.
-
- """
-
- _tag = 'snippettype'
- def SnippettypeFromString(xml_string):
- return atom.CreateClassFromXMLString(Snippettype, xml_string)
- class Thumbnail(PhotosBaseElement):
- """The Google Photo `Thumbnail' element
- Used to display user's photo thumbnail (hackergotchi).
-
- (Not to be confused with the <media:thumbnail> element, which gives you
- small versions of the photo object.)"""
-
- _tag = 'thumbnail'
- def ThumbnailFromString(xml_string):
- return atom.CreateClassFromXMLString(Thumbnail, xml_string)
- class Timestamp(PhotosBaseElement):
- """The Google Photo `Timestamp' element
- Represented as the number of milliseconds since January 1st, 1970.
-
-
- Take a look at the convenience methods .isoformat() and .datetime():
- photo_epoch = Time.text # 1180294337000
- photo_isostring = Time.isoformat() # '2007-05-27T19:32:17.000Z'
- Alternatively:
- photo_datetime = Time.datetime() # (requires python >= 2.3)
- """
-
- _tag = 'timestamp'
- def isoformat(self):
- """(string) Return the timestamp as a ISO 8601 formatted string,
- e.g. '2007-05-27T19:32:17.000Z'
- """
- import time
- epoch = float(self.text)/1000
- return time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(epoch))
-
- def datetime(self):
- """(datetime.datetime) Return the timestamp as a datetime.datetime object
- Requires python 2.3
- """
- import datetime
- epoch = float(self.text)/1000
- return datetime.datetime.fromtimestamp(epoch)
- def TimestampFromString(xml_string):
- return atom.CreateClassFromXMLString(Timestamp, xml_string)
- class Truncated(PhotosBaseElement):
- """The Google Photo `Truncated' element
- You'll find this element in searches -- that is, feeds that combine the
- `kind=photo' and `q=yoursearch' parameters in the request.
- See also gphoto:snippet and gphoto:snippettype.
-
- Possible values and their interpretation:
- 0 -- unknown
- """
-
- _tag = 'Truncated'
- def TruncatedFromString(xml_string):
- return atom.CreateClassFromXMLString(Truncated, xml_string)
- class User(PhotosBaseElement):
- "The Google Photo `User' element"
-
- _tag = 'user'
- def UserFromString(xml_string):
- return atom.CreateClassFromXMLString(User, xml_string)
- class Version(PhotosBaseElement):
- "The Google Photo `Version' element"
-
- _tag = 'version'
- def VersionFromString(xml_string):
- return atom.CreateClassFromXMLString(Version, xml_string)
- class Width(PhotosBaseElement):
- "The Google Photo `Width' element"
-
- _tag = 'width'
- def WidthFromString(xml_string):
- return atom.CreateClassFromXMLString(Width, xml_string)
- class Weight(PhotosBaseElement):
- """The Google Photo `Weight' element.
- The weight of the tag is the number of times the tag
- appears in the collection of tags currently being viewed.
- The default weight is 1, in which case this tags is omitted."""
- _tag = 'weight'
- def WeightFromString(xml_string):
- return atom.CreateClassFromXMLString(Weight, xml_string)
- class CommentAuthor(atom.Author):
- """The Atom `Author' element in CommentEntry entries is augmented to
- contain elements from the PHOTOS_NAMESPACE
- http://groups.google.com/group/Google-Picasa-Data-API/msg/819b0025b5ff5e38
- """
- _children = atom.Author._children.copy()
- _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User)
- _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname)
- _children['{%s}thumbnail' % PHOTOS_NAMESPACE] = ('thumbnail', Thumbnail)
- def CommentAuthorFromString(xml_string):
- return atom.CreateClassFromXMLString(CommentAuthor, xml_string)
- ########################## ################################
- class AlbumData(object):
- _children = {}
- _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id)
- _children['{%s}name' % PHOTOS_NAMESPACE] = ('name', Name)
- _children['{%s}location' % PHOTOS_NAMESPACE] = ('location', Location)
- _children['{%s}access' % PHOTOS_NAMESPACE] = ('access', Access)
- _children['{%s}bytesUsed' % PHOTOS_NAMESPACE] = ('bytesUsed', BytesUsed)
- _children['{%s}timestamp' % PHOTOS_NAMESPACE] = ('timestamp', Timestamp)
- _children['{%s}numphotos' % PHOTOS_NAMESPACE] = ('numphotos', Numphotos)
- _children['{%s}numphotosremaining' % PHOTOS_NAMESPACE] = \
- ('numphotosremaining', Numphotosremaining)
- _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User)
- _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname)
- _children['{%s}commentingEnabled' % PHOTOS_NAMESPACE] = \
- ('commentingEnabled', CommentingEnabled)
- _children['{%s}commentCount' % PHOTOS_NAMESPACE] = \
- ('commentCount', CommentCount)
- ## NOTE: storing media:group as self.media, to create a self-explaining api
- gphoto_id = None
- name = None
- location = None
- access = None
- bytesUsed = None
- timestamp = None
- numphotos = None
- numphotosremaining = None
- user = None
- nickname = None
- commentingEnabled = None
- commentCount = None
- class AlbumEntry(GPhotosBaseEntry, AlbumData):
- """All metadata for a Google Photos Album
- Take a look at AlbumData for metadata accessible as attributes to this object.
- Notes:
- To avoid name clashes, and to create a more sensible api, some
- objects have names that differ from the original elements:
-
- o media:group -> self.media,
- o geo:where -> self.geo,
- o photo:id -> self.gphoto_id
- """
-
- _kind = 'album'
- _children = GPhotosBaseEntry._children.copy()
- _children.update(AlbumData._children.copy())
- # child tags only for Album entries, not feeds
- _children['{%s}where' % GEORSS_NAMESPACE] = ('geo', Geo.Where)
- _children['{%s}group' % MEDIA_NAMESPACE] = ('media', Media.Group)
- media = Media.Group()
- geo = Geo.Where()
-
- def __init__(self, author=None, category=None, content=None,
- atom_id=None, link=None, published=None,
- title=None, updated=None,
- #GPHOTO NAMESPACE:
- gphoto_id=None, name=None, location=None, access=None,
- timestamp=None, numphotos=None, user=None, nickname=None,
- commentingEnabled=None, commentCount=None, thumbnail=None,
- # MEDIA NAMESPACE:
- media=None,
- # GEORSS NAMESPACE:
- geo=None,
- extended_property=None,
- extension_elements=None, extension_attributes=None, text=None):
- GPhotosBaseEntry.__init__(self, author=author, category=category,
- content=content, atom_id=atom_id, link=link,
- published=published, title=title,
- updated=updated, text=text,
- extension_elements=extension_elements,
- extension_attributes=extension_attributes)
- ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id
- self.gphoto_id = gphoto_id
- self.name = name
- self.location = location
- self.access = access
- self.timestamp = timestamp
- self.numphotos = numphotos
- self.user = user
- self.nickname = nickname
- self.commentingEnabled = commentingEnabled
- self.commentCount = commentCount
- self.thumbnail = thumbnail
- self.extended_property = extended_property or []
- self.text = text
- ## NOTE: storing media:group as self.media, and geo:where as geo,
- ## to create a self-explaining api
- self.media = media or Media.Group()
- self.geo = geo or Geo.Where()
- def GetAlbumId(self):
- "Return the id of this album"
-
- return self.GetFeedLink().href.split('/')[-1]
-
- def GetPhotosUri(self):
- "(string) Return the uri to this albums feed of the PhotoEntry kind"
- return self._feedUri('photo')
-
- def GetCommentsUri(self):
- "(string) Return the uri to this albums feed of the CommentEntry kind"
- return self._feedUri('comment')
-
- def GetTagsUri(self):
- "(string) Return the uri to this albums feed of the TagEntry kind"
- return self._feedUri('tag')
- def AlbumEntryFromString(xml_string):
- return atom.CreateClassFromXMLString(AlbumEntry, xml_string)
-
- class AlbumFeed(GPhotosBaseFeed, AlbumData):
- """All metadata for a Google Photos Album, including its sub-elements
-
- This feed represents an album as the container for other objects.
- A Album feed contains entries of
- PhotoEntry, CommentEntry or TagEntry,
- depending on the `kind' parameter in the original query.
- Take a look at AlbumData for accessible attributes.
-
- """
-
- _children = GPhotosBaseFeed._children.copy()
- _children.update(AlbumData._children.copy())
- def GetPhotosUri(self):
- "(string) Return the uri to the same feed, but of the PhotoEntry kind"
-
- return self._feedUri('photo')
-
- def GetTagsUri(self):
- "(string) Return the uri to the same feed, but of the TagEntry kind"
- return self._feedUri('tag')
-
- def GetCommentsUri(self):
- "(string) Return the uri to the same feed, but of the CommentEntry kind"
- return self._feedUri('comment')
-
- def AlbumFeedFromString(xml_string):
- return atom.CreateClassFromXMLString(AlbumFeed, xml_string)
- class PhotoData(object):
- _children = {}
- ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id
- _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id)
- _children['{%s}albumid' % PHOTOS_NAMESPACE] = ('albumid', Albumid)
- _children['{%s}checksum' % PHOTOS_NAMESPACE] = ('checksum', Checksum)
- _children['{%s}client' % PHOTOS_NAMESPACE] = ('client', Client)
- _children['{%s}height' % PHOTOS_NAMESPACE] = ('height', Height)
- _children['{%s}position' % PHOTOS_NAMESPACE] = ('position', Position)
- _children['{%s}rotation' % PHOTOS_NAMESPACE] = ('rotation', Rotation)
- _children['{%s}size' % PHOTOS_NAMESPACE] = ('size', Size)
- _children['{%s}timestamp' % PHOTOS_NAMESPACE] = ('timestamp', Timestamp)
- _children['{%s}version' % PHOTOS_NAMESPACE] = ('version', Version)
- _children['{%s}width' % PHOTOS_NAMESPACE] = ('width', Width)
- _children['{%s}commentingEnabled' % PHOTOS_NAMESPACE] = \
- ('commentingEnabled', CommentingEnabled)
- _children['{%s}commentCount' % PHOTOS_NAMESPACE] = \
- ('commentCount', CommentCount)
- ## NOTE: storing media:group as self.media, exif:tags as self.exif, and
- ## geo:where as self.geo, to create a self-explaining api
- _children['{%s}tags' % EXIF_NAMESPACE] = ('exif', Exif.Tags)
- _children['{%s}where' % GEORSS_NAMESPACE] = ('geo', Geo.Where)
- _children['{%s}group' % MEDIA_NAMESPACE] = ('media', Media.Group)
- # These elements show up in search feeds
- _children['{%s}snippet' % PHOTOS_NAMESPACE] = ('snippet', Snippet)
- _children['{%s}snippettype' % PHOTOS_NAMESPACE] = ('snippettype', Snippettype)
- _children['{%s}truncated' % PHOTOS_NAMESPACE] = ('truncated', Truncated)
- gphoto_id = None
- albumid = None
- checksum = None
- client = None
- height = None
- position = None
- rotation = None
- size = None
- timestamp = None
- version = None
- width = None
- commentingEnabled = None
- commentCount = None
- snippet=None
- snippettype=None
- truncated=None
- media = Media.Group()
- geo = Geo.Where()
- tags = Exif.Tags()
- class PhotoEntry(GPhotosBaseEntry, PhotoData):
- """All metadata for a Google Photos Photo
- Take a look at PhotoData for metadata accessible as attributes to this object.
- Notes:
- To avoid name clashes, and to create a more sensible api, some
- objects have names that differ from the original elements:
-
- o media:group -> self.media,
- o exif:tags -> self.exif,
- o geo:where -> self.geo,
- o photo:id -> self.gphoto_id
- """
- _kind = 'photo'
- _children = GPhotosBaseEntry._children.copy()
- _children.update(PhotoData._children.copy())
-
- def __init__(self, author=None, category=None, content=None,
- atom_id=None, link=None, published=None,
- title=None, updated=None, text=None,
- # GPHOTO NAMESPACE:
- gphoto_id=None, albumid=None, checksum=None, client=None, height=None,
- position=None, rotation=None, size=None, timestamp=None, version=None,
- width=None, commentCount=None, commentingEnabled=None,
- # MEDIARSS NAMESPACE:
- media=None,
- # EXIF_NAMESPACE:
- exif=None,
- # GEORSS NAMESPACE:
- geo=None,
- extension_elements=None, extension_attributes=None):
- GPhotosBaseEntry.__init__(self, author=author, category=category,
- content=content,
- atom_id=atom_id, link=link, published=published,
- title=title, updated=updated, text=text,
- extension_elements=extension_elements,
- extension_attributes=extension_attributes)
-
- ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id
- self.gphoto_id = gphoto_id
- self.albumid = albumid
- self.checksum = checksum
- self.client = client
- self.height = height
- self.position = position
- self.rotation = rotation
- self.size = size
- self.timestamp = timestamp
- self.version = version
- self.width = width
- self.commentingEnabled = commentingEnabled
- self.commentCount = commentCount
- ## NOTE: storing media:group as self.media, to create a self-explaining api
- self.media = media or Media.Group()
- self.exif = exif or Exif.Tags()
- self.geo = geo or Geo.Where()
- def GetPostLink(self):
- "Return the uri to this photo's `POST' link (use it for updates of the object)"
- return self.GetFeedLink()
- def GetCommentsUri(self):
- "Return the uri to this photo's feed of CommentEntry comments"
- return self._feedUri('comment')
- def GetTagsUri(self):
- "Return the uri to this photo's feed of TagEntry tags"
- return self._feedUri('tag')
- def GetAlbumUri(self):
- """Return the uri to the AlbumEntry containing this photo"""
- href = self.GetSelfLink().href
- return href[:href.find('/photoid')]
- def PhotoEntryFromString(xml_string):
- return atom.CreateClassFromXMLString(PhotoEntry, xml_string)
- class PhotoFeed(GPhotosBaseFeed, PhotoData):
- """All metadata for a Google Photos Photo, including its sub-elements
-
- This feed represents a photo as the container for other objects.
- A Photo feed contains entries of
- CommentEntry or TagEntry,
- depending on the `kind' parameter in the original query.
- Take a look at PhotoData for metadata accessible as attributes to this object.
-
- """
- _children = GPhotosBaseFeed._children.copy()
- _children.update(PhotoData._children.copy())
- def GetTagsUri(self):
- "(string) Return the uri to the same feed, but of the TagEntry kind"
-
- return self._feedUri('tag')
-
- def GetCommentsUri(self):
- "(string) Return the uri to the same feed, but of the CommentEntry kind"
-
- return self._feedUri('comment')
- def PhotoFeedFromString(xml_string):
- return atom.CreateClassFromXMLString(PhotoFeed, xml_string)
- class TagData(GPhotosBaseData):
- _children = {}
- _children['{%s}weight' % PHOTOS_NAMESPACE] = ('weight', Weight)
- weight=None
- class TagEntry(GPhotosBaseEntry, TagData):
- """All metadata for a Google Photos Tag
- The actual tag is stored in the .title.text attribute
- """
- _kind = 'tag'
- _children = GPhotosBaseEntry._children.copy()
- _children.update(TagData._children.copy())
- def __init__(self, author=None, category=None, content=None,
- atom_id=None, link=None, published=None,
- title=None, updated=None,
- # GPHOTO NAMESPACE:
- weight=None,
- extended_property=None,
- extension_elements=None, extension_attributes=None, text=None):
- GPhotosBaseEntry.__init__(self, author=author, category=category,
- content=content,
- atom_id=atom_id, link=link, published=published,
- title=title, updated=updated, text=text,
- extension_elements=extension_elements,
- extension_attributes=extension_attributes)
-
- self.weight = weight
- def GetAlbumUri(self):
- """Return the uri to the AlbumEntry containing this tag"""
- href = self.GetSelfLink().href
- pos = href.find('/photoid')
- if pos == -1:
- return None
- return href[:pos]
- def GetPhotoUri(self):
- """Return the uri to the PhotoEntry containing this tag"""
- href = self.GetSelfLink().href
- pos = href.find('/tag')
- if pos == -1:
- return None
- return href[:pos]
- def TagEntryFromString(xml_string):
- return atom.CreateClassFromXMLString(TagEntry, xml_string)
- class TagFeed(GPhotosBaseFeed, TagData):
- """All metadata for a Google Photos Tag, including its sub-elements"""
-
- _children = GPhotosBaseFeed._children.copy()
- _children.update(TagData._children.copy())
-
- def TagFeedFromString(xml_string):
- return atom.CreateClassFromXMLString(TagFeed, xml_string)
- class CommentData(GPhotosBaseData):
- _children = {}
- ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id
- _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id)
- _children['{%s}albumid' % PHOTOS_NAMESPACE] = ('albumid', Albumid)
- _children['{%s}photoid' % PHOTOS_NAMESPACE] = ('photoid', Photoid)
- _children['{%s}author' % atom.ATOM_NAMESPACE] = ('author', [CommentAuthor,])
- gphoto_id=None
- albumid=None
- photoid=None
- author=None
- class CommentEntry(GPhotosBaseEntry, CommentData):
- """All metadata for a Google Photos Comment
-
- The comment is stored in the .content.text attribute,
- with a content type in .content.type.
- """
-
- _kind = 'comment'
- _children = GPhotosBaseEntry._children.copy()
- _children.update(CommentData._children.copy())
- def __init__(self, author=None, category=None, content=None,
- atom_id=None, link=None, published=None,
- title=None, updated=None,
- # GPHOTO NAMESPACE:
- gphoto_id=None, albumid=None, photoid=None,
- extended_property=None,
- extension_elements=None, extension_attributes=None, text=None):
-
- GPhotosBaseEntry.__init__(self, author=author, category=category,
- content=content,
- atom_id=atom_id, link=link, published=published,
- title=title, updated=updated,
- extension_elements=extension_elements,
- extension_attributes=extension_attributes,
- text=text)
-
- self.gphoto_id = gphoto_id
- self.albumid = albumid
- self.photoid = photoid
- def GetCommentId(self):
- """Return the globally unique id of this comment"""
- return self.GetSelfLink().href.split('/')[-1]
- def GetAlbumUri(self):
- """Return the uri to the AlbumEntry containing this comment"""
- href = self.GetSelfLink().href
- return href[:href.find('/photoid')]
- def GetPhotoUri(self):
- """Return the uri to the PhotoEntry containing this comment"""
- href = self.GetSelfLink().href
- return href[:href.find('/commentid')]
- def CommentEntryFromString(xml_string):
- return atom.CreateClassFromXMLString(CommentEntry, xml_string)
- class CommentFeed(GPhotosBaseFeed, CommentData):
- """All metadata for a Google Photos Comment, including its sub-elements"""
-
- _children = GPhotosBaseFeed._children.copy()
- _children.update(CommentData._children.copy())
- def CommentFeedFromString(xml_string):
- return atom.CreateClassFromXMLString(CommentFeed, xml_string)
- class UserData(GPhotosBaseData):
- _children = {}
- _children['{%s}maxPhotosPerAlbum' % PHOTOS_NAMESPACE] = ('maxPhotosPerAlbum', MaxPhotosPerAlbum)
- _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname)
- _children['{%s}quotalimit' % PHOTOS_NAMESPACE] = ('quotalimit', Quotalimit)
- _children['{%s}quotacurrent' % PHOTOS_NAMESPACE] = ('quotacurrent', Quotacurrent)
- _children['{%s}thumbnail' % PHOTOS_NAMESPACE] = ('thumbnail', Thumbnail)
- _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User)
- _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id)
- maxPhotosPerAlbum=None
- nickname=None
- quotalimit=None
- quotacurrent=None
- thumbnail=None
- user=None
- gphoto_id=None
-
- class UserEntry(GPhotosBaseEntry, UserData):
- """All metadata for a Google Photos User
- This entry represents an album owner and all appropriate metadata.
- Take a look at at the attributes of the UserData for metadata available.
- """
- _children = GPhotosBaseEntry._children.copy()
- _children.update(UserData._children.copy())
- _kind = 'user'
-
- def __init__(self, author=None, category=None, content=None,
- atom_id=None, link=None, published=None,
- title=None, updated=None,
- # GPHOTO NAMESPACE:
- gphoto_id=None, maxPhotosPerAlbum=None, nickname=None, quotalimit=None,
- quotacurrent=None, thumbnail=None, user=None,
- extended_property=None,
- extension_elements=None, extension_attributes=None, text=None):
-
- GPhotosBaseEntry.__init__(self, author=author, category=category,
- content=content,
- atom_id=atom_id, link=link, published=published,
- title=title, updated=updated,
- extension_elements=extension_elements,
- extension_attributes=extension_attributes,
- text=text)
-
-
- self.gphoto_id=gphoto_id
- self.maxPhotosPerAlbum=maxPhotosPerAlbum
- self.nickname=nickname
- self.quotalimit=quotalimit
- self.quotacurrent=quotacurrent
- self.thumbnail=thumbnail
- self.user=user
- def GetAlbumsUri(self):
- "(string) Return the uri to this user's feed of the AlbumEntry kind"
- return self._feedUri('album')
-
- def GetPhotosUri(self):
- "(string) Return the uri to this user's feed of the PhotoEntry kind"
- return self._feedUri('photo')
-
- def GetCommentsUri(self):
- "(string) Return the uri to this user's feed of the CommentEntry kind"
- return self._feedUri('comment')
-
- def GetTagsUri(self):
- "(string) Return the uri to this user's feed of the TagEntry kind"
- return self._feedUri('tag')
- def UserEntryFromString(xml_string):
- return atom.CreateClassFromXMLString(UserEntry, xml_string)
-
- class UserFeed(GPhotosBaseFeed, UserData):
- """Feed for a User in the google photos api.
- This feed represents a user as the container for other objects.
- A User feed contains entries of
- AlbumEntry, PhotoEntry, CommentEntry, UserEntry or TagEntry,
- depending on the `kind' parameter in the original query.
- The user feed itself also contains all of the metadata available
- as part of a UserData object."""
- _children = GPhotosBaseFeed._children.copy()
- _children.update(UserData._children.copy())
- def GetAlbumsUri(self):
- """Get the uri to this feed, but with entries of the AlbumEntry kind."""
- return self._feedUri('album')
- def GetTagsUri(self):
- """Get the uri to this feed, but with entries of the TagEntry kind."""
- return self._feedUri('tag')
- def GetPhotosUri(self):
- """Get the uri to this feed, but with entries of the PhotosEntry kind."""
- return self._feedUri('photo')
- def GetCommentsUri(self):
- """Get the uri to this feed, but with entries of the CommentsEntry kind."""
- return self._feedUri('comment')
- def UserFeedFromString(xml_string):
- return atom.CreateClassFromXMLString(UserFeed, xml_string)
-
-
- def AnyFeedFromString(xml_string):
- """Creates an instance of the appropriate feed class from the
- xml string contents.
- Args:
- xml_string: str A string which contains valid XML. The root element
- of the XML string should match the tag and namespace of the desired
- class.
- Returns:
- An instance of the target class with members assigned according to the
- contents of the XML - or a basic gdata.GDataFeed instance if it is
- impossible to determine the appropriate class (look for extra elements
- in GDataFeed's .FindExtensions() and extension_elements[] ).
- """
- tree = ElementTree.fromstring(xml_string)
- category = tree.find('{%s}category' % atom.ATOM_NAMESPACE)
- if category is None:
- # TODO: is this the best way to handle this?
- return atom._CreateClassFromElementTree(GPhotosBaseFeed, tree)
- namespace, kind = category.get('term').split('#')
- if namespace != PHOTOS_NAMESPACE:
- # TODO: is this the best way to handle this?
- return atom._CreateClassFromElementTree(GPhotosBaseFeed, tree)
- ## TODO: is getattr safe this way?
- feed_class = getattr(gdata.photos, '%sFeed' % kind.title())
- return atom._CreateClassFromElementTree(feed_class, tree)
- def AnyEntryFromString(xml_string):
- """Creates an instance of the appropriate entry class from the
- xml string contents.
- Args:
- xml_string: str A string which contains valid XML. The root element
- of the XML string should match the tag and namespace of the desired
- class.
- Returns:
- An instance of the target class with members assigned according to the
- contents of the XML - or a basic gdata.GDataEndry instance if it is
- impossible to determine the appropriate class (look for extra elements
- in GDataEntry's .FindExtensions() and extension_elements[] ).
- """
- tree = ElementTree.fromstring(xml_string)
- category = tree.find('{%s}category' % atom.ATOM_NAMESPACE)
- if category is None:
- # TODO: is this the best way to handle this?
- return atom._CreateClassFromElementTree(GPhotosBaseEntry, tree)
- namespace, kind = category.get('term').split('#')
- if namespace != PHOTOS_NAMESPACE:
- # TODO: is this the best way to handle this?
- return atom._CreateClassFromElementTree(GPhotosBaseEntry, tree)
- ## TODO: is getattr safe this way?
- feed_class = getattr(gdata.photos, '%sEntry' % kind.title())
- return atom._CreateClassFromElementTree(feed_class, tree)