models.py 11.9 KB
Newer Older
1
# Tweepy
2
3
# Copyright 2009-2010 Joshua Roesslein
# See LICENSE for details.
4

Josh Roesslein's avatar
Josh Roesslein committed
5
from tweepy.error import TweepError
Joshua Roesslein's avatar
Joshua Roesslein committed
6
from tweepy.utils import parse_datetime, parse_html_value, parse_a_href
7
8
9
10


class ResultSet(list):
    """A list like object that holds results from a Twitter API query."""
11
12
13
14
    def __init__(self, max_id=None, since_id=None):
        super(ResultSet, self).__init__()
        self._max_id = max_id
        self._since_id = since_id
Josh Roesslein's avatar
Josh Roesslein committed
15

16
17
    @property
    def max_id(self):
18
19
20
21
        if self._max_id:
            return self._max_id
        ids = self.ids()
        return max(ids) if ids else None
22
23
24

    @property
    def since_id(self):
25
26
27
28
        if self._since_id:
            return self._since_id
        ids = self.ids()
        return min(ids) if ids else None
29
30
31

    def ids(self):
        return [item.id for item in self if hasattr(item, 'id')]
Josh Roesslein's avatar
Josh Roesslein committed
32

33
34
class Model(object):

35
36
37
    def __init__(self, api=None):
        self._api = api

Josh Roesslein's avatar
Josh Roesslein committed
38
39
    def __getstate__(self):
        # pickle
Joshua's avatar
Joshua committed
40
        pickle = dict(self.__dict__)
41
42
43
44
        try:
            del pickle['_api']  # do not pickle the API reference
        except KeyError:
            pass
Josh Roesslein's avatar
Josh Roesslein committed
45
46
        return pickle

47
48
49
50
51
52
53
54
55
56
    @classmethod
    def parse(cls, api, json):
        """Parse a JSON object into a model instance."""
        raise NotImplementedError

    @classmethod
    def parse_list(cls, api, json_list):
        """Parse a list of JSON objects into a result set of model instances."""
        results = ResultSet()
        for obj in json_list:
57
58
            if obj:
                results.append(cls.parse(api, obj))
59
60
        return results

Josh Roesslein's avatar
Josh Roesslein committed
61

62
class Status(Model):
63

64
65
66
67
68
    @classmethod
    def parse(cls, api, json):
        status = cls(api)
        for k, v in json.items():
            if k == 'user':
69
70
                user_model = getattr(api.parser.model_factory, 'user')
                user = user_model.parse(api, v)
71
72
73
74
75
76
77
78
79
80
                setattr(status, 'author', user)
                setattr(status, 'user', user)  # DEPRECIATED
            elif k == 'created_at':
                setattr(status, k, parse_datetime(v))
            elif k == 'source':
                if '<' in v:
                    setattr(status, k, parse_html_value(v))
                    setattr(status, 'source_url', parse_a_href(v))
                else:
                    setattr(status, k, v)
81
                    setattr(status, 'source_url', None)
82
            elif k == 'retweeted_status':
83
                setattr(status, k, Status.parse(api, v))
84
            elif k == 'place':
85
86
87
88
                if v is not None:
                    setattr(status, k, Place.parse(api, v))
                else:
                    setattr(status, k, None)
89
90
91
92
            else:
                setattr(status, k, v)
        return status

Josh Roesslein's avatar
Josh Roesslein committed
93
    def destroy(self):
94
95
96
97
98
99
100
101
102
103
        return self._api.destroy_status(self.id)

    def retweet(self):
        return self._api.retweet(self.id)

    def retweets(self):
        return self._api.retweets(self.id)

    def favorite(self):
        return self._api.create_favorite(self.id)
Josh Roesslein's avatar
Josh Roesslein committed
104

105

106
class User(Model):
107

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    @classmethod
    def parse(cls, api, json):
        user = cls(api)
        for k, v in json.items():
            if k == 'created_at':
                setattr(user, k, parse_datetime(v))
            elif k == 'status':
                setattr(user, k, Status.parse(api, v))
            elif k == 'following':
                # twitter sets this to null if it is false
                if v is True:
                    setattr(user, k, True)
                else:
                    setattr(user, k, False)
            else:
                setattr(user, k, v)
        return user

    @classmethod
    def parse_list(cls, api, json_list):
        if isinstance(json_list, list):
            item_list = json_list
        else:
            item_list = json_list['users']

        results = ResultSet()
        for obj in item_list:
            results.append(cls.parse(api, obj))
        return results

Josh Roesslein's avatar
Josh Roesslein committed
138
    def timeline(self, **kargs):
Joshua Roesslein's avatar
Joshua Roesslein committed
139
        return self._api.user_timeline(user_id=self.id, **kargs)
