2 # Copyright 2009-2010 Joshua Roesslein
3 # See LICENSE for details.
5 from tweepy.error import TweepError
6 from tweepy.utils import parse_datetime, parse_html_value, parse_a_href
10 """A list like object that holds results from a Twitter API query."""
11 def __init__(self, max_id=None, since_id=None):
12 super(ResultSet, self).__init__()
14 self._since_id = since_id
21 return max(ids) if ids else None
28 return min(ids) if ids else None
31 return [item.id for item in self if hasattr(item, 'id')]
35 def __init__(self, api=None):
38 def __getstate__(self):
40 pickle = dict(self.__dict__)
42 del pickle['_api'] # do not pickle the API reference
48 def parse(cls, api, json):
49 """Parse a JSON object into a model instance."""
50 raise NotImplementedError
53 def parse_list(cls, api, json_list):
54 """Parse a list of JSON objects into a result set of model instances."""
58 results.append(cls.parse(api, obj))
62 state = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
63 return '%s(%s)' % (self.__class__.__name__, ', '.join(state))
69 def parse(cls, api, json):
71 for k, v in json.items():
73 # Hack by guyrt to fix unicode issues, which we do *not* want to teach.
74 if isinstance(v, basestring):
75 v = v.encode('ascii', errors='ignore')
78 user_model = getattr(api.parser.model_factory, 'user') if api else User
79 user = user_model.parse(api, v)
80 setattr(status, 'author', user)
81 setattr(status, 'user', user) # DEPRECIATED
82 elif k == 'created_at':
83 setattr(status, k, parse_datetime(v))
86 setattr(status, k, parse_html_value(v))
87 setattr(status, 'source_url', parse_a_href(v))
90 setattr(status, 'source_url', None)
91 elif k == 'retweeted_status':
92 setattr(status, k, Status.parse(api, v))
95 setattr(status, k, Place.parse(api, v))
97 setattr(status, k, None)
103 return self._api.destroy_status(self.id)
106 return self._api.retweet(self.id)
109 return self._api.retweets(self.id)
112 return self._api.create_favorite(self.id)
118 def parse(cls, api, json):
120 for k, v in json.items():
121 if k == 'created_at':
122 setattr(user, k, parse_datetime(v))
124 setattr(user, k, Status.parse(api, v))
125 elif k == 'following':
126 # twitter sets this to null if it is false
128 setattr(user, k, True)
130 setattr(user, k, False)
136 def parse_list(cls, api, json_list):
137 if isinstance(json_list, list):
138 item_list = json_list
140 item_list = json_list['users']
142 results = ResultSet()
143 for obj in item_list:
144 results.append(cls.parse(api, obj))
147 def timeline(self, **kargs):
148 return self._api.user_timeline(user_id=self.id, **kargs)
150 def friends(self, **kargs):
151 return self._api.friends(user_id=self.id, **kargs)
153 def followers(self, **kargs):
154 return self._api.followers(user_id=self.id, **kargs)
157 self._api.create_friendship(user_id=self.id)
158 self.following = True
161 self._api.destroy_friendship(user_id=self.id)
162 self.following = False
164 def lists_memberships(self, *args, **kargs):
165 return self._api.lists_memberships(user=self.screen_name, *args, **kargs)
167 def lists_subscriptions(self, *args, **kargs):
168 return self._api.lists_subscriptions(user=self.screen_name, *args, **kargs)
170 def lists(self, *args, **kargs):
171 return self._api.lists_all(user=self.screen_name, *args, **kargs)
173 def followers_ids(self, *args, **kargs):
174 return self._api.followers_ids(user_id=self.id, *args, **kargs)
177 class DirectMessage(Model):
180 def parse(cls, api, json):
182 for k, v in json.items():
183 if k == 'sender' or k == 'recipient':
184 setattr(dm, k, User.parse(api, v))
185 elif k == 'created_at':
186 setattr(dm, k, parse_datetime(v))
192 return self._api.destroy_direct_message(self.id)
195 class Friendship(Model):
198 def parse(cls, api, json):
199 relationship = json['relationship']
203 for k, v in relationship['source'].items():
204 setattr(source, k, v)
208 for k, v in relationship['target'].items():
209 setattr(target, k, v)
211 return source, target
214 class Category(Model):
217 def parse(cls, api, json):
219 for k, v in json.items():
220 setattr(category, k, v)
224 class SavedSearch(Model):
227 def parse(cls, api, json):
229 for k, v in json.items():
230 if k == 'created_at':
231 setattr(ss, k, parse_datetime(v))
237 return self._api.destroy_saved_search(self.id)
240 class SearchResults(ResultSet):
243 def parse(cls, api, json):
244 metadata = json['search_metadata']
245 results = SearchResults(metadata.get('max_id'), metadata.get('since_id'))
246 results.refresh_url = metadata.get('refresh_url')
247 results.completed_in = metadata.get('completed_in')
248 results.query = metadata.get('query')
249 results.count = metadata.get('count')
250 results.next_results = metadata.get('next_results')
252 for status in json['statuses']:
253 results.append(Status.parse(api, status))
260 def parse(cls, api, json):
262 for k,v in json.items():
264 setattr(lst, k, User.parse(api, v))
265 elif k == 'created_at':
266 setattr(lst, k, parse_datetime(v))
272 def parse_list(cls, api, json_list, result_set=None):
273 results = ResultSet()
274 if isinstance(json_list, dict):
275 json_list = json_list['lists']
276 for obj in json_list:
277 results.append(cls.parse(api, obj))
280 def update(self, **kargs):
281 return self._api.update_list(self.slug, **kargs)
284 return self._api.destroy_list(self.slug)
286 def timeline(self, **kargs):
287 return self._api.list_timeline(self.user.screen_name, self.slug, **kargs)
289 def add_member(self, id):
290 return self._api.add_list_member(self.slug, id)
292 def remove_member(self, id):
293 return self._api.remove_list_member(self.slug, id)
295 def members(self, **kargs):
296 return self._api.list_members(self.user.screen_name, self.slug, **kargs)
298 def is_member(self, id):
299 return self._api.is_list_member(self.user.screen_name, self.slug, id)
302 return self._api.subscribe_list(self.user.screen_name, self.slug)
304 def unsubscribe(self):
305 return self._api.unsubscribe_list(self.user.screen_name, self.slug)
307 def subscribers(self, **kargs):
308 return self._api.list_subscribers(self.user.screen_name, self.slug, **kargs)
310 def is_subscribed(self, id):
311 return self._api.is_subscribed_list(self.user.screen_name, self.slug, id)
313 class Relation(Model):
315 def parse(cls, api, json):
317 for k,v in json.items():
318 if k == 'value' and json['kind'] in ['Tweet', 'LookedupStatus']:
319 setattr(result, k, Status.parse(api, v))
321 setattr(result, k, Relation.parse_list(api, v))
323 setattr(result, k, v)
326 class Relationship(Model):
328 def parse(cls, api, json):
330 for k,v in json.items():
331 if k == 'connections':
332 setattr(result, 'is_following', 'following' in v)
333 setattr(result, 'is_followed_by', 'followed_by' in v)
335 setattr(result, k, v)
338 class JSONModel(Model):
341 def parse(cls, api, json):
345 class IDModel(Model):
348 def parse(cls, api, json):
349 if isinstance(json, list):
355 class BoundingBox(Model):
358 def parse(cls, api, json):
361 for k, v in json.items():
362 setattr(result, k, v)
367 Return longitude, latitude of southwest (bottom, left) corner of
368 bounding box, as a tuple.
370 This assumes that bounding box is always a rectangle, which
371 appears to be the case at present.
373 return tuple(self.coordinates[0][0])
377 Return longitude, latitude of northeast (top, right) corner of
378 bounding box, as a tuple.
380 This assumes that bounding box is always a rectangle, which
381 appears to be the case at present.
383 return tuple(self.coordinates[0][2])
389 def parse(cls, api, json):
391 for k, v in json.items():
392 if k == 'bounding_box':
393 # bounding_box value may be null (None.)
394 # Example: "United States" (id=96683cc9126741d1)
396 t = BoundingBox.parse(api, v)
400 elif k == 'contained_within':
401 # contained_within is a list of Places.
402 setattr(place, k, Place.parse_list(api, v))
408 def parse_list(cls, api, json_list):
409 if isinstance(json_list, list):
410 item_list = json_list
412 item_list = json_list['result']['places']
414 results = ResultSet()
415 for obj in item_list:
416 results.append(cls.parse(api, obj))
419 class ModelFactory(object):
421 Used by parsers for creating instances
422 of models. You may subclass this factory
423 to add your own extended models.
428 direct_message = DirectMessage
429 friendship = Friendship
430 saved_search = SavedSearch
431 search_results = SearchResults
435 relationship = Relationship
440 bounding_box = BoundingBox