* __upd__: Add `Comment()` to `diaspy.models.Post.comments` on `diaspy.models.Post...
authorCYBERDEViLNL <CYBERDEViLNL@github.com>
Tue, 18 Dec 2018 23:25:33 +0000 (00:25 +0100)
committerCYBERDEViLNL <CYBERDEViLNL@github.com>
Tue, 18 Dec 2018 23:25:33 +0000 (00:25 +0100)
* __upd__:  Delete `Comment()` from `diaspy.models.Post.comments` on `diaspy.models.Post.delete_comment()`
* __upd__:  Update `diaspy.models.Post._data["poll"]["participation_count"]` and `diaspy.models.Post._data["poll_participation_answer_id"]` after `diaspy.models.Post.vote_poll()`
* __upd__:  Update `diaspy.models.Post._data["participation"]` on `diaspy.models.Post.subscribe()` and `diaspy.models.Post.unsubscribe()`
* __upd__:  Removed `verify` keyword from `diaspy.connection.Connection` it's methods, it's now done through `**requestKwargs`. Default is still `True`.
* __upd__:  `diaspy.streams.Stream._photoupload()` to more public method `diaspy.streams.Stream.photoupload()`
* __upd__:  Update `diaspy.models.Notification.unread` and `diaspy.models.Notification._data['unread']` on `diasply.models.Notification.mark()`

* __fix__:  Don't update `diaspy.notifications.Notifications` it's `unread_count_by_type` and `unread_count` on `diaspy.notifications.Notifications._expand()` and `diaspy.notifications.Notifications._update()` since it's already done in `diaspy.notifications.Notifications._finalize()`

* __new__:  `diaspy.streams.Generic.deletePostGuid()` deletes `Post` with given `guid` from the local stream object. (If you manualy update a post and find that it's been deleted, call this with the `guid` of the deleted `Post`)
* __new__:  `diaspy.streams.Stream.deletephoto()`
* __new__:  `diaspy.notifications.Notifications.__len__()`
* __new__:  `diaspy.notifications.Notifications.data()`
* __new__:  `diaspy.models.Comments.delete()` deletes comment by `id`.
* __new__:  `diaspy.errors.NotificationError`

Note: the current Camo check will be removed and replaced by nodeInfo soon.

Changelog.markdown
diaspy/connection.py
diaspy/errors.py
diaspy/models.py
diaspy/notifications.py
diaspy/streams.py

index e7b0af153402b4997611e30b7fff1970c9d03dbe..70c584bc07cf9ca8a65db22ee2976e30f9771db5 100644 (file)
@@ -30,9 +30,19 @@ up-to-date than manual and if conflicts appear they should follow the order:
 #### Version `0.6.1.dev` (not final changelog for this version, still in development)
 
 * __upd__:  `diaspy.models.Post.like()`, `diaspy.models.Post.delete_like()`, `diaspy.models.Post.reshare()` will now update data (count and likes/reshares) without doing another request.
+* __upd__:  Add `Comment()` to `diaspy.models.Post.comments` on `diaspy.models.Post.comment()`
+* __upd__:  Delete `Comment()` from `diaspy.models.Post.comments` on `diaspy.models.Post.delete_comment()`
+* __upd__:  Update `diaspy.models.Post._data["poll"]["participation_count"]` and `diaspy.models.Post._data["poll_participation_answer_id"]` after `diaspy.models.Post.vote_poll()`
+* __upd__:  Update `diaspy.models.Post._data["participation"]` on `diaspy.models.Post.subscribe()` and `diaspy.models.Post.unsubscribe()`
+* __upd__:  Removed `verify` keyword from `diaspy.connection.Connection` it's methods, it's now done through `**requestKwargs`. Default is still `True`.
+* __upd__:  `diaspy.streams.Stream._photoupload()` to more public method `diaspy.streams.Stream.photoupload()`
+* __upd__:  Update `diaspy.models.Notification.unread` and `diaspy.models.Notification._data['unread']` on `diasply.models.Notification.mark()`
+
 
 * __fix__:  `diaspy.models.Post.__init__()` checking on different fetch states was a mess.
 * __fix__:  `diaspy.streams.Asepcts.filter()` location fix.
+* __fix__:  Don't update `diaspy.notifications.Notifications` it's `unread_count_by_type` and `unread_count` on `diaspy.notifications.Notifications._expand()` and `diaspy.notifications.Notifications._update()` since it's already done in `diaspy.notifications.Notifications._finalize()`
+
 
 * __new__:  `diaspy.tagFollowings.TagFollowings()` which represents the tags followed by the user.
 * __new__:  `diaspy.models.FollowedTag()` which represents a tag followed by the user. It is used by `diaspy.tagFollowings.TagFollowings()`.
@@ -42,6 +52,12 @@ up-to-date than manual and if conflicts appear they should follow the order:
 * __new__:  `diaspy.streams.Public()`
 * __new__:  `diaspy.models.Post.fetchlikes()`.
 * __new__:  `diaspy.models.Post.fetchreshares()`
+* __new__:  `diaspy.streams.Generic.deletePostGuid()` deletes `Post` with given `guid` from the local stream object. (If you manualy update a post and find that it's been deleted, call this with the `guid` of the deleted `Post`)
+* __new__:  `diaspy.streams.Stream.deletephoto()`
+* __new__:  `diaspy.notifications.Notifications.__len__()`
+* __new__:  `diaspy.notifications.Notifications.data()`
+* __new__:  `diaspy.models.Comments.delete()` deletes comment by `id`.
+* __new__:  `diaspy.errors.NotificationError`
 
 * __rem__:  `diaspy.streams.FollowedTags.get()` since it wasn’t doing anything usefull.
 
index 24a6e95376dd2e506a3545c6699e80714dbab698..2c91b7ee8c7ee72b2017ffb01c3301e46b70cec5 100644 (file)
@@ -45,7 +45,9 @@ class Connection():
                self._token = ''
                self._diaspora_session = ''
                self._fetch_token_from = 'stream'
-               self._requests_kwargs = requestsKwargs
+               self._requests_kwargs = {'verify':self._verify_SSL}
+               if requestsKwargs: self._requests_kwargs.update(requestsKwargs)
+
                self._camo_enabled = False
                try: self._setlogin(username, password)
                except requests.exceptions.MissingSchema:
@@ -98,7 +100,7 @@ class Connection():
                if not direct: url = '{0}/{1}'.format(self.pod, string)
                else: url = string
                if not kwargs: kwargs = self._requests_kwargs
-               return self._session.get(url, params=params, headers=headers, verify=self._verify_SSL, **kwargs)
+               return self._session.get(url, params=params, headers=headers, **kwargs)
 
        def tokenFrom(self, location):
                """Sets location for the *next* fetch of CSRF token.
@@ -131,7 +133,7 @@ class Connection():
                if 'X-CSRF-Token' not in headers:
                        headers['X-CSRF-Token'] = self.get_token()
                if not kwargs: kwargs = self._requests_kwargs
-               request = self._session.post(string, data, headers=headers, params=params, verify=self._verify_SSL, **kwargs)
+               request = self._session.post(string, data, headers=headers, params=params, **kwargs)
                return request
 
        def put(self, string, data=None, headers={}, params={}, **kwargs):
@@ -142,7 +144,7 @@ class Connection():
                        headers['X-CSRF-Token'] = self.get_token()
                if not kwargs: kwargs = self._requests_kwargs
                if data is not None: request = self._session.put(string, data, headers=headers, params=params, **kwargs)
-               else: request = self._session.put(string, headers=headers, params=params, verify=self._verify_SSL, **kwargs)
+               else: request = self._session.put(string, headers=headers, params=params, **kwargs)
                return request
 
        def delete(self, string, data = None, headers={}, **kwargs):
@@ -159,7 +161,7 @@ class Connection():
                if 'X-CSRF-Token' not in headers:
                        headers['X-CSRF-Token'] = self.get_token()
                if not kwargs: kwargs = self._requests_kwargs
-               request = self._session.delete(string, data=data, headers=headers, verify=self._verify_SSL, **kwargs)
+               request = self._session.delete(string, data=data, headers=headers, **kwargs)
                return request
 
        def _checkCamo(self):
@@ -208,6 +210,9 @@ class Connection():
                """
                self.get('users/sign_out')
                self.token = ''
+               self._userdata = {}
+               self._diaspora_session = ''
+               self._camo_enabled = False
 
        def podswitch(self, pod, username, password, login=True):
                """Switches pod from current to another one.
@@ -272,3 +277,4 @@ class Connection():
                """Sets whether there should be an error if a SSL-Certificate could not be verified.
                """
                self._verify_SSL = verify
+               self._requests_kwargs.update({'verify':verify})
index 48d754a5393a44ad18965d33081e23c77c8a5e3a..5aa612a309cb60ec8b4d6eb3c2ad0e459ddf0232 100644 (file)
@@ -72,6 +72,10 @@ class SearchError(DiaspyError):
        """
        pass
 
+class NotificationError(DiaspyError):
+       """Exception raised when something related to notifications goes wrong.
+       """
+       pass
 
 class ConversationError(DiaspyError):
        """Exception raised when something related to conversations goes wrong.
index d256c247fd5d767e6cc1e46a19a74c1861b8240a..e06f8dc40c2105a560f5f68fbe95f36773600d29 100644 (file)
@@ -212,8 +212,11 @@ class Notification():
                """
                headers = {'x-csrf-token': repr(self._connection)}
                params = {'set_unread': json.dumps(unread)}
-               self._connection.put('notifications/{0}'.format(self['id']), params=params, headers=headers)
+               response = self._connection.put('notifications/{0}'.format(self['id']), params=params, headers=headers)
+               if response.status_code != 200:
+                       raise errors.NotificationError('Cannot mark notification: {0}'.format(response.status_code))
                self._data['unread'] = unread
+               self.unread = unread
 
 
 class Conversation():
@@ -450,6 +453,12 @@ class Comments():
        def ids(self):
                return [c.id for c in self._comments]
 
+       def delete(self, comment_id):
+               for index, comment in enumerate(self._comments):
+                       if comment.id == comment_id:
+                               self._comments.pop(index);
+                               break;
+
        def add(self, comment):
                """ Expects Comment() object
 
@@ -633,7 +642,9 @@ class Post():
                if request.status_code != 201:
                        raise Exception('{0}: Comment could not be posted.'
                                                        .format(request.status_code))
-               return Comment(request.json())
+               comment = Comment(request.json())
+               self.comments.add(comment);
+               return comment
 
        def vote_poll(self, poll_answer_id):
                """This function votes on a post's poll
@@ -652,7 +663,16 @@ class Post():
                if request.status_code != 201:
                        raise Exception('{0}: Vote on poll failed.'
                                                        .format(request.status_code))
-               return request.json()
+
+               data = request.json()
+               self._data["poll"]["participation_count"] += 1
+               self._data["poll_participation_answer_id"] = data["poll_participation"]["poll_answer_id"]
+               
+               for answer in self._data["poll"]["poll_answers"]:
+                       if answer["id"] == poll_answer_id:
+                               answer["vote_count"] +=1;
+                               break;
+               return data
 
        def hide(self):
                """
@@ -693,6 +713,8 @@ class Post():
                        raise Exception('{0}: Failed to subscribe to post'
                                                        .format(request.status_code))
 
+               self._data.update({"participation" : True})
+
        def unsubscribe(self):
                """
                ->      POST /posts/123/participation HTTP/1.1
@@ -707,6 +729,8 @@ class Post():
                        raise Exception('{0}: Failed to unsubscribe to post'
                                                        .format(request.status_code))
 
+               self._data.update({"participation" : False})
+
        def report(self):
                """
                TODO
@@ -739,6 +763,8 @@ class Post():
                        raise errors.PostError('{0}: Comment could not be deleted'
                                                                   .format(request.status_code))
 
+               self.comments.delete(comment_id)
+
        def delete_like(self):
                """This function removes a like from a post
                """
index cb5b42f3ff25f984022eaca700cffec47bc93a44..877c80ff660f62673836d3e853dcbe907d0904c3 100644 (file)
@@ -19,6 +19,8 @@ class Notifications():
                self._notifications = self.get()
                self.page = 1
 
+       def __len__(self): return len(self._notifications);
+
        def __iter__(self):
                return iter(self._notifications)
 
@@ -30,6 +32,8 @@ class Notifications():
                self._data['unread_count_by_type'] = notifications['unread_count_by_type']
                return [Notification(self._connection, n) for n in notifications.get('notification_list', [])]
 
+       def data(self): return self._data;
+
        def last(self):
                """Returns list of most recent notifications.
                """
@@ -48,13 +52,10 @@ class Notifications():
                data = self._data
                for n in new_notifications:
                        if n.id not in ids:
-                               if n.unread:
-                                       data['unread_count'] +=1
-                                       data['unread_count_by_type'][n.type] +=1
                                notifications.append(n)
                                ids.append(n.id)
                self._notifications = notifications
-               self._data = data
+               self._data.update(data);
 
        def _update(self, new_notifications):
                ids = [notification.id for notification in self._notifications]
@@ -67,9 +68,6 @@ class Notifications():
 
                for i in range(len(new_notifications)):
                        if new_notifications[-i].id not in ids:
-                               if new_notifications[-i].unread:
-                                       data['unread_count'] +=1
-                                       data['unread_count_by_type'][new_notifications[-i].type] +=1
                                notifications = [new_notifications[-i]] + notifications
                                ids.append(new_notifications[-i].id)
                self._notifications = notifications
@@ -84,8 +82,7 @@ class Notifications():
                if not page: page = self.page + 1
                self.page = page
                result = self.get(per_page=per_page, page=page)
-               if result:
-                       self._expand( result )
+               if result: self._expand( result )
 
        def get(self, per_page=5, page=1):
                """Returns list of notifications.
index c76946d06e925de66c5718e8483fd1a69dd89cbb..2593b22f052b671b1fb50f189ea219cce70a2d99 100644 (file)
@@ -131,6 +131,21 @@ class Generic():
                """
                self._stream = []
 
+       def deletePostGuid(self, guid):
+               """Deleted post from stream by guid
+
+               :param guid: guid of the post to delete
+               :type guid: str
+
+               NOTE: This affects local object only! So no request will be made to a
+               pod. This can be used if you tried to update a Post() and it failed then
+               you know the post is deleted.
+               """
+               for index, post in enumerate(self._stream):
+                       if post.guid == guid:
+                               self._stream.pop(index);
+                               break;
+
        def purge(self):
                """Removes all unexistent posts from stream.
                """
@@ -273,7 +288,7 @@ class Stream(Generic):
                :param photo: filename of photo to post
                :type photo: str
 
-               :param photos: id of photo to post (obtained from _photoupload())
+               :param photos: id of photo to post (obtained from photoupload())
                :type photos: int
 
                :param provider_display_name: name of provider displayed under the post
@@ -294,7 +309,7 @@ class Stream(Generic):
                data['aspect_ids'] = aspect_ids
                data['status_message'] = ({'text': text,
                                                                'provider_display_name': provider_display_name})
-               if photo: data['photos'] = self._photoupload(photo)
+               if photo: data['photos'] = self.photoupload(photo)
                if photos: data['photos'] = photos
                if poll_question and poll_answers:
                        data['poll_question'] = poll_question
@@ -312,9 +327,23 @@ class Stream(Generic):
                post_json = request.json()
                post = Post(self._connection, id=post_json['id'],
                                        guid=post_json['guid'], post_data=post_json)
+               self._stream.insert(0, post);
                return post
 
-       def _photoupload(self, filename, aspects=[]):
+       def deletephoto(self, id):
+               """Remove photo from the pod (if decided not needed anymore).
+
+               :param id: photo id to delete
+               :type id: int
+               """
+               data = {'authenticity_token': repr(self._connection)}
+               request = self._connection.delete('photos/{0}'.format(id),
+                                                                               data=data,
+                                                                               headers={'accept': 'application/json'})
+               if request.status_code != 204:
+                       raise errors.StreamError('{0}: Photo could not be deleted'.format(request.status_code))
+
+       def photoupload(self, filename, aspects=[]):
                """Uploads picture to the pod.
 
                :param filename: path to picture file