* __upd__: `diaspy.models.Post.like()`, `diaspy.models.Post.delete_like()`, `diaspy...
authorCYBERDEViLNL <CYBERDEViLNL@github.com>
Thu, 6 Dec 2018 18:16:11 +0000 (19:16 +0100)
committerCYBERDEViLNL <CYBERDEViLNL@github.com>
Thu, 6 Dec 2018 18:16:11 +0000 (19:16 +0100)
* __fix__:  `diaspy.models.Post.__init__()` checking on different fetch states was a mess.
* __fix__:  `diaspy.streams.Asepcts.filter()` location fix.
* __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()`.
* __new__:  It is now possible to give `**requestKwargs` to `diaspy.connection.Connection()` which will be used for every `request` unless directly overwritten by given the function you call different key-worded arguments.
* __new__:  `diaspy.connection.Connection()` now does check if the `pod` you are connecting to has `Camo` enabled or not. Call `diaspy.connection.Connection.camo()` to receive `True` or `False`.
* __new__:  `diaspy.models.Comment.authordata()` which will return all author data instead of `diaspy.models.Comment.author()` which will only return data for a certain key.
* __new__:  `diaspy.streams.Public()`
* __new__:  `diaspy.models.Post.fetchlikes()`.
* __new__:  `diaspy.models.Post.fetchreshares()`
* __rem__:  `diaspy.streams.FollowedTags.get()` since it wasn’t doing anything usefull.
* __dep__:  `diaspy.streams.FollowedTags.remove()` Use `diaspy.tagFollowings.TagFollowings[“tagName”].delete()` instead.
* __dep__:  `diaspy.streams.FollowedTags.add()` Use diaspy.tagFollowings.TagFollowings.follow() instead.

Changelog.markdown
diaspy/__init__.py
diaspy/connection.py
diaspy/models.py
diaspy/streams.py
diaspy/tagFollowings.py [new file with mode: 0644]
docs/source/conf.py
setup.py

