models.py 11.5 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
6
7
8
9
10
11
from tweepy.utils import parse_datetime, parse_html_value, parse_a_href, \
        parse_search_datetime, unescape_html


class ResultSet(list):
    """A list like object that holds results from a Twitter API query."""
Josh Roesslein's avatar
Josh Roesslein committed
12

Josh Roesslein's avatar
Josh Roesslein committed
13

14
15
class Model(object):

16
17
18
    def __init__(self, api=None):
        self._api = api

Josh Roesslein's avatar
Josh Roesslein committed
19
20
    def __getstate__(self):
        # pickle
Joshua's avatar
Joshua committed
21
        pickle = dict(self.__dict__)
22
23
24
25
        try:
            del pickle['_api']  # do not pickle the API reference
        except KeyError:
            pass
Josh Roesslein's avatar
Josh Roesslein committed
26
27
        return pickle

28
29
30
31
32
33
34
35
36
37
    @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:
38
39
            if obj:
                results.append(cls.parse(api, obj))
40
41
        return results

Josh Roesslein's avatar
Josh Roesslein committed
42

43
class Status(Model):
44

45
46
47
48
49
    @classmethod
    def parse(cls, api, json):
        status = cls(api)
        for k, v in json.items():
            if k == 'user':
50
51
                user_model = getattr(api.parser.model_factory, 'user')
                user = user_model.parse(api, v)
52
53
54
55
56
57
58
59
60
61
                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)
62
                    setattr(status, 'source_url', None)
63
            elif k == 'retweeted_status':
64
                setattr(status, k, Status.parse(api, v))
65
66
            elif k == 'place':
                setattr(status, k, Place.parse(api, v))
67
68
69
70
            else:
                setattr(status, k, v)
        return status

Josh Roesslein's avatar
Josh Roesslein committed
71
    def destroy(self):
72
73
74
75
76
77
78
79
80
81
        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
82

83

84
class User(Model):
85

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
    @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
116
    def timeline(self, **kargs):
Joshua Roesslein's avatar
Joshua Roesslein committed
117
        return self._api.user_timeline(user_id=self.id, **kargs)
Josh Roesslein's avatar
Josh Roesslein committed
118
119

    def friends(self, **kargs):
Joshua Roesslein's avatar
Joshua Roesslein committed
120
        return self._api.friends(user_id=self.id, **kargs)
Josh Roesslein's avatar
Josh Roesslein committed
121
122

    def followers(self, **kargs):
Arthur Debert's avatar
Arthur Debert committed
123
        return self._api.followers(user_id=self.id, **kargs)
Josh Roesslein's avatar
Josh Roesslein committed
124
125
126
127
128
129
130
131
132

    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

133
134
    def lists_memberships(self, *args, **kargs):
        return self._api.lists_memberships(user=self.screen_name, *args, **kargs)
135

136
137
    def lists_subscriptions(self, *args, **kargs):
        return self._api.lists_subscriptions(user=self.screen_name, *args, **kargs)
138

139
140
    def lists(self, *args, **kargs):
        return self._api.lists(user=self.screen_name, *args, **kargs)
141

142
143
    def followers_ids(self, *args, **kargs):
        return self._api.followers_ids(user_id=self.id, *args, **kargs)
144

145

146
class DirectMessage(Model):
147

148
149
150
151
152
153
154
155
156
157
158
159
    @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
160
    def destroy(self):
Josh Roesslein's avatar
Josh Roesslein committed
161
        return self._api.destroy_direct_message(self.id)
Josh Roesslein's avatar
Josh Roesslein committed
162

163

164
class Friendship(Model):
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    @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
181

182

183
class SavedSearch(Model):
184

185
186
187
188
189
190
191
192
193
194
    @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
195
196
    def destroy(self):
        return self._api.destroy_saved_search(self.id)
Josh Roesslein's avatar
Josh Roesslein committed
197

Josh Roesslein's avatar
Josh Roesslein committed
198

199
class SearchResult(Model):
Josh Roesslein's avatar
Josh Roesslein committed
200

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
    @classmethod
    def parse(cls, api, json):
        result = cls()
        for k, v in json.items():
            if k == 'created_at':
                setattr(result, k, parse_search_datetime(v))
            elif k == 'source':
                setattr(result, k, parse_html_value(unescape_html(v)))
            else:
                setattr(result, k, v)
        return result

    @classmethod
    def parse_list(cls, api, json_list, result_set=None):
        results = ResultSet()
        results.max_id = json_list.get('max_id')
        results.since_id = json_list.get('since_id')
        results.refresh_url = json_list.get('refresh_url')
        results.next_page = json_list.get('next_page')
        results.results_per_page = json_list.get('results_per_page')
        results.page = json_list.get('page')
        results.completed_in = json_list.get('completed_in')
        results.query = json_list.get('query')

        for obj in json_list['results']:
            results.append(cls.parse(api, obj))
        return results

229

Josh Roesslein's avatar
Josh Roesslein committed
230
231
class List(Model):

232
233
234
235
236
237
    @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))
238
239
            elif k == 'created_at':
                setattr(lst, k, parse_datetime(v))
240
241
242
243
244
245
246
247
248
249
250
            else:
                setattr(lst, k, v)
        return lst

    @classmethod
    def parse_list(cls, api, json_list, result_set=None):
        results = ResultSet()
        for obj in json_list['lists']:
            results.append(cls.parse(api, obj))
        return results

251
252
253
    def update(self, **kargs):
        return self._api.update_list(self.slug, **kargs)

Josh Roesslein's avatar
Josh Roesslein committed
254
255
    def destroy(self):
        return self._api.destroy_list(self.slug)
Josh Roesslein's avatar
Josh Roesslein committed
256

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
    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
284
285
286
287
288
289
290
291
292
293
294
295
296
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

297
298
299
300
301
302
303
304
305
306
307
class Relationship(Model):
    @classmethod
    def parse(cls, api, json):
        result = cls(api)
        for k,v in json.items():
            if k == 'connections':
            	setattr(result, 'is_following', 'following' in v)
                setattr(result, 'is_followed_by', 'followed_by' in v)
            else:
                setattr(result, k, v)
        return result
308

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
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']


326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
class BoundingBox(Model):

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

    def origin(self):
        """
        Return longitude, latitude of northwest corner of bounding box, as
        tuple.  This assumes that bounding box is always a rectangle, which
        appears to be the case at present.
        """
        return tuple(self.coordinates[0][0])

    def corner(self):
        """
        Return longitude, latitude of southeast corner of bounding box, as
        tuple.  This assumes that bounding box is always a rectangle, which
        appears to be the case at present.
        """
        return tuple(self.coordinates[0][1])


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

385
386
387
388
389
390
391
392
393
394
395
396
397
398
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
    search_result = SearchResult
    list = List
Aaron Swartz's avatar
Aaron Swartz committed
399
    relation = Relation
400
    relationship = Relationship
401

402
403
    json = JSONModel
    ids = IDModel
404
405
    place = Place
    bounding_box = BoundingBox
406