1338ab456b37227d895f2128af9942e79417edd0
[twitter-api-cdsw] / tweepy / models.py
1 # Tweepy
2 # Copyright 2009-2010 Joshua Roesslein
3 # See LICENSE for details.
4
5 from tweepy.error import TweepError
6 from tweepy.utils import parse_datetime, parse_html_value, parse_a_href
7
8
9 class ResultSet(list):
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__()
13         self._max_id = max_id
14         self._since_id = since_id
15
16     @property
17     def max_id(self):
18         if self._max_id:
19             return self._max_id
20         ids = self.ids()
21         return max(ids) if ids else None
22
23     @property
24     def since_id(self):
25         if self._since_id:
26             return self._since_id
27         ids = self.ids()
28         return min(ids) if ids else None
29
30     def ids(self):
31         return [item.id for item in self if hasattr(item, 'id')]
32
33 class Model(object):
34
35     def __init__(self, api=None):
36         self._api = api
37
38     def __getstate__(self):
39         # pickle
40         pickle = dict(self.__dict__)
41         try:
42             del pickle['_api']  # do not pickle the API reference
43         except KeyError:
44             pass
45         return pickle
46
47     @classmethod
48     def parse(cls, api, json):
49         """Parse a JSON object into a model instance."""
50         raise NotImplementedError
51
52     @classmethod
53     def parse_list(cls, api, json_list):
54         """Parse a list of JSON objects into a result set of model instances."""
55         results = ResultSet()
56         for obj in json_list:
57             if obj:
58                 results.append(cls.parse(api, obj))
59         return results
60
61     def __repr__(self):
62         state = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
63         return '%s(%s)' % (self.__class__.__name__, ', '.join(state))
64
65
66 class Status(Model):
67
68     @classmethod
69     def parse(cls, api, json):
70         status = cls(api)
71         for k, v in json.items():
72             if k == 'user':
73                 user_model = getattr(api.parser.model_factory, 'user') if api else User
74                 user = user_model.parse(api, v)
75                 setattr(status, 'author', user)
76                 setattr(status, 'user', user)  # DEPRECIATED
77             elif k == 'created_at':
78                 setattr(status, k, parse_datetime(v))
79             elif k == 'source':
80                 if '<' in v:
81                     setattr(status, k, parse_html_value(v))
82                     setattr(status, 'source_url', parse_a_href(v))
83                 else:
84                     setattr(status, k, v)
85                     setattr(status, 'source_url', None)
86             elif k == 'retweeted_status':
87                 setattr(status, k, Status.parse(api, v))
88             elif k == 'place':
89                 if v is not None:
90                     setattr(status, k, Place.parse(api, v))
91                 else:
92                     setattr(status, k, None)
93             else:
94                 setattr(status, k, v)
95         return status
96
97     def destroy(self):
98         return self._api.destroy_status(self.id)
99
100     def retweet(self):
101         return self._api.retweet(self.id)
102
103     def retweets(self):
104         return self._api.retweets(self.id)
105
106     def favorite(self):
107         return self._api.create_favorite(self.id)
108
109
110 class User(Model):
111
112     @classmethod
113     def parse(cls, api, json):
114         user = cls(api)
115         for k, v in json.items():
116             if k == 'created_at':
117                 setattr(user, k, parse_datetime(v))
118             elif k == 'status':
119                 setattr(user, k, Status.parse(api, v))
120             elif k == 'following':
121                 # twitter sets this to null if it is false
122                 if v is True:
123                     setattr(user, k, True)
124                 else:
125                     setattr(user, k, False)
126             else:
127                 setattr(user, k, v)
128         return user
129
130     @classmethod
131     def parse_list(cls, api, json_list):
132         if isinstance(json_list, list):
133             item_list = json_list
134         else:
135             item_list = json_list['users']
136
137         results = ResultSet()
138         for obj in item_list:
139             results.append(cls.parse(api, obj))
140         return results
141
142     def timeline(self, **kargs):
143         return self._api.user_timeline(user_id=self.id, **kargs)
144
145     def friends(self, **kargs):
146         return self._api.friends(user_id=self.id, **kargs)
147
148     def followers(self, **kargs):
149         return self._api.followers(user_id=self.id, **kargs)
150
151     def follow(self):
152         self._api.create_friendship(user_id=self.id)
153         self.following = True
154
155     def unfollow(self):
156         self._api.destroy_friendship(user_id=self.id)
157         self.following = False
158
159     def lists_memberships(self, *args, **kargs):
160         return self._api.lists_memberships(user=self.screen_name, *args, **kargs)
161
162     def lists_subscriptions(self, *args, **kargs):
163         return self._api.lists_subscriptions(user=self.screen_name, *args, **kargs)
164
165     def lists(self, *args, **kargs):
166         return self._api.lists_all(user=self.screen_name, *args, **kargs)
167
168     def followers_ids(self, *args, **kargs):
169         return self._api.followers_ids(user_id=self.id, *args, **kargs)
170
171
172 class DirectMessage(Model):
173
174     @classmethod
175     def parse(cls, api, json):
176         dm = cls(api)
177         for k, v in json.items():
178             if k == 'sender' or k == 'recipient':
179                 setattr(dm, k, User.parse(api, v))
180             elif k == 'created_at':
181                 setattr(dm, k, parse_datetime(v))
182             else:
183                 setattr(dm, k, v)
184         return dm
185
186     def destroy(self):
187         return self._api.destroy_direct_message(self.id)
188
189
190 class Friendship(Model):
191
192     @classmethod
193     def parse(cls, api, json):
194         relationship = json['relationship']
195
196         # parse source
197         source = cls(api)
198         for k, v in relationship['source'].items():
199             setattr(source, k, v)
200
201         # parse target
202         target = cls(api)
203         for k, v in relationship['target'].items():
204             setattr(target, k, v)
205
206         return source, target
207
208
209 class Category(Model):
210
211     @classmethod
212     def parse(cls, api, json):
213         category = cls(api)
214         for k, v in json.items():
215             setattr(category, k, v)
216         return category
217
218
219 class SavedSearch(Model):
220
221     @classmethod
222     def parse(cls, api, json):
223         ss = cls(api)
224         for k, v in json.items():
225             if k == 'created_at':
226                 setattr(ss, k, parse_datetime(v))
227             else:
228                 setattr(ss, k, v)
229         return ss
230
231     def destroy(self):
232         return self._api.destroy_saved_search(self.id)
233
234
235 class SearchResults(ResultSet):
236
237     @classmethod
238     def parse(cls, api, json):
239         metadata = json['search_metadata']
240         results = SearchResults(metadata.get('max_id'), metadata.get('since_id'))
241         results.refresh_url = metadata.get('refresh_url')
242         results.completed_in = metadata.get('completed_in')
243         results.query = metadata.get('query')
244         results.count = metadata.get('count')
245         results.next_results = metadata.get('next_results')
246
247         for status in json['statuses']:
248             results.append(Status.parse(api, status))
249         return results
250
251
252 class List(Model):
253
254     @classmethod
255     def parse(cls, api, json):
256         lst = List(api)
257         for k,v in json.items():
258             if k == 'user':
259                 setattr(lst, k, User.parse(api, v))
260             elif k == 'created_at':
261                 setattr(lst, k, parse_datetime(v))
262             else:
263                 setattr(lst, k, v)
264         return lst
265
266     @classmethod
267     def parse_list(cls, api, json_list, result_set=None):
268         results = ResultSet()
269         if isinstance(json_list, dict):
270             json_list = json_list['lists']
271         for obj in json_list:
272             results.append(cls.parse(api, obj))
273         return results
274
275     def update(self, **kargs):
276         return self._api.update_list(self.slug, **kargs)
277
278     def destroy(self):
279         return self._api.destroy_list(self.slug)
280
281     def timeline(self, **kargs):
282         return self._api.list_timeline(self.user.screen_name, self.slug, **kargs)
283
284     def add_member(self, id):
285         return self._api.add_list_member(self.slug, id)
286
287     def remove_member(self, id):
288         return self._api.remove_list_member(self.slug, id)
289
290     def members(self, **kargs):
291         return self._api.list_members(self.user.screen_name, self.slug, **kargs)
292
293     def is_member(self, id):
294         return self._api.is_list_member(self.user.screen_name, self.slug, id)
295
296     def subscribe(self):
297         return self._api.subscribe_list(self.user.screen_name, self.slug)
298
299     def unsubscribe(self):
300         return self._api.unsubscribe_list(self.user.screen_name, self.slug)
301
302     def subscribers(self, **kargs):
303         return self._api.list_subscribers(self.user.screen_name, self.slug, **kargs)
304
305     def is_subscribed(self, id):
306         return self._api.is_subscribed_list(self.user.screen_name, self.slug, id)
307
308 class Relation(Model):
309     @classmethod
310     def parse(cls, api, json):
311         result = cls(api)
312         for k,v in json.items():
313             if k == 'value' and json['kind'] in ['Tweet', 'LookedupStatus']:
314                 setattr(result, k, Status.parse(api, v))
315             elif k == 'results':
316                 setattr(result, k, Relation.parse_list(api, v))
317             else:
318                 setattr(result, k, v)
319         return result
320
321 class Relationship(Model):
322     @classmethod
323     def parse(cls, api, json):
324         result = cls(api)
325         for k,v in json.items():
326             if k == 'connections':
327                 setattr(result, 'is_following', 'following' in v)
328                 setattr(result, 'is_followed_by', 'followed_by' in v)
329             else:
330                 setattr(result, k, v)
331         return result
332
333 class JSONModel(Model):
334
335     @classmethod
336     def parse(cls, api, json):
337         return json
338
339
340 class IDModel(Model):
341
342     @classmethod
343     def parse(cls, api, json):
344         if isinstance(json, list):
345             return json
346         else:
347             return json['ids']
348
349
350 class BoundingBox(Model):
351
352     @classmethod
353     def parse(cls, api, json):
354         result = cls(api)
355         if json is not None:
356             for k, v in json.items():
357                 setattr(result, k, v)
358         return result
359
360     def origin(self):
361         """
362         Return longitude, latitude of southwest (bottom, left) corner of
363         bounding box, as a tuple.
364
365         This assumes that bounding box is always a rectangle, which
366         appears to be the case at present.
367         """
368         return tuple(self.coordinates[0][0])
369
370     def corner(self):
371         """
372         Return longitude, latitude of northeast (top, right) corner of
373         bounding box, as a tuple.
374
375         This assumes that bounding box is always a rectangle, which
376         appears to be the case at present.
377         """
378         return tuple(self.coordinates[0][2])
379
380
381 class Place(Model):
382
383     @classmethod
384     def parse(cls, api, json):
385         place = cls(api)
386         for k, v in json.items():
387             if k == 'bounding_box':
388                 # bounding_box value may be null (None.)
389                 # Example: "United States" (id=96683cc9126741d1)
390                 if v is not None:
391                     t = BoundingBox.parse(api, v)
392                 else:
393                     t = v
394                 setattr(place, k, t)
395             elif k == 'contained_within':
396                 # contained_within is a list of Places.
397                 setattr(place, k, Place.parse_list(api, v))
398             else:
399                 setattr(place, k, v)
400         return place
401
402     @classmethod
403     def parse_list(cls, api, json_list):
404         if isinstance(json_list, list):
405             item_list = json_list
406         else:
407             item_list = json_list['result']['places']
408
409         results = ResultSet()
410         for obj in item_list:
411             results.append(cls.parse(api, obj))
412         return results
413
414 class ModelFactory(object):
415     """
416     Used by parsers for creating instances
417     of models. You may subclass this factory
418     to add your own extended models.
419     """
420
421     status = Status
422     user = User
423     direct_message = DirectMessage
424     friendship = Friendship
425     saved_search = SavedSearch
426     search_results = SearchResults
427     category = Category
428     list = List
429     relation = Relation
430     relationship = Relationship
431
432     json = JSONModel
433     ids = IDModel
434     place = Place
435     bounding_box = BoundingBox
436

Benjamin Mako Hill || Want to submit a patch?