index 8b11250e2ffbc6948dd230b5ef67319e42e8a186..e7b0af153402b4997611e30b7fff1970c9d03dbe 100644 (file)
@@ -27,6 +27,31 @@ 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.
+
+* __fix__:  `diaspy.models.Post.__init__()` checking on different fetch states was a mess.
+* __fix__:  `diaspy.streams.Asepcts.filter()` location fix.
+
+* __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()`.
+* __new__:  It is now possible to give `**requestKwargs` to `diaspy.connection.Connection()` which will be used for every `request` unless directly overwritten by given the function you call different key-worded arguments.
+* __new__:  `diaspy.connection.Connection()` now does check if the `pod` you are connecting to has `Camo` enabled or not. Call `diaspy.connection.Connection.camo()` to receive `True` or `False`.
+* __new__:  `diaspy.models.Comment.authordata()` which will return all author data instead of `diaspy.models.Comment.author()` which will only return data for a certain key.
+* __new__:  `diaspy.streams.Public()`
+* __new__:  `diaspy.models.Post.fetchlikes()`.
+* __new__:  `diaspy.models.Post.fetchreshares()`
+
+* __rem__:  `diaspy.streams.FollowedTags.get()` since it wasn’t doing anything usefull.
+
+* __dep__:  `diaspy.streams.FollowedTags.remove()` Use `diaspy.tagFollowings.TagFollowings[“tagName”].delete()` instead.
+* __dep__:  `diaspy.streams.FollowedTags.add()` Use diaspy.tagFollowings.TagFollowings.follow() instead.
+
+
+----
+
+
 #### Version `0.6.0`
 
 In this release some bugs due to Diaspora changes where adressed, it also 
index fee78fdc579370103693bf50af72c7f57abf5e85..aa470b6713c824a873db1fc8af48f79f2e28b6e0 100644 (file)
@@ -8,6 +8,7 @@ import diaspy.conversations as conversations
 import diaspy.people as people
 import diaspy.notifications as notifications
 import diaspy.settings as settings
+import diaspy.tagFollowings as tagFollowings
 
 
-__version__ = '0.6.0'
+__version__ = '0.6.1.dev'
index 2001c4543485aafe7797c6af5aee3fd80abb47c4..24a6e95376dd2e506a3545c6699e80714dbab698 100644 (file)
@@ -27,7 +27,7 @@ class Connection():
        _userinfo_regex_2 = re.compile(r'gon.user=({.*?});gon.')
        _verify_SSL = True
 
-       def __init__(self, pod, username, password, schema='https'):
+       def __init__(self, pod, username, password, schema='https', **requestsKwargs):
                """
                :param pod: The complete url of the diaspora pod to use.
                :type pod: str
@@ -35,6 +35,8 @@ class Connection():
                :type username: str
                :param password: The password used to log in.
                :type password: str
+               :param requestsKwargs: default kwargs for requests (proxy, timeout, etc)
+               :type requestsKwargs: keyworded arguments
                """
                self.pod = pod
                self._session = requests.Session()
@@ -43,15 +45,22 @@ class Connection():
                self._token = ''
                self._diaspora_session = ''
                self._fetch_token_from = 'stream'
+               self._requests_kwargs = requestsKwargs
+               self._camo_enabled = False
                try: self._setlogin(username, password)
                except requests.exceptions.MissingSchema:
                        self.pod = '{0}://{1}'.format(schema, self.pod)
                        warnings.warn('schema was missing')
+
                        try: self._setlogin(username, password)
                        except Exception as e:
                                raise errors.LoginError('cannot create login data (caused by: {0})'.format(e))
                self._cookies = self._fetchcookies()
 
+       def __bool__(self):
+               if self._token: return True
+               return False
+
        def _fetchcookies(self):
                request = self.get('stream')
                return request.cookies
@@ -64,6 +73,16 @@ class Connection():
                """
                return self._fetchtoken()
 
+       def requestsKwargs(self):
+               """Returns keyworded arguments set to use for all requests.
+               """
+               return self._requests_kwargs
+
+       def setRequestsKwargs(self, **requestsKwargs):
+               """Sets keyworded arguments that will be used for earch request.
+               """
+               self._requests_kwargs = requestsKwargs
+
        def get(self, string, headers={}, params={}, direct=False, **kwargs):
                """This method gets data from session.
                Performs additional checks if needed.
@@ -78,6 +97,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)
 
        def tokenFrom(self, location):
@@ -110,6 +130,7 @@ class Connection():
                string = '{0}/{1}'.format(self.pod, string)
                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)
                return request
 
@@ -119,6 +140,7 @@ class Connection():
                string = '{0}/{1}'.format(self.pod, string)
                if 'X-CSRF-Token' not in headers:
                        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)
                return request
@@ -136,9 +158,18 @@ class Connection():
                string = '{0}/{1}'.format(self.pod, string)
                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)
                return request
 
+       def _checkCamo(self):
+               response = self._session.head("{0}/camo/".format(self.pod),
+                                                                               **self._requests_kwargs)
+               if response.status_code == 200: self._camo_enabled = True
+               else: self._camo_enabled = False
+
+       def camo(self): return self._camo_enabled;
+
        def _setlogin(self, username, password):
                """This function is used to set data for login.
 
@@ -158,6 +189,7 @@ class Connection():
                                                        allow_redirects=False)
                if request.status_code != 302:
                        raise errors.LoginError('{0}: login failed'.format(request.status_code))
+               self._checkCamo()
 
        def login(self, remember_me=1):
                """This function is used to log in to a pod.
@@ -223,8 +255,7 @@ class Connection():
                """
                return self._diaspora_session
 
-       def userdata(self):
-               return self._userdata
+       def userdata(self): return self._userdata
 
        def getUserData(self):
                """Returns user data.
index 9468ddcbf442ce25df82ffd15f22275fb2d6e0e3..d256c247fd5d767e6cc1e46a19a74c1861b8240a 100644 (file)
@@ -425,6 +425,11 @@ class Comment():
                """
                return self._data['author'][key]
 
+       def authordata(self):
+               """Returns all author data of the comment.
+               """
+               return self._data['author']
+
 class Comments():
        def __init__(self, comments=[]):
                self._comments = comments
@@ -492,16 +497,11 @@ class Post():
                self.guid = guid
                self._data = {}
                self.comments = Comments()
-               if post_data:
-                       self._data = post_data
-
-               if fetch: self._fetchdata()
+               if post_data: self._setdata(post_data)
+               elif fetch: self._fetchdata()
                if comments:
                        if not self._data: self._fetchdata()
                        self._fetchcomments()
-               else:
-                       if not self._data: self._fetchdata()
-                       self.comments.set_json( self.data()['interactions']['comments'] )
 
        def __repr__(self):
                """Returns string containing more information then str().
@@ -513,6 +513,11 @@ class Post():
                """
                return self._data['text']
 
+       def _setdata(self, data):
+               self._data = data
+               if not bool(self.comments) and data['interactions'].get('comments', []):
+                       self.comments.set_json(data['interactions'].get('comments', []))
+
        def _fetchdata(self):
                """This function retrieves data of the post.
 
@@ -523,8 +528,7 @@ class Post():
                request = self._connection.get('posts/{0}.json'.format(id))
                if request.status_code != 200:
                        raise errors.PostError('{0}: could not fetch data for post: {1}'.format(request.status_code, id))
-               elif request:
-                       self._data = request.json()
+               elif request: self._setdata(request.json());
                return self.data()['guid']
 
        def _fetchcomments(self):
@@ -540,12 +544,33 @@ class Post():
                        else:
                                self.comments.set([Comment(c) for c in request.json()])
 
+       def _fetchlikes(self):
+               id = self.data()['id']
+               request = self._connection.get('posts/{0}/likes.json'.format(id))
+               if request.status_code != 200:
+                       raise errors.PostError('{0}: could not fetch likes for post: {1}'.format(request.status_code, id))
+               json = request.json();
+               if json: self._data['interactions']['likes'] = request.json();
+               return self._data['interactions']['likes'];
+
+       def _fetchreshares(self):
+               id = self.data()['id']
+               request = self._connection.get('posts/{0}/reshares.json'.format(id))
+               if request.status_code != 200:
+                       raise errors.PostError('{0}: could not fetch likes for post: {1}'.format(request.status_code, id))
+
+               json = request.json();
+               if json: self._data['interactions']['reshares'] = request.json();
+               return self._data['interactions']['reshares'];
+
+       def fetchlikes(self): return self._fetchlikes();
+       def fetchreshares(self): return self._fetchreshares();
+
        def fetch(self, comments = False):
                """Fetches post data.
                """
                self._fetchdata()
-               if comments:
-                       self._fetchcomments()
+               if comments: self._fetchcomments()
                return self
 
        def data(self, data = None):
@@ -561,7 +586,7 @@ class Post():
                """
                data = {'authenticity_token': repr(self._connection)}
 
-               request = self._connection.post('posts/{0}/likes'.format(self.id),    
+               request = self._connection.post('posts/{0}/likes'.format(self.id),      
                                                                                data=data,
                                                                                headers={'accept': 'application/json'})
 
@@ -571,7 +596,8 @@ class Post():
 
                likes_json = request.json()
                if likes_json:
-                       self._data['interactions']['likes'] = [likes_json]
+                       self._data['interactions']['likes'].insert(0, likes_json)
+                       self._data['interactions']['likes_count'] = str(int(self._data['interactions']['likes_count'])+1)
                return likes_json
 
        def reshare(self):
@@ -585,6 +611,11 @@ class Post():
                                                                                headers={'accept': 'application/json'})
                if request.status_code != 201:
                        raise Exception('{0}: Post could not be reshared'.format(request.status_code))
+
+               reshares_json = request.json()
+               if reshares_json:
+                       self._data['interactions']['reshares'].insert(0, reshares_json)
+                       self._data['interactions']['reshares_count'] = str(int(self._data['interactions']['reshares_count'])+1)
                return request.json()
 
        def comment(self, text):
@@ -625,9 +656,9 @@ class Post():
 
        def hide(self):
                """
-               ->    PUT /share_visibilities/42 HTTP/1.1
+               ->      PUT /share_visibilities/42 HTTP/1.1
                          post_id=123
-               <-    HTTP/1.1 200 OK
+               <-      HTTP/1.1 200 OK
                """
                headers = {'x-csrf-token': repr(self._connection)}
                params = {'post_id': json.dumps(self.id)}
@@ -638,9 +669,9 @@ class Post():
 
        def mute(self):
                """
-               ->    POST /blocks HTTP/1.1
+               ->      POST /blocks HTTP/1.1
                        {"block":{"person_id":123}}
-               <-    HTTP/1.1 204 No Content 
+               <-      HTTP/1.1 204 No Content 
                """
                headers = {'content-type':'application/json', 'x-csrf-token': repr(self._connection)}
                data = json.dumps({ 'block': { 'person_id' : self._data['author']['id'] } })
@@ -651,8 +682,8 @@ class Post():
 
        def subscribe(self):
                """
-               ->    POST /posts/123/participation HTTP/1.1
-               <-    HTTP/1.1 201 Created
+               ->      POST /posts/123/participation HTTP/1.1
+               <-      HTTP/1.1 201 Created
                """
                headers = {'x-csrf-token': repr(self._connection)}
                data = {}
@@ -664,9 +695,9 @@ class Post():
 
        def unsubscribe(self):
                """
-               ->    POST /posts/123/participation HTTP/1.1
+               ->      POST /posts/123/participation HTTP/1.1
                          _method=delete
-               <-    HTTP/1.1 200 OK
+               <-      HTTP/1.1 200 OK
                """
                headers = {'x-csrf-token': repr(self._connection)}
                data = { "_method": "delete" }
@@ -718,8 +749,32 @@ class Post():
                        raise errors.PostError('{0}: Like could not be removed.'
                                                                   .format(request.status_code))
 
+               self._data['interactions']['likes'].pop(0);
+               self._data['interactions']['likes_count'] = str(int(self._data['interactions']['likes_count'])-1)
+
        def author(self, key='name'):
                """Returns author of the post.
                :param key: all keys available in data['author']
                """
                return self._data['author'][key]
+
+class FollowedTag():
+       """This class represents a followed tag.
+       `diaspy.tagFollowings.TagFollowings()` uses it.
+       """
+       def __init__(self, connection, id, name, taggings_count):
+               self._connection = connection
+               self._id, self._name, self._taggings_count = id, name, taggings_count
+
+       def id(self): return self._id
+       def name(self): return self._name
+       def count(self): return self._taggings_count
+
+       def delete(self):
+               data = {'authenticity_token': repr(self._connection)}
+               request = self._connection.delete('tag_followings/{0}'.format(self._id),
+                                                                               data=data,
+                                                                               headers={'accept': 'application/json'})
+               if request.status_code != 204:
+                       raise errors.TagError('{0}: Tag could not be deleted.'
+                                                                  .format(request.status_code))
index 5a6a95a6a2b825480833614a3d5eb812fc8a20ea..c76946d06e925de66c5718e8483fd1a69dd89cbb 100644 (file)
@@ -63,6 +63,12 @@ class Generic():
                """
                return len(self._stream)
 
+       def __bool__(self):
+               """Returns True if stream os filled, False if not.
+               """
+               if self._stream: return True
+               return False
+
        def _obtain(self, max_time=0, suppress=True):
                """Obtains stream from pod.
 
@@ -78,14 +84,17 @@ class Generic():
                        params['_'] = self.latest
                request = self._connection.get(self._location, params=params)
                if request.status_code != 200:
-                       raise errors.StreamError('wrong status code: {0}'.format(request.status_code))
+                       raise errors.StreamError('wrong status code: {0}'
+                                                                       .format(request.status_code))
                posts = []
                latest_time = None # Used to get the created_at from the latest posts we received.
                for post in request.json():
                        try:
                                comments = False
                                if post['interactions']['comments_count'] > 3: comments = True
-                               posts.append(Post(self._connection, id=post['id'], guid=post['guid'], fetch=False, comments=comments, post_data=post))
+                               posts.append(Post(self._connection, id=post['id'],
+                                                       guid=post['guid'], fetch=False, comments=comments,
+                                                       post_data=post))
                                if post['created_at']: latest_time = post['created_at']
                        except errors.PostError:
                                if not suppress:
@@ -249,7 +258,9 @@ class Stream(Generic):
        """
        location = 'stream.json'
 
-       def post(self, text='', aspect_ids='public', photos=None, photo='', poll_question=None, poll_answers=None, location_coords=None, provider_display_name=''):
+       def post(self, text='', aspect_ids='public', photos=None, photo='',
+                       poll_question=None, poll_answers=None, location_coords=None,
+                       provider_display_name=''):
                """This function sends a post to an aspect.
                If both `photo` and `photos` are specified `photos` takes precedence.
 
@@ -281,7 +292,8 @@ class Stream(Generic):
                """
                data = {}
                data['aspect_ids'] = aspect_ids
-               data['status_message'] = {'text': text, 'provider_display_name': provider_display_name}
+               data['status_message'] = ({'text': text,
+                                                               'provider_display_name': provider_display_name})
                if photo: data['photos'] = self._photoupload(photo)
                if photos: data['photos'] = photos
                if poll_question and poll_answers:
@@ -290,14 +302,16 @@ class Stream(Generic):
                if location_coords: data['location_coords'] = location_coords
 
                request = self._connection.post('status_messages',
-                                                                               data=json.dumps(data),
-                                                                               headers={'content-type': 'application/json',
-                                                                                                'accept': 'application/json',
-                                                                                                'x-csrf-token': repr(self._connection)})
+                                                       data=json.dumps(data),
+                                                       headers={'content-type': 'application/json',
+                                                                        'accept': 'application/json',
+                                                                        'x-csrf-token': repr(self._connection)})
                if request.status_code != 201:
-                       raise Exception('{0}: Post could not be posted.'.format(request.status_code))
+                       raise Exception('{0}: Post could not be posted.'
+                                                       .format(request.status_code))
                post_json = request.json()
-               post = Post(self._connection, id=post_json['id'], guid=post_json['guid'], post_data=post_json)
+               post = Post(self._connection, id=post_json['id'],
+                                       guid=post_json['guid'], post_data=post_json)
                return post
 
        def _photoupload(self, filename, aspects=[]):
@@ -326,9 +340,11 @@ class Stream(Generic):
                                   'x-csrf-token': repr(self._connection),
                                   'x-file-name': filename}
 
-               request = self._connection.post('photos', data=image, params=params, headers=headers)
+               request = self._connection.post('photos', data=image, params=params,
+                                                                               headers=headers)
                if request.status_code != 200:
-                       raise errors.StreamError('photo cannot be uploaded: {0}'.format(request.status_code))
+                       raise errors.StreamError('photo cannot be uploaded: {0}'
+                                                                       .format(request.status_code))
                return request.json()['data']['photo']['id']
 
 
@@ -392,7 +408,8 @@ class Aspects(Generic):
                :parameter ids: list of apsect ids
                :type ids: list of integers
                """
-               self._location = 'aspects.json?a_ids[]=' + '{}'.format('&a_ids[]='.join(ids))
+               self._location = 'aspects.json?a_ids[]=' + '{}'.format(
+                                               '&a_ids[]='.join(str(id) for id in ids))
                self.fill() # this will create entirely new list of posts.
 
        def add(self, aspect_name, visible=0):
@@ -411,7 +428,8 @@ class Aspects(Generic):
 
                request = self._connection.post('aspects', data=data)
                if request.status_code not in [200, 422]:
-                       raise Exception('wrong status code: {0}'.format(request.status_code))
+                       raise Exception('wrong status code: {0}'
+                                                       .format(request.status_code))
 
                id = self.getAspectID(aspect_name)
                return Aspect(self._connection, id)
@@ -434,7 +452,8 @@ class Aspects(Generic):
                                'authenticity_token': repr(self._connection)}
                request = self._connection.post('aspects/{0}'.format(id), data=data)
                if request.status_code not in [200, 302, 500]:
-                       raise Exception('wrong status code: {0}: cannot remove aspect'.format(request.status_code))
+                       raise Exception('wrong status code: {0}: cannot remove aspect'
+                                                       .format(request.status_code))
 
 
 class Commented(Generic):
@@ -457,27 +476,34 @@ class Mentions(Generic):
        _location = 'mentions.json'
 
 
+class Public(Generic):
+       """Public stream.
+       """
+       _location = 'public.json'
+
+
 class FollowedTags(Generic):
        """This stream contains all posts
        containing tags the user is following.
        """
        _location = 'followed_tags.json'
 
-       def get(self):
-               """Returns list of followed tags.
-               """
-               return []
-
        def remove(self, tag_id):
                """Stop following a tag.
 
                :param tag_id: tag id
                :type tag_id: int
+
+               FIXME:
+                       Deprecated, this function will be removed in next version.
+                       Use diaspy.tagFollowings.TagFollowings[“tagName”].delete() instead.
                """
                data = {'authenticity_token': self._connection.get_token()}
-               request = self._connection.delete('tag_followings/{0}'.format(tag_id), data=data)
+               request = self._connection.delete('tag_followings/{0}'
+                                                                                       .format(tag_id), data=data)
                if request.status_code != 404:
-                       raise Exception('wrong status code: {0}'.format(request.status_code))
+                       raise Exception('wrong status code: {0}'
+                                                                                       .format(request.status_code))
 
        def add(self, tag_name):
                """Follow new tag.
@@ -487,6 +513,10 @@ class FollowedTags(Generic):
                :param tag_name: tag name
                :type tag_name: str
                :returns: int (response code)
+
+               FIXME:
+                       Deprecated, this function will be removed in next version.
+                       Use diaspy.tagFollowings.TagFollowings.follow() instead.
                """
                data = {'name': tag_name,
                                'authenticity_token': repr(self._connection),
@@ -496,7 +526,8 @@ class FollowedTags(Generic):
                                   'accept': 'application/json'
                                   }
 
-               request = self._connection.post('tag_followings', data=json.dumps(data), headers=headers)
+               request = self._connection.post('tag_followings', data=json.dumps(data),
+                                                                               headers=headers)
 
                if request.status_code not in [201, 403]:
                        raise Exception('wrong error code: {0}'.format(request.status_code))
@@ -513,6 +544,4 @@ class Tag(Generic):
                :param tag: tag name
                :type tag: str
                """
-               self._connection = connection
-               self._location = 'tags/{0}.json'.format(tag)
-               if fetch: self.fill()
+               super().__init__(connection, 'tags/{0}.json'.format(tag), fetch)
diff --git a/diaspy/tagFollowings.py b/diaspy/tagFollowings.py
new file mode 100644 (file)
index 0000000..65daa94
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+from diaspy.models import FollowedTag
+from diaspy import errors
+class TagFollowings():
+       """This class represents the tags followed by the user.
+
+       Should return `dict`s in a `list` with the following keys:
+               `id`, `name`, `taggings_count`
+       """
+       def __init__(self, connection, fetch=True):
+               self._connection = connection
+               self._tags = []
+               if fetch: self.fetch()
+
+       def __iter__(self): return iter(self._tags)
+
+       def __getitem__(self, t): return self._tags[t]
+
+       def _finalise(self, tags):
+               return([FollowedTag(self._connection, t['id'], t['name'],
+                                                       t['taggings_count']) for t in tags])
+
+       def fetch(self):
+               """(Re-)Fetches your followed tags.
+               """
+               self._tags = self.get()
+
+       def follow(self, name):
+               """Follows a tag by given name.
+
+               Returns FollowedTag object.
+               """
+               data = {'authenticity_token': repr(self._connection)}
+               params = {'name': name}
+               request = self._connection.post('tag_followings', data=data,
+                                               params=params, headers={'accept': 'application/json'})
+               if request.status_code != 201:
+                       raise errors.TagError('{0}: Tag could not be followed.'
+                                                       .format(request.status_code))
+               result = request.json()
+               self._tags.append(FollowedTag(self._connection, result['id'],
+                                                                       result['name'], result['taggings_count']))
+               return self._tags[(len(self._tags) - 1)]
+
+       def get(self):
+               request = self._connection.get('tag_followings.json')
+               if request.status_code != 200:
+                       raise Exception('status code: {0}: cannot retreive tag_followings'
+                                                       .format(request.status_code))
+               return self._finalise(request.json())
index 3e71fe474e5c8918155de094c5e20c652e5db89b..d2d1fe3ec012098d9883082de459b3cccc0fae53 100644 (file)
@@ -51,9 +51,9 @@ copyright = '2013, Moritz Kiefer, Marek Marecki and others'
 # built documents.
 #
 # The short X.Y version.
-version = '0.6.0'
+version = '0.6.1'
 # The full version, including alpha/beta/rc tags.
-release = '0.6.0'
+release = '0.6.1.dev'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
index 34f03ed6b5c825547f4d6c829e1d2dcb6d66d195..471d1fde2e1ec48204f96ef57bf7254d7243528a 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 
 setup(
     name='diaspy-api',
-    version='0.6.0',
+    version='0.6.1.dev',
     author='Marek Marecki',
     author_email='marekjm@ozro.pw',
     url='https://github.com/marekjm/diaspy',