2 # Copyright 2009-2010 Joshua Roesslein
3 # See LICENSE for details.
5 from __future__ import absolute_import, print_function
7 from tweepy.utils import parse_datetime, parse_html_value, parse_a_href
10 class ResultSet(list):
11 """A list like object that holds results from a Twitter API query."""
12 def __init__(self, max_id=None, since_id=None):
13 super(ResultSet, self).__init__()
15 self._since_id = since_id
22 # Max_id is always set to the *smallest* id, minus one, in the set
23 return (min(ids) - 1) if ids else None
30 # Since_id is always set to the *greatest* id in the set
31 return max(ids) if ids else None
34 return [item.id for item in self if hasattr(item, 'id')]
39 def __init__(self, api=None):
42 def __getstate__(self):
44 pickle = dict(self.__dict__)
46 del pickle['_api'] # do not pickle the API reference
52 def parse(cls, api, json):
53 """Parse a JSON object into a model instance."""
54 raise NotImplementedError
57 def parse_list(cls, api, json_list):
59 Parse a list of JSON objects into
60 a result set of model instances.
65 results.append(cls.parse(api, obj))
69 state = ['%s=%s' % (k, repr(v)) for (k, v) in vars(self).items()]
70 return '%s(%s)' % (self.__class__.__name__, ', '.join(state))
76 def parse(cls, api, json):
79 # I'm not proud. Blame billg.
81 json['text'] = str(json['text'].encode(sys.stdout.encoding, 'replace'))[2:-1]
83 json['user']['screen_name'] = str(json['user']['screen_name'].encode(sys.stdout.encoding, 'replace'))[2:-1]
85 setattr(status, '_json', json)
86 for k, v in json.items():
88 user_model = getattr(api.parser.model_factory, 'user') if api else User
89 user = user_model.parse(api, v)
90 setattr(status, 'author', user)
91 setattr(status, 'user', user) # DEPRECIATED
92 elif k == 'created_at':
93 setattr(status, k, parse_datetime(v))
96 setattr(status, k, parse_html_value(v))
97 setattr(status, 'source_url', parse_a_href(v))
100 setattr(status, 'source_url', None)
101 elif k == 'retweeted_status':
102 setattr(status, k, Status.parse(api, v))
105 setattr(status, k, Place.parse(api, v))
107 setattr(status, k, None)
109 setattr(status, k, v)
113 return self._api.destroy_status(self.id)
116 return self._api.retweet(self.id)
119 return self._api.retweets(self.id)
122 return self._api.create_favorite(self.id)
124 def __eq__(self, other):
125 if isinstance(other, Status):
126 return self.id == other.id
128 return NotImplemented
130 def __ne__(self, other):
131 result = self == other
133 if result is NotImplemented:
142 def parse(cls, api, json):
144 setattr(user, '_json', json)
145 for k, v in json.items():
146 if k == 'created_at':
147 setattr(user, k, parse_datetime(v))
149 setattr(user, k, Status.parse(api, v))
150 elif k == 'following':
151 # twitter sets this to null if it is false
153 setattr(user, k, True)
155 setattr(user, k, False)
161 def parse_list(cls, api, json_list):
162 if isinstance(json_list, list):
163 item_list = json_list
165 item_list = json_list['users']
167 results = ResultSet()
168 for obj in item_list:
169 results.append(cls.parse(api, obj))
172 def timeline(self, **kargs):
173 return self._api.user_timeline(user_id=self.id, **kargs)
175 def friends(self, **kargs):
176 return self._api.friends(user_id=self.id, **kargs)
178 def followers(self, **kargs):
179 return self._api.followers(user_id=self.id, **kargs)
182 self._api.create_friendship(user_id=self.id)
183 self.following = True
186 self._api.destroy_friendship(user_id=self.id)
187 self.following = False
189 def lists_memberships(self, *args, **kargs):
190 return self._api.lists_memberships(user=self.screen_name,
194 def lists_subscriptions(self, *args, **kargs):
195 return self._api.lists_subscriptions(user=self.screen_name,
199 def lists(self, *args, **kargs):
200 return self._api.lists_all(user=self.screen_name,
204 def followers_ids(self, *args, **kargs):
205 return self._api.followers_ids(user_id=self.id,
210 class DirectMessage(Model):
213 def parse(cls, api, json):
215 for k, v in json.items():
216 if k == 'sender' or k == 'recipient':
217 setattr(dm, k, User.parse(api, v))
218 elif k == 'created_at':
219 setattr(dm, k, parse_datetime(v))
225 return self._api.destroy_direct_message(self.id)
228 class Friendship(Model):
231 def parse(cls, api, json):
232 relationship = json['relationship']
236 for k, v in relationship['source'].items():
237 setattr(source, k, v)
241 for k, v in relationship['target'].items():
242 setattr(target, k, v)
244 return source, target
247 class Category(Model):
250 def parse(cls, api, json):
252 for k, v in json.items():
253 setattr(category, k, v)
257 class SavedSearch(Model):
260 def parse(cls, api, json):
262 for k, v in json.items():
263 if k == 'created_at':
264 setattr(ss, k, parse_datetime(v))
270 return self._api.destroy_saved_search(self.id)
273 class SearchResults(ResultSet):
276 def parse(cls, api, json):
277 metadata = json['search_metadata']
278 results = SearchResults()
279 results.refresh_url = metadata.get('refresh_url')
280 results.completed_in = metadata.get('completed_in')
281 results.query = metadata.get('query')
282 results.count = metadata.get('count')
283 results.next_results = metadata.get('next_results')
285 status_model = getattr(api.parser.model_factory, 'status') if api else Status
287 for status in json['statuses']:
288 results.append(status_model.parse(api, status))
295 def parse(cls, api, json):
297 for k, v in json.items():
299 setattr(lst, k, User.parse(api, v))
300 elif k == 'created_at':
301 setattr(lst, k, parse_datetime(v))
307 def parse_list(cls, api, json_list, result_set=None):
308 results = ResultSet()
309 if isinstance(json_list, dict):
310 json_list = json_list['lists']
311 for obj in json_list:
312 results.append(cls.parse(api, obj))
315 def update(self, **kargs):
316 return self._api.update_list(self.slug, **kargs)
319 return self._api.destroy_list(self.slug)
321 def timeline(self, **kargs):
322 return self._api.list_timeline(self.user.screen_name,
326 def add_member(self, id):
327 return self._api.add_list_member(self.slug, id)
329 def remove_member(self, id):
330 return self._api.remove_list_member(self.slug, id)
332 def members(self, **kargs):
333 return self._api.list_members(self.user.screen_name,
337 def is_member(self, id):
338 return self._api.is_list_member(self.user.screen_name,
343 return self._api.subscribe_list(self.user.screen_name, self.slug)
345 def unsubscribe(self):
346 return self._api.unsubscribe_list(self.user.screen_name, self.slug)
348 def subscribers(self, **kargs):
349 return self._api.list_subscribers(self.user.screen_name,
353 def is_subscribed(self, id):
354 return self._api.is_subscribed_list(self.user.screen_name,
359 class Relation(Model):
361 def parse(cls, api, json):
363 for k, v in json.items():
364 if k == 'value' and json['kind'] in ['Tweet', 'LookedupStatus']:
365 setattr(result, k, Status.parse(api, v))
367 setattr(result, k, Relation.parse_list(api, v))
369 setattr(result, k, v)
373 class Relationship(Model):
375 def parse(cls, api, json):
377 for k, v in json.items():
378 if k == 'connections':
379 setattr(result, 'is_following', 'following' in v)
380 setattr(result, 'is_followed_by', 'followed_by' in v)
382 setattr(result, k, v)
386 class JSONModel(Model):
389 def parse(cls, api, json):
393 class IDModel(Model):
396 def parse(cls, api, json):
397 if isinstance(json, list):
403 class BoundingBox(Model):
406 def parse(cls, api, json):
409 for k, v in json.items():
410 setattr(result, k, v)
415 Return longitude, latitude of southwest (bottom, left) corner of
416 bounding box, as a tuple.
418 This assumes that bounding box is always a rectangle, which
419 appears to be the case at present.
421 return tuple(self.coordinates[0][0])
425 Return longitude, latitude of northeast (top, right) corner of
426 bounding box, as a tuple.
428 This assumes that bounding box is always a rectangle, which
429 appears to be the case at present.
431 return tuple(self.coordinates[0][2])
437 def parse(cls, api, json):
439 for k, v in json.items():
440 if k == 'bounding_box':
441 # bounding_box value may be null (None.)
442 # Example: "United States" (id=96683cc9126741d1)
444 t = BoundingBox.parse(api, v)
448 elif k == 'contained_within':
449 # contained_within is a list of Places.
450 setattr(place, k, Place.parse_list(api, v))
456 def parse_list(cls, api, json_list):
457 if isinstance(json_list, list):
458 item_list = json_list
460 item_list = json_list['result']['places']
462 results = ResultSet()
463 for obj in item_list:
464 results.append(cls.parse(api, obj))
471 def parse(cls, api, json):
473 for k, v in json.items():
478 class ModelFactory(object):
480 Used by parsers for creating instances
481 of models. You may subclass this factory
482 to add your own extended models.
487 direct_message = DirectMessage
488 friendship = Friendship
489 saved_search = SavedSearch
490 search_results = SearchResults
494 relationship = Relationship
500 bounding_box = BoundingBox