Josh Roesslein's avatar
Josh Roesslein committed
140
141

    def friends(self, **kargs):
Joshua Roesslein's avatar
Joshua Roesslein committed
142
        return self._api.friends(user_id=self.id, **kargs)
Josh Roesslein's avatar
Josh Roesslein committed
143
144

    def followers(self, **kargs):
Arthur Debert's avatar
Arthur Debert committed
145
        return self._api.followers(user_id=self.id, **kargs)
Josh Roesslein's avatar
Josh Roesslein committed
146
147
148
149
150
151
152
153
154

    def follow(self):
        self._api.create_friendship(user_id=self.id)
        self.following = True

    def unfollow(self):
        self._api.destroy_friendship(user_id=self.id)
        self.following = False

155
156
    def lists_memberships(self, *args, **kargs):
        return self._api.lists_memberships(user=self.screen_name, *args, **kargs)
157

158
159
    def lists_subscriptions(self, *args, **kargs):
        return self._api.lists_subscriptions(user=self.screen_name, *args, **kargs)
160

161
162
    def lists(self, *args, **kargs):
        return self._api.lists(user=self.screen_name, *args, **kargs)
163

164
165
    def followers_ids(self, *args, **kargs):
        return self._api.followers_ids(user_id=self.id, *args, **kargs)
166

167

168
class DirectMessage(Model):
169

170
171
172
173
174
175
176
177
178
179
180
181
    @classmethod
    def parse(cls, api, json):
        dm = cls(api)
        for k, v in json.items():
            if k == 'sender' or k == 'recipient':
                setattr(dm, k, User.parse(api, v))
            elif k == 'created_at':
                setattr(dm, k, parse_datetime(v))
            else:
                setattr(dm, k, v)
        return dm

Josh Roesslein's avatar
Josh Roesslein committed
182
    def destroy(self):
Josh Roesslein's avatar
Josh Roesslein committed
183
        return self._api.destroy_direct_message(self.id)
Josh Roesslein's avatar
Josh Roesslein committed
184

185

186
class Friendship(Model):
187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
    @classmethod
    def parse(cls, api, json):
        relationship = json['relationship']

        # parse source
        source = cls(api)
        for k, v in relationship['source'].items():
            setattr(source, k, v)

        # parse target
        target = cls(api)
        for k, v in relationship['target'].items():
            setattr(target, k, v)

        return source, target
Josh Roesslein's avatar
Josh Roesslein committed
203

204

205
206
207
208
209
210
211
212
213
214
class Category(Model):

    @classmethod
    def parse(cls, api, json):
        category = cls(api)
        for k, v in json.items():
            setattr(category, k, v)
        return category


215
class SavedSearch(Model):
216

217
218
219
220
221
222
223
224
225
226
    @classmethod
    def parse(cls, api, json):
        ss = cls(api)
        for k, v in json.items():
            if k == 'created_at':
                setattr(ss, k, parse_datetime(v))
            else:
                setattr(ss, k, v)
        return ss

Josh Roesslein's avatar
Josh Roesslein committed
227
228
    def destroy(self):
        return self._api.destroy_saved_search(self.id)
Josh Roesslein's avatar
Josh Roesslein committed
229

Josh Roesslein's avatar
Josh Roesslein committed
230

231
class SearchResults(ResultSet):
Josh Roesslein's avatar
Josh Roesslein committed
232

233
234
    @classmethod
    def parse(cls, api, json):
235
        metadata = json['search_metadata']
236
        results = SearchResults(metadata.get('max_id'), metadata.get('since_id'))
237
238
239
240
241
242
        results.refresh_url = metadata.get('refresh_url')
        results.completed_in = metadata.get('completed_in')
        results.query = metadata.get('query')

        for status in json['statuses']:
            results.append(Status.parse(api, status))
243
244
        return results

245

Josh Roesslein's avatar
Josh Roesslein committed
246
247
class List(Model):

248
249
250
251
252
253
    @classmethod
    def parse(cls, api, json):
        lst = List(api)
        for k,v in json.items():
            if k == 'user':
                setattr(lst, k, User.parse(api, v))
254
255
            elif k == 'created_at':
                setattr(lst, k, parse_datetime(v))
256
257
258
259
260
261
262
            else:
                setattr(lst, k, v)
        return lst

    @classmethod
    def parse_list(cls, api, json_list, result_set=None):
        results = ResultSet()
Joshua Roesslein's avatar
Joshua Roesslein committed
263
264
265
        if isinstance(json_list, dict):
            json_list = json_list['lists']
        for obj in json_list:
266
267
268
            results.append(cls.parse(api, obj))
        return results

