From f1f75efc3a62e7e7c72f46d2655b983aecf73933 Mon Sep 17 00:00:00 2001 From: sanj Date: Thu, 3 Jun 2010 03:57:01 +0530 Subject: [PATCH] forgot to add flickr.py to tree --- vurbanism/flickr.py | 1087 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1087 insertions(+) create mode 100644 vurbanism/flickr.py diff --git a/vurbanism/flickr.py b/vurbanism/flickr.py new file mode 100644 index 0000000..971ad74 --- /dev/null +++ b/vurbanism/flickr.py @@ -0,0 +1,1087 @@ +""" + flickr.py + Copyright 2004-2006 James Clarke + Portions Copyright 2007-2008 Joshua Henderson + +THIS SOFTWARE IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, AND MAY BE +COPIED, MODIFIED OR DISTRIBUTED IN ANY WAY, AS LONG AS THIS NOTICE +AND ACKNOWLEDGEMENT OF AUTHORSHIP REMAIN. + +2007-12-17 + For an upto date TODO list, please see: + http://code.google.com/p/flickrpy/wiki/TodoList + + For information on how to use the Authentication + module, plese see: + http://code.google.com/p/flickrpy/wiki/UserAuthentication + +2006-12-19 + Applied patches from Berco Beute and Wolfram Kriesing. + +""" + +__author__ = "James Clarke " +__version__ = "$Rev: 43 $" +__date__ = "$Date: 2009-12-07 13:34:28 +0530 (Mon, 07 Dec 2009) $" +__copyright__ = "Copyright: 2004-2006 James Clarke; Portions: 2007-2008 Joshua Henderson" + +from urllib import urlencode, urlopen +from xml.dom import minidom +import hashlib +import os + +HOST = 'http://flickr.com' +API = '/services/rest' + +# set these here or using flickr.API_KEY in your application +API_KEY = '' +API_SECRET = '' +email = None +password = None +AUTH = False + + +# The next 2 variables are only importatnt if authentication is used + +# this can be set here or using flickr.tokenPath in your application +# this is the path to the folder containing tokenFile (default: token.txt) +tokenPath = '' + +# this can be set here or using flickr.tokenFile in your application +# this is the name of the file containing the stored token. +tokenFile = 'token.txt' + + +class FlickrError(Exception): pass + +class Photo(object): + """Represents a Flickr Photo.""" + + __readonly = ['id', 'secret', 'server', 'farm', 'isfavorite', 'license', 'rotation', + 'owner', 'dateposted', 'datetaken', 'takengranularity', + 'title', 'description', 'ispublic', 'isfriend', 'isfamily', + 'cancomment', 'canaddmeta', 'comments', 'tags', 'permcomment', + 'permaddmeta'] + + #XXX: Hopefully None won't cause problems + def __init__(self, id, owner=None, dateuploaded=None, \ + title=None, description=None, ispublic=None, \ + isfriend=None, isfamily=None, cancomment=None, \ + canaddmeta=None, comments=None, tags=None, secret=None, \ + isfavorite=None, server=None, farm=None, license=None, rotation=None): + """Must specify id, rest is optional.""" + self.__loaded = False + self.__cancomment = cancomment + self.__canaddmeta = canaddmeta + self.__comments = comments + self.__dateuploaded = dateuploaded + self.__description = description + self.__id = id + self.__license = license + self.__isfamily = isfamily + self.__isfavorite = isfavorite + self.__isfriend = isfriend + self.__ispublic = ispublic + self.__owner = owner + self.__rotation = rotation + self.__secret = secret + self.__server = server + self.__farm = farm + self.__tags = tags + self.__title = title + + self.__dateposted = None + self.__datetaken = None + self.__takengranularity = None + self.__permcomment = None + self.__permaddmeta = None + + def __setattr__(self, key, value): + if key in self.__class__.__readonly: + raise AttributeError("The attribute %s is read-only." % key) + else: + super(Photo, self).__setattr__(key, value) + + def __getattr__(self, key): + if not self.__loaded: + self._load_properties() + if key in self.__class__.__readonly: + return super(Photo, self).__getattribute__("_%s__%s" % (self.__class__.__name__, key)) + else: + return super(Photo, self).__getattribute__(key) + + def _load_properties(self): + """Loads the properties from Flickr.""" + self.__loaded = True + + method = 'flickr.photos.getInfo' + data = _doget(method, photo_id=self.id) + + photo = data.rsp.photo + + self.__secret = photo.secret + self.__server = photo.server + self.__farm = photo.farm + self.__isfavorite = photo.isfavorite + self.__license = photo.license + self.__rotation = photo.rotation + + + + owner = photo.owner + self.__owner = User(owner.nsid, username=owner.username,\ + realname=owner.realname,\ + location=owner.location) + + self.__title = photo.title.text + self.__description = photo.description.text + self.__ispublic = photo.visibility.ispublic + self.__isfriend = photo.visibility.isfriend + self.__isfamily = photo.visibility.isfamily + + self.__dateposted = photo.dates.posted + self.__datetaken = photo.dates.taken + self.__takengranularity = photo.dates.takengranularity + + self.__cancomment = photo.editability.cancomment + self.__canaddmeta = photo.editability.canaddmeta + self.__comments = photo.comments.text + + try: + self.__permcomment = photo.permissions.permcomment + self.__permaddmeta = photo.permissions.permaddmeta + except AttributeError: + self.__permcomment = None + self.__permaddmeta = None + + #TODO: Implement Notes? + if hasattr(photo.tags, "tag"): + if isinstance(photo.tags.tag, list): + self.__tags = [Tag(tag.id, User(tag.author), tag.raw, tag.text) \ + for tag in photo.tags.tag] + else: + tag = photo.tags.tag + self.__tags = [Tag(tag.id, User(tag.author), tag.raw, tag.text)] + + + def __str__(self): + return '' % self.id + + + def setTags(self, tags): + """Set the tags for current photo to list tags. + (flickr.photos.settags) + """ + method = 'flickr.photos.setTags' + tags = uniq(tags) + _dopost(method, auth=True, photo_id=self.id, tags=tags) + self._load_properties() + + + def addTags(self, tags): + """Adds the list of tags to current tags. (flickr.photos.addtags) + """ + method = 'flickr.photos.addTags' + if isinstance(tags, list): + tags = uniq(tags) + + _dopost(method, auth=True, photo_id=self.id, tags=tags) + #load properties again + self._load_properties() + + def removeTag(self, tag): + """Remove the tag from the photo must be a Tag object. + (flickr.photos.removeTag) + """ + method = 'flickr.photos.removeTag' + tag_id = '' + try: + tag_id = tag.id + except AttributeError: + raise FlickrError, "Tag object expected" + _dopost(method, auth=True, photo_id=self.id, tag_id=tag_id) + self._load_properties() + + + def setMeta(self, title=None, description=None): + """Set metadata for photo. (flickr.photos.setMeta)""" + method = 'flickr.photos.setMeta' + + if title is None: + title = self.title + if description is None: + description = self.description + + _dopost(method, auth=True, title=title, \ + description=description, photo_id=self.id) + + self.__title = title + self.__description = description + + + def getURL(self, size='Medium', urlType='url'): + """Retrieves a url for the photo. (flickr.photos.getSizes) + + urlType - 'url' or 'source' + 'url' - flickr page of photo + 'source' - image file + """ + method = 'flickr.photos.getSizes' + data = _doget(method, photo_id=self.id) + for psize in data.rsp.sizes.size: + if psize.label == size: + return getattr(psize, urlType) + raise FlickrError, "No URL found" + + def getSizes(self): + """ + Get all the available sizes of the current image, and all available + data about them. + Returns: A list of dicts with the size data. + """ + method = 'flickr.photos.getSizes' + data = _doget(method, photo_id=self.id) + ret = [] + # The given props are those that we return and the according types, since + # return width and height as string would make "75">"100" be True, which + # is just error prone. + props = {'url':str,'width':int,'height':int,'label':str,'source':str,'text':str} + for psize in data.rsp.sizes.size: + d = {} + for prop,convert_to_type in props.items(): + d[prop] = convert_to_type(getattr(psize, prop)) + ret.append(d) + return ret + + #def getExif(self): + #method = 'flickr.photos.getExif' + #data = _doget(method, photo_id=self.id) + #ret = [] + #for exif in data.rsp.photo.exif: + #print exif.label, dir(exif) + ##ret.append({exif.label:exif.}) + #return ret + ##raise FlickrError, "No URL found" + + def getLocation(self): + """ + Return the latitude+longitutde of the picture. + Returns None if no location given for this pic. + """ + method = 'flickr.photos.geo.getLocation' + try: + data = _doget(method, photo_id=self.id) + except FlickrError: # Some other error might have occured too!? + return None + loc = data.rsp.photo.location + return [loc.latitude, loc.longitude] + + + def getComments(self) : + """" + get list of comments for photo + returns a list of comment objects + comment text is in return [item].text + """ + method = "flickr.photos.comments.getList" + try: + data = _doget(method, photo_id=self.id) + except FlickrError: # ???? what errors might there be???? + return None + return data.rsp.comments + + + +class Photoset(object): + """A Flickr photoset.""" + + def __init__(self, id, title, primary, photos=0, description='', \ + secret='', server=''): + self.__id = id + self.__title = title + self.__primary = primary + self.__description = description + self.__count = photos + self.__secret = secret + self.__server = server + + id = property(lambda self: self.__id) + title = property(lambda self: self.__title) + description = property(lambda self: self.__description) + primary = property(lambda self: self.__primary) + + def __len__(self): + return self.__count + + def __str__(self): + return '' % self.id + + def getPhotos(self): + """Returns list of Photos.""" + method = 'flickr.photosets.getPhotos' + data = _doget(method, photoset_id=self.id) + photos = data.rsp.photoset.photo + p = [] + for photo in photos: + p.append(Photo(photo.id, title=photo.title, secret=photo.secret, \ + server=photo.server)) + return p + + def editPhotos(self, photos, primary=None): + """Edit the photos in this set. + + photos - photos for set + primary - primary photo (if None will used current) + """ + method = 'flickr.photosets.editPhotos' + + if primary is None: + primary = self.primary + + ids = [photo.id for photo in photos] + if primary.id not in ids: + ids.append(primary.id) + + _dopost(method, auth=True, photoset_id=self.id,\ + primary_photo_id=primary.id, + photo_ids=ids) + self.__count = len(ids) + return True + + def addPhoto(self, photo): + """Add a photo to this set. + + photo - the photo + """ + method = 'flickr.photosets.addPhoto' + + _dopost(method, auth=True, photoset_id=self.id, photo_id=photo.id) + + self.__count += 1 + return True + + def removePhoto(self, photo): + """Remove the photo from this set. + + photo - the photo + """ + method = 'flickr.photosets.removePhoto' + + _dopost(method, auth=True, photoset_id=self.id, photo_id=photo.id) + self.__count = self.__count - 1 + return True + + def editMeta(self, title=None, description=None): + """Set metadata for photo. (flickr.photos.setMeta)""" + method = 'flickr.photosets.editMeta' + + if title is None: + title = self.title + if description is None: + description = self.description + + _dopost(method, auth=True, title=title, \ + description=description, photoset_id=self.id) + + self.__title = title + self.__description = description + return True + + #XXX: Delete isn't handled well as the python object will still exist + def delete(self): + """Deletes the photoset. + """ + method = 'flickr.photosets.delete' + + _dopost(method, auth=True, photoset_id=self.id) + return True + + def create(cls, photo, title, description=''): + """Create a new photoset. + + photo - primary photo + """ + if not isinstance(photo, Photo): + raise TypeError, "Photo expected" + + method = 'flickr.photosets.create' + data = _dopost(method, auth=True, title=title,\ + description=description,\ + primary_photo_id=photo.id) + + set = Photoset(data.rsp.photoset.id, title, Photo(photo.id), + photos=1, description=description) + return set + create = classmethod(create) + + +class User(object): + """A Flickr user.""" + + def __init__(self, id, username=None, isadmin=None, ispro=None, \ + realname=None, location=None, firstdate=None, count=None): + """id required, rest optional.""" + self.__loaded = False #so we don't keep loading data + self.__id = id + self.__username = username + self.__isadmin = isadmin + self.__ispro = ispro + self.__realname = realname + self.__location = location + self.__photos_firstdate = firstdate + self.__photos_count = count + + #property fu + id = property(lambda self: self._general_getattr('id')) + username = property(lambda self: self._general_getattr('username')) + isadmin = property(lambda self: self._general_getattr('isadmin')) + ispro = property(lambda self: self._general_getattr('ispro')) + realname = property(lambda self: self._general_getattr('realname')) + location = property(lambda self: self._general_getattr('location')) + photos_firstdate = property(lambda self: \ + self._general_getattr('photos_firstdate')) + photos_firstdatetaken = property(lambda self: \ + self._general_getattr\ + ('photos_firstdatetaken')) + photos_count = property(lambda self: \ + self._general_getattr('photos_count')) + icon_server= property(lambda self: self._general_getattr('icon_server')) + icon_url= property(lambda self: self._general_getattr('icon_url')) + + def _general_getattr(self, var): + """Generic get attribute function.""" + if getattr(self, "_%s__%s" % (self.__class__.__name__, var)) is None \ + and not self.__loaded: + self._load_properties() + return getattr(self, "_%s__%s" % (self.__class__.__name__, var)) + + def _load_properties(self): + """Load User properties from Flickr.""" + method = 'flickr.people.getInfo' + data = _doget(method, user_id=self.__id) + + self.__loaded = True + + person = data.rsp.person + + self.__isadmin = person.isadmin + self.__ispro = person.ispro + self.__icon_server = person.iconserver + if int(person.iconserver) > 0: + self.__icon_url = 'http://photos%s.flickr.com/buddyicons/%s.jpg' \ + % (person.iconserver, self.__id) + else: + self.__icon_url = 'http://www.flickr.com/images/buddyicon.jpg' + + self.__username = person.username.text + self.__realname = getattr((getattr(person, 'realname', u'')), 'text', u'') + self.__location = getattr((getattr(person, 'location', u'')), 'text', u'') + self.__photos_count = getattr((getattr(getattr(person, 'photos', None), 'count', u'')), 'text', u'') + if self.__photos_count: + self.__photos_firstdate = person.photos.firstdate.text + self.__photos_firstdatetaken = person.photos.firstdatetaken.text + else: + self.__photos_firstdate = None + self.__photos_firstdatetaken = None + + def __str__(self): + return '' % self.id + + def getPhotosets(self): + """Returns a list of Photosets.""" + method = 'flickr.photosets.getList' + data = _doget(method, user_id=self.id) + + sets = [] + if not getattr(data.rsp.photosets, 'photoset',None): + return sets #N.B. returns an empty set + if isinstance(data.rsp.photosets.photoset, list): + for photoset in data.rsp.photosets.photoset: + sets.append(Photoset(photoset.id, photoset.title.text,\ + Photo(photoset.primary),\ + secret=photoset.secret, \ + server=photoset.server, \ + description=photoset.description.text, + photos=photoset.photos)) + else: + photoset = data.rsp.photosets.photoset + sets.append(Photoset(photoset.id, photoset.title.text,\ + Photo(photoset.primary),\ + secret=photoset.secret, \ + server=photoset.server, \ + description=photoset.description.text, + photos=photoset.photos)) + return sets + + def getPublicFavorites(self, per_page='', page=''): + return favorites_getPublicList(user_id=self.id, per_page=per_page, \ + page=page) + + def getFavorites(self, per_page='', page=''): + return favorites_getList(user_id=self.id, per_page=per_page, \ + page=page) + +class Group(object): + """Flickr Group Pool""" + def __init__(self, id, name=None, members=None, online=None,\ + privacy=None, chatid=None, chatcount=None): + self.__loaded = False + self.__id = id + self.__name = name + + self.__members = members + self.__online = online + self.__privacy = privacy + self.__chatid = chatid + self.__chatcount = chatcount + self.__url = None + + id = property(lambda self: self._general_getattr('id')) + name = property(lambda self: self._general_getattr('name')) + members = property(lambda self: self._general_getattr('members')) + online = property(lambda self: self._general_getattr('online')) + privacy = property(lambda self: self._general_getattr('privacy')) + chatid = property(lambda self: self._general_getattr('chatid')) + chatcount = property(lambda self: self._general_getattr('chatcount')) + + def _general_getattr(self, var): + """Generic get attribute function.""" + if getattr(self, "_%s__%s" % (self.__class__.__name__, var)) is None \ + and not self.__loaded: + self._load_properties() + return getattr(self, "_%s__%s" % (self.__class__.__name__, var)) + + def _load_properties(self): + """Loads the properties from Flickr.""" + method = 'flickr.groups.getInfo' + data = _doget(method, group_id=self.id) + + self.__loaded = True + + group = data.rsp.group + + self.__name = photo.name.text + self.__members = photo.members.text + self.__online = photo.online.text + self.__privacy = photo.privacy.text + self.__chatid = photo.chatid.text + self.__chatcount = photo.chatcount.text + + def __str__(self): + return '' % self.id + + def getPhotos(self, tags='', per_page='', page=''): + """Get a list of photo objects for this group""" + method = 'flickr.groups.pools.getPhotos' + data = _doget(method, group_id=self.id, tags=tags,\ + per_page=per_page, page=page) + photos = [] + for photo in data.rsp.photos.photo: + photos.append(_parse_photo(photo)) + return photos + + def add(self, photo): + """Adds a Photo to the group""" + method = 'flickr.groups.pools.add' + _dopost(method, auth=True, photo_id=photo.id, group_id=self.id) + return True + + def remove(self, photo): + """Remove a Photo from the group""" + method = 'flickr.groups.pools.remove' + _dopost(method, auth=True, photo_id=photo.id, group_id=self.id) + return True + +class Tag(object): + def __init__(self, id, author, raw, text): + self.id = id + self.author = author + self.raw = raw + self.text = text + + def __str__(self): + return '' % (self.id, self.text) + + +#Flickr API methods +#see api docs http://www.flickr.com/services/api/ +#for details of each param + +#XXX: Could be Photo.search(cls) +def photos_search(user_id='', auth=False, tags='', tag_mode='', text='',\ + min_upload_date='', max_upload_date='',\ + min_taken_date='', max_taken_date='', \ + license='', per_page='', page='', sort='',\ + safe_search='', content_type='' ): + """Returns a list of Photo objects. + + If auth=True then will auth the user. Can see private etc + """ + method = 'flickr.photos.search' + + data = _doget(method, auth=auth, user_id=user_id, tags=tags, text=text,\ + min_upload_date=min_upload_date,\ + max_upload_date=max_upload_date, \ + min_taken_date=min_taken_date, \ + max_taken_date=max_taken_date, \ + license=license, per_page=per_page,\ + page=page, sort=sort, safe_search=safe_search, \ + content_type=content_type, \ + tag_mode=tag_mode) + photos = [] + if data.rsp.photos.__dict__.has_key('photo'): + if isinstance(data.rsp.photos.photo, list): + for photo in data.rsp.photos.photo: + photos.append(_parse_photo(photo)) + else: + photos = [_parse_photo(data.rsp.photos.photo)] + return photos + +def photos_search_pages(user_id='', auth=False, tags='', tag_mode='', text='',\ + min_upload_date='', max_upload_date='',\ + min_taken_date='', max_taken_date='', \ + license='', per_page='', page='', sort=''): + """Returns the number of pages for the previous function (photos_search()) + """ + + method = 'flickr.photos.search' + + data = _doget(method, auth=auth, user_id=user_id, tags=tags, text=text,\ + min_upload_date=min_upload_date,\ + max_upload_date=max_upload_date, \ + min_taken_date=min_taken_date, \ + max_taken_date=max_taken_date, \ + license=license, per_page=per_page,\ + page=page, sort=sort) + + return data.rsp.photos.pages + + + +#XXX: Could be class method in User +def people_findByEmail(email): + """Returns User object.""" + method = 'flickr.people.findByEmail' + data = _doget(method, find_email=email) + user = User(data.rsp.user.id, username=data.rsp.user.username.text) + return user + +def people_findByUsername(username): + """Returns User object.""" + method = 'flickr.people.findByUsername' + data = _doget(method, username=username) + user = User(data.rsp.user.id, username=data.rsp.user.username.text) + return user + +#XXX: Should probably be in User as a list User.public +def people_getPublicPhotos(user_id, per_page='', page=''): + """Returns list of Photo objects.""" + method = 'flickr.people.getPublicPhotos' + data = _doget(method, user_id=user_id, per_page=per_page, page=page) + photos = [] + if hasattr(data.rsp.photos, "photo"): # Check if there are photos at all (may be been paging too far). + if isinstance(data.rsp.photos.photo, list): + for photo in data.rsp.photos.photo: + photos.append(_parse_photo(photo)) + else: + photos = [_parse_photo(data.rsp.photos.photo)] + return photos + +#XXX: These are also called from User +def favorites_getList(user_id='', per_page='', page=''): + """Returns list of Photo objects.""" + method = 'flickr.favorites.getList' + data = _doget(method, auth=True, user_id=user_id, per_page=per_page,\ + page=page) + photos = [] + if isinstance(data.rsp.photos.photo, list): + for photo in data.rsp.photos.photo: + photos.append(_parse_photo(photo)) + else: + photos = [_parse_photo(data.rsp.photos.photo)] + return photos + +def favorites_getPublicList(user_id, per_page='', page=''): + """Returns list of Photo objects.""" + method = 'flickr.favorites.getPublicList' + data = _doget(method, auth=False, user_id=user_id, per_page=per_page,\ + page=page) + photos = [] + if isinstance(data.rsp.photos.photo, list): + for photo in data.rsp.photos.photo: + photos.append(_parse_photo(photo)) + else: + photos = [_parse_photo(data.rsp.photos.photo)] + return photos + +def favorites_add(photo_id): + """Add a photo to the user's favorites.""" + method = 'flickr.favorites.add' + _dopost(method, auth=True, photo_id=photo_id) + return True + +def favorites_remove(photo_id): + """Remove a photo from the user's favorites.""" + method = 'flickr.favorites.remove' + _dopost(method, auth=True, photo_id=photo_id) + return True + +def groups_getPublicGroups(): + """Get a list of groups the auth'd user is a member of.""" + method = 'flickr.groups.getPublicGroups' + data = _doget(method, auth=True) + groups = [] + if isinstance(data.rsp.groups.group, list): + for group in data.rsp.groups.group: + groups.append(Group(group.id, name=group.name)) + else: + group = data.rsp.groups.group + groups = [Group(group.id, name=group.name)] + return groups + +def groups_pools_getGroups(): + """Get a list of groups the auth'd user can post photos to.""" + method = 'flickr.groups.pools.getGroups' + data = _doget(method, auth=True) + groups = [] + if isinstance(data.rsp.groups.group, list): + for group in data.rsp.groups.group: + groups.append(Group(group.id, name=group.name, \ + privacy=group.privacy)) + else: + group = data.rsp.groups.group + groups = [Group(group.id, name=group.name, privacy=group.privacy)] + return groups + + +def tags_getListUser(user_id=''): + """Returns a list of tags for the given user (in string format)""" + method = 'flickr.tags.getListUser' + auth = user_id == '' + data = _doget(method, auth=auth, user_id=user_id) + if isinstance(data.rsp.tags.tag, list): + return [tag.text for tag in data.rsp.tags.tag] + else: + return [data.rsp.tags.tag.text] + +def tags_getListUserPopular(user_id='', count=''): + """Gets the popular tags for a user in dictionary form tag=>count""" + method = 'flickr.tags.getListUserPopular' + auth = user_id == '' + data = _doget(method, auth=auth, user_id=user_id) + result = {} + if isinstance(data.rsp.tags.tag, list): + for tag in data.rsp.tags.tag: + result[tag.text] = tag.count + else: + result[data.rsp.tags.tag.text] = data.rsp.tags.tag.count + return result + +def tags_getrelated(tag): + """Gets the related tags for given tag.""" + method = 'flickr.tags.getRelated' + data = _doget(method, auth=False, tag=tag) + if isinstance(data.rsp.tags.tag, list): + return [tag.text for tag in data.rsp.tags.tag] + else: + return [data.rsp.tags.tag.text] + +def contacts_getPublicList(user_id): + """Gets the contacts (Users) for the user_id""" + method = 'flickr.contacts.getPublicList' + data = _doget(method, auth=False, user_id=user_id) + + try: + if isinstance(data.rsp.contacts.contact, list): + return [User(user.nsid, username=user.username) \ + for user in data.rsp.contacts.contact] + + except AttributeError: + return "No users in the list" + except: + return "Unknown error" + +# else: +# user = data.rsp.contacts.contact +# return [User(user.nsid, username=user.username)] + +def interestingness(): + method = 'flickr.interestingness.getList' + data = _doget(method) + photos = [] + if isinstance(data.rsp.photos.photo , list): + for photo in data.rsp.photos.photo: + photos.append(_parse_photo(photo)) + else: + photos = [_parse_photo(data.rsp.photos.photo)] + return photos + +def test_login(): + method = 'flickr.test.login' + data = _doget(method, auth=True) + user = User(data.rsp.user.id, username=data.rsp.user.username.text) + return user + +def test_echo(): + method = 'flickr.test.echo' + data = _doget(method) + return data.rsp.stat + + +#useful methods + +def _doget(method, auth=False, **params): + #uncomment to check you aren't killing the flickr server + #print "***** do get %s" % method + + params = _prepare_params(params) + url = '%s%s/?api_key=%s&method=%s&%s%s'% \ + (HOST, API, API_KEY, method, urlencode(params), + _get_auth_url_suffix(method, auth, params)) + + #another useful debug print statement + #print url + + return _get_data(minidom.parse(urlopen(url))) + +def _dopost(method, auth=False, **params): + #uncomment to check you aren't killing the flickr server + #print "***** do post %s" % method + + params = _prepare_params(params) + url = '%s%s/%s' % (HOST, API, _get_auth_url_suffix(method, auth, params)) + payload = 'api_key=%s&method=%s&%s'% \ + (API_KEY, method, urlencode(params)) + + #another useful debug print statement + #print url + #print payload + + return _get_data(minidom.parse(urlopen(url, payload))) + +def _prepare_params(params): + """Convert lists to strings with ',' between items.""" + for (key, value) in params.items(): + if isinstance(value, list): + params[key] = ','.join([item for item in value]) + return params + +def _get_data(xml): + """Given a bunch of XML back from Flickr, we turn it into a data structure + we can deal with (after checking for errors).""" + data = unmarshal(xml) + if not data.rsp.stat == 'ok': + msg = "ERROR [%s]: %s" % (data.rsp.err.code, data.rsp.err.msg) + raise FlickrError, msg + return data + +def _get_auth_url_suffix(method, auth, params): + """Figure out whether we want to authorize, and if so, construct a suitable + URL suffix to pass to the Flickr API.""" + authentication = False + + # auth may be passed in via the API, AUTH may be set globally (in the same + # manner as API_KEY, etc). We do a few more checks than may seem necessary + # because we allow the 'auth' parameter to actually contain the + # authentication token, not just True/False. + if auth or AUTH: + token = userToken() + authentication = True; + elif auth != False: + token = auth; + authentication = True; + elif AUTH != False: + token = AUTH; + authentication = True; + + # If we're not authenticating, no suffix is required. + if not authentication: + return '' + + paramaters = ['API_KEY', 'method', 'auth_token'] + for item in params.items(): + paramaters.append(item[0]) + paramaters.sort() + + api_string = [API_SECRET] + for item in paramaters: + for chocolate in params.items(): + if item == chocolate[0]: + api_string.append(item) + api_string.append(str(chocolate[1])) + if item == 'method': + api_string.append('method') + api_string.append(method) + if item == 'API_KEY': + api_string.append('api_key') + api_string.append(API_KEY) + if item == 'auth_token': + api_string.append('auth_token') + api_string.append(token) + + api_signature = hashlib.md5(''.join(api_string)).hexdigest() + + return '&auth_token=%s&api_sig=%s' % (token, api_signature) + +def _parse_photo(photo): + """Create a Photo object from photo data.""" + owner = User(photo.owner) + title = photo.title + ispublic = photo.ispublic + isfriend = photo.isfriend + isfamily = photo.isfamily + secret = photo.secret + server = photo.server + p = Photo(photo.id, owner=owner, title=title, ispublic=ispublic,\ + isfriend=isfriend, isfamily=isfamily, secret=secret, \ + server=server) + return p + +#stolen methods + +class Bag: pass + +#unmarshal taken and modified from pyamazon.py +#makes the xml easy to work with +def unmarshal(element): + rc = Bag() + if isinstance(element, minidom.Element): + for key in element.attributes.keys(): + setattr(rc, key, element.attributes[key].value) + + childElements = [e for e in element.childNodes \ + if isinstance(e, minidom.Element)] + if childElements: + for child in childElements: + key = child.tagName + if hasattr(rc, key): + if type(getattr(rc, key)) <> type([]): + setattr(rc, key, [getattr(rc, key)]) + setattr(rc, key, getattr(rc, key) + [unmarshal(child)]) + elif isinstance(child, minidom.Element) and \ + (child.tagName == 'Details'): + # make the first Details element a key + setattr(rc,key,[unmarshal(child)]) + #dbg: because otherwise 'hasattr' only tests + #dbg: on the second occurence: if there's a + #dbg: single return to a query, it's not a + #dbg: list. This module should always + #dbg: return a list of Details objects. + else: + setattr(rc, key, unmarshal(child)) + else: + #jec: we'll have the main part of the element stored in .text + #jec: will break if tag is also present + text = "".join([e.data for e in element.childNodes \ + if isinstance(e, minidom.Text)]) + setattr(rc, 'text', text) + return rc + +#unique items from a list from the cookbook +def uniq(alist): # Fastest without order preserving + set = {} + map(set.__setitem__, alist, []) + return set.keys() + +## Only the "getList" module is complete. +## Work in Progress; Nearly Finished +class Blogs(): + def getList(self,auth=True): + """blogs.getList requires READ authentication""" + # please read documentation on how to use this + + method = 'flickr.blogs.getList' + if auth==True : data = _doget(method, auth=True) + if not auth==True : data = _doget(method, auth=False) + + bID = [] + bName = [] + bNeedsPword = [] + bURL = [] + + try: + for plog in data.rsp.blogs.blog: + bID.append(plog.id) + bName.append(plog.name) + bNeedsPword.append(plog.needspassword) + bURL.append(plog.url) + except TypeError: + try: + bID.append(data.rsp.blogs.blog.id) + bName.append(data.rsp.blogs.blog.name) + bNeedsPword.append(data.rsp.blogs.blog.needspassword) + bURL.append(data.rsp.blogs.blog.url) + except AttributeError: + return "AttributeError, unexplained!" + except: + return "Unknown error!" + except AttributeError: + return "There are no blogs!" + + myReturn = [bID,bName,bNeedsPword,bURL] + return myReturn + + def postPhoto(self, blogID, photoID, title, description, bpassword): + """blogs.postPhoto requires WRITE authentication""" + method = 'flickr.blogs.postPhoto' + return None + +class Urls(): + def getUserPhotosURL(userid): + """Returns user URL in an array (to access, use array[1])""" + method = 'flickr.urls.getUserPhotos' + data = _doget(method, user_id=userid) + return [data.rsp.user.nsid,data.rsp.user.url] + +class Auth(): + def getFrob(self): + """Returns a frob that is used in authentication""" + method = 'flickr.auth.getFrob' + sig_str = API_SECRET + 'api_key' + API_KEY + 'method' + method + signature_hash = hashlib.md5(sig_str).hexdigest() + data = _doget(method, auth=False, api_sig=signature_hash) + return data.rsp.frob.text + + def loginLink(self, permission, frob): + """Generates a link that the user should be sent to""" + myAuth = Auth() + sig_str = API_SECRET + 'api_key' + API_KEY + 'frob' + frob + 'perms' + permission + signature_hash = hashlib.md5(sig_str).hexdigest() + perms = permission + link = "http://flickr.com/services/auth/?api_key=%s&perms=%s&frob=%s&api_sig=%s" % (API_KEY, perms, frob, signature_hash) + return link + + def getToken(self, frob): + """This token is what needs to be used in future API calls""" + method = 'flickr.auth.getToken' + sig_str = API_SECRET + 'api_key' + API_KEY + 'frob' + frob + 'method' + method + signature_hash = hashlib.md5(sig_str).hexdigest() + data = _doget(method, auth=False, api_sig=signature_hash, + api_key=API_KEY, frob=frob) + return data.rsp.auth.token.text + +def userToken(): + # This method allows you flickr.py to retrive the saved token + # as once the token for a program has been got from flickr, + # it cannot be got again, so flickr.py saves it in a file + # called token.txt (default) somewhere. + if not tokenPath == '': + f = file(os.path.join(tokenPath,tokenFile),'r') + else: + f = file(tokenFile,'r') + token = f.read() + f.close() + return token + +def getUserPhotosURL(userid): + """Returns user URL in an array (to access, use array[1])""" + # This addition has been added upon request of + # nsteinmetz. It will be "cleaned up" at another + # time. + method = 'flickr.urls.getUserPhotos' + data = _doget(method, user_id=userid) + userurl = [data.rsp.user.nsid,data.rsp.user.url] + return userurl + +if __name__ == '__main__': + print test_echo()