2 from diaspy
.models
import Post
4 """Docstrings for this module are taken from:
5 https://gist.github.com/MrZYX/01c93096c30dc44caf71
7 Documentation for D* JSON API taken from:
8 http://pad.spored.de/ro/r.qWmvhSZg7rk4OQam
12 """Object representing generic stream. Used in Tag(),
13 Stream(), Activity() etc.
15 def __init__(self
, connection
, location
=''):
17 :param connection: Connection() object
18 :type connection: diaspy.connection.Connection
19 :param location: location of json
22 self
._connection
= connection
24 if location
: self
._location
= location
28 def __contains__(self
, post
):
29 """Returns True if stream contains given post.
31 if type(post
) is not Post
:
32 raise TypeError('stream can contain only posts: checked for {0}'.format(type(post
)))
33 return post
in self
._stream
36 """Provides iterable interface for stream.
38 return iter(self
._stream
)
40 def __getitem__(self
, n
):
41 """Returns n-th item in Stream.
43 return self
._stream
[n
]
46 """Returns length of the Stream.
48 return len(self
._stream
)
50 def _setlocation(self
):
51 """Sets location of the stream.
52 Location defaults to 'stream.json'
54 NOTICE: inheriting objects should override this method
55 and set their own value to the `location`.
56 However, it is possible to override default location by
57 passing the desired one to the constructor.
60 def _setlocation(self):
61 self._location = 'foo.json'
63 :param location: url of the stream
67 self
._location
= 'stream.json'
71 """Obtains stream from pod.
73 request
= self
._connection
.get(self
._location
)
74 if request
.status_code
!= 200:
75 raise Exception('wrong status code: {0}'.format(request
.status_code
))
76 return [Post(str(post
['id']), self
._connection
) for post
in request
.json()]
79 """Removes all posts from stream.
84 """Removes all unexistent posts from stream.
87 for post
in self
._stream
:
95 if not deleted
: stream
.append(post
)
101 new_stream
= self
._obtain
()
102 ids
= [post
.post_id
for post
in self
._stream
]
104 stream
= self
._stream
105 for i
in range(len(new_stream
)):
106 if new_stream
[-i
].post_id
not in ids
:
107 stream
= [new_stream
[-i
]] + stream
108 ids
.append(new_stream
[-i
].post_id
)
110 self
._stream
= stream
113 """Fills the stream with posts.
115 self
._stream
= self
._obtain
()
118 class Outer(Generic
):
119 """Object used by diaspy.models.User to represent
120 stream of other user.
123 """Obtains stream of other user.
125 request
= self
._connection
.get(self
._location
)
126 if request
.status_code
!= 200:
127 raise Exception('wrong status code: {0}'.format(request
.status_code
))
128 return [Post(str(post
['id']), self
._connection
) for post
in request
.json()]
131 class Stream(Generic
):
132 """The main stream containing the combined posts of the
133 followed users and tags and the community spotlights posts
134 if the user enabled those.
136 def _setlocation(self
):
137 self
._location
= 'stream.json'
139 def post(self
, text
='', aspect_ids
='public', photos
=None, photo
=''):
140 """This function sends a post to an aspect.
141 If both `photo` and `photos` are specified `photos` takes precedence.
143 :param text: Text to post.
145 :param aspect_ids: Aspect ids to send post to.
146 :type aspect_ids: str
147 :param photo: filename of photo to post
149 :param photos: id of photo to post (obtained from _photoupload())
152 :returns: diaspy.models.Post -- the Post which has been created
155 data
['aspect_ids'] = aspect_ids
156 data
['status_message'] = {'text': text
}
157 if photo
: data
['photos'] = self
._photoupload
(photo
)
158 if photos
: data
['photos'] = photos
160 request
= self
._connection
.post('status_messages',
161 data
=json
.dumps(data
),
162 headers
={'content-type': 'application/json',
163 'accept': 'application/json',
164 'x-csrf-token': self
._connection
.get_token()})
165 if request
.status_code
!= 201:
166 raise Exception('{0}: Post could not be posted.'.format(request
.status_code
))
168 post
= Post(str(request
.json()['id']), self
._connection
)
171 def _photoupload(self
, filename
):
172 """Uploads picture to the pod.
174 :param filename: path to picture file
177 :returns: id of the photo being uploaded
179 data
= open(filename
, 'rb')
184 params
['photo[pending]'] = 'true'
185 params
['set_profile_image'] = ''
186 params
['qqfile'] = filename
187 aspects
= self
._connection
.getUserInfo()['aspects']
188 for i
, aspect
in enumerate(aspects
):
189 params
['photo[aspect_ids][{0}]'.format(i
)] = aspect
['id']
191 headers
= {'content-type': 'application/octet-stream',
192 'x-csrf-token': self
._connection
.get_token(),
193 'x-file-name': filename
}
195 request
= self
._connection
.post('photos', data
=image
, params
=params
, headers
=headers
)
196 if request
.status_code
!= 200:
197 raise Exception('wrong error code: {0}'.format(request
.status_code
))
198 return request
.json()['data']['photo']['id']
201 class Activity(Generic
):
202 """Stream representing user's activity.
204 def _setlocation(self
):
205 self
._location
= 'activity.json'
207 def _delid(self
, id):
208 """Deletes post with given id.
211 for p
in self
._stream
:
215 if post
is not None: post
.delete()
217 def delete(self
, post
):
218 """Deletes post from users activity.
219 `post` can be either post id or Post()
220 object which will be identified and deleted.
221 After deleting post the stream will be filled.
223 :param post: post identifier
224 :type post: str, diaspy.models.Post
226 if type(post
) == str: self
._delid
(post
)
227 elif type(post
) == Post
: post
.delete()
229 raise TypeError('this method accepts only int, str or Post: {0} given')
233 class Aspects(Generic
):
234 """This stream contains the posts filtered by
235 the specified aspect IDs. You can choose the aspect IDs with
236 the parameter `aspect_ids` which value should be
237 a comma seperated list of aspect IDs.
238 If the parameter is ommitted all aspects are assumed.
239 An example call would be `aspects.json?aspect_ids=23,5,42`
241 def _setlocation(self
):
242 self
._location
= 'aspects.json'
244 def add(self
, aspect_name
, visible
=0):
245 """This function adds a new aspect.
247 data
= {'authenticity_token': self
._connection
.get_token(),
248 'aspect[name]': aspect_name
,
249 'aspect[contacts_visible]': visible
}
251 r
= self
._connection
.post('aspects', data
=data
)
252 if r
.status_code
!= 200:
253 raise Exception('wrong status code: {0}'.format(r
.status_code
))
255 def remove(self
, aspect_id
):
256 """This method removes an aspect.
258 data
= {'authenticity_token': self
.connection
.get_token()}
259 r
= self
.connection
.delete('aspects/{}'.format(aspect_id
),
261 if r
.status_code
!= 404:
262 raise Exception('wrong status code: {0}'.format(r
.status_code
))
265 class Commented(Generic
):
266 """This stream contains all posts
267 the user has made a comment on.
269 def _setlocation(self
):
270 self
._location
= 'commented.json'
273 class Liked(Generic
):
274 """This stream contains all posts the user liked.
276 def _setlocation(self
):
277 self
._location
= 'liked.json'
280 class Mentions(Generic
):
281 """This stream contains all posts
282 the user is mentioned in.
284 def _setlocation(self
):
285 self
._location
= 'mentions.json'
288 class FollowedTags(Generic
):
289 """This stream contains all posts
290 containing tags the user is following.
292 def _setlocation(self
):
293 self
._location
= 'followed_tags.json'
295 def remove(self
, tag_id
):
296 """Stop following a tag.
298 :param tag_id: tag id
301 data
= {'authenticity_token':self
._connection
.get_token()}
302 request
= self
._connection
.delete('tag_followings/{0}'.format(tag_id
), data
=data
)
303 if request
.status_code
!= 404:
304 raise Exception('wrong status code: {0}'.format(request
.status_code
))
306 def add(self
, tag_name
):
308 Error code 403 is accepted because pods respod with it when request
309 is sent to follow a tag that a user already follows.
311 :param tag_name: tag name
313 :returns: int (response code)
315 data
= {'name':tag_name
,
316 'authenticity_token':self
._connection
.get_token(),
318 headers
={'content-type': 'application/json',
319 'x-csrf-token': self
._connection
.get_token(),
320 'accept': 'application/json'}
322 request
= self
._connection
.post('tag_followings', data
=json
.dumps(data
), headers
=headers
)
324 if request
.status_code
not in [201, 403]:
325 raise Exception('wrong error code: {0}'.format(request
.status_code
))
326 return request
.status_code