269
270
271
    def update(self, **kargs):
        return self._api.update_list(self.slug, **kargs)

Josh Roesslein's avatar
Josh Roesslein committed
272
273
    def destroy(self):
        return self._api.destroy_list(self.slug)
Josh Roesslein's avatar
Josh Roesslein committed
274

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
    def timeline(self, **kargs):
        return self._api.list_timeline(self.user.screen_name, self.slug, **kargs)

    def add_member(self, id):
        return self._api.add_list_member(self.slug, id)

    def remove_member(self, id):
        return self._api.remove_list_member(self.slug, id)

    def members(self, **kargs):
        return self._api.list_members(self.user.screen_name, self.slug, **kargs)

    def is_member(self, id):
        return self._api.is_list_member(self.user.screen_name, self.slug, id)

    def subscribe(self):
        return self._api.subscribe_list(self.user.screen_name, self.slug)

    def unsubscribe(self):
        return self._api.unsubscribe_list(self.user.screen_name, self.slug)

    def subscribers(self, **kargs):
        return self._api.list_subscribers(self.user.screen_name, self.slug, **kargs)

    def is_subscribed(self, id):
        return self._api.is_subscribed_list(self.user.screen_name, self.slug, id)

Aaron Swartz's avatar
Aaron Swartz committed
302
303
304
305
306
307
308
309
310
311
312
313
314
class Relation(Model):
    @classmethod
    def parse(cls, api, json):
        result = cls(api)
        for k,v in json.items():
            if k == 'value' and json['kind'] in ['Tweet', 'LookedupStatus']:
                setattr(result, k, Status.parse(api, v))
            elif k == 'results':
                setattr(result, k, Relation.parse_list(api, v))
            else:
                setattr(result, k, v)
        return result

315
316
317
318
319
320
class Relationship(Model):
    @classmethod
    def parse(cls, api, json):
        result = cls(api)
        for k,v in json.items():
            if k == 'connections':
Santosh Kumar's avatar
Santosh Kumar committed
321
                setattr(result, 'is_following', 'following' in v)
322
323
324
325
                setattr(result, 'is_followed_by', 'followed_by' in v)
            else:
                setattr(result, k, v)
        return result
326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
class JSONModel(Model):

    @classmethod
    def parse(cls, api, json):
        return json


class IDModel(Model):

    @classmethod
    def parse(cls, api, json):
        if isinstance(json, list):
            return json
        else:
            return json['ids']


344
345
346
347
348
class BoundingBox(Model):

    @classmethod
    def parse(cls, api, json):
        result = cls(api)
349
350
351
        if json is not None:
            for k, v in json.items():
                setattr(result, k, v)
352
353
354
355
        return result

    def origin(self):
        """
356
357
358
        Return longitude, latitude of southwest (bottom, left) corner of
        bounding box, as a tuple.

359
        This assumes that bounding box is always a rectangle, which
360
361
362
363
364
365
        appears to be the case at present.
        """
        return tuple(self.coordinates[0][0])

    def corner(self):
        """
366
        Return longitude, latitude of northeast (top, right) corner of
367
368
        bounding box, as a tuple.

369
        This assumes that bounding box is always a rectangle, which
370
371
        appears to be the case at present.
        """
372
        return tuple(self.coordinates[0][2])
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407


class Place(Model):

    @classmethod
    def parse(cls, api, json):
        place = cls(api)
        for k, v in json.items():
            if k == 'bounding_box':
                # bounding_box value may be null (None.)
                # Example: "United States" (id=96683cc9126741d1)
                if v is not None:
                    t = BoundingBox.parse(api, v)
                else:
                    t = v
                setattr(place, k, t)
            elif k == 'contained_within':
                # contained_within is a list of Places.
                setattr(place, k, Place.parse_list(api, v))
            else:
                setattr(place, k, v)
        return place

    @classmethod
    def parse_list(cls, api, json_list):
        if isinstance(json_list, list):
            item_list = json_list
        else:
            item_list = json_list['result']['places']

        results = ResultSet()
        for obj in item_list:
            results.append(cls.parse(api, obj))
        return results

408
409
410
411
412
413
414
415
416
417
418
419
class ModelFactory(object):
    """
    Used by parsers for creating instances
    of models. You may subclass this factory
    to add your own extended models.
    """

    status = Status
    user = User
    direct_message = DirectMessage
    friendship = Friendship
    saved_search = SavedSearch
420
    search_results = SearchResults
421
    category = Category
422
    list = List
Aaron Swartz's avatar
Aaron Swartz committed
423
    relation = Relation
424
    relationship = Relationship
425

426
427
    json = JSONModel
    ids = IDModel
428
429
    place = Place
    bounding_box = BoundingBox
430