From 8bfe79c780885a202971110b0bfee15e9196fa22 Mon Sep 17 00:00:00 2001 From: CYBERDEViLNL Date: Thu, 6 Dec 2018 19:16:11 +0100 Subject: [PATCH] =?utf8?q?*=20=5F=5Fupd=5F=5F:=20=20`diaspy.models.Post.li?= =?utf8?q?ke()`,=20`diaspy.models.Post.delete=5Flike()`,=20`diaspy.models.?= =?utf8?q?Post.reshare()`=20will=20now=20update=20data=20(count=20and=20li?= =?utf8?q?kes/reshares)=20without=20doing=20another=20request.=20*=20=5F?= =?utf8?q?=5Ffix=5F=5F:=20=20`diaspy.models.Post.=5F=5Finit=5F=5F()`=20che?= =?utf8?q?cking=20on=20different=20fetch=20states=20was=20a=20mess.=20*=20?= =?utf8?q?=5F=5Ffix=5F=5F:=20=20`diaspy.streams.Asepcts.filter()`=20locati?= =?utf8?q?on=20fix.=20*=20=5F=5Fnew=5F=5F:=20=20`diaspy.tagFollowings.TagF?= =?utf8?q?ollowings()`=20which=20represents=20the=20tags=20followed=20by?= =?utf8?q?=20the=20user.=20*=20=5F=5Fnew=5F=5F:=20=20`diaspy.models.Follow?= =?utf8?q?edTag()`=20which=20represents=20a=20tag=20followed=20by=20the=20?= =?utf8?q?user.=20It=20is=20used=20by=20`diaspy.tagFollowings.TagFollowing?= =?utf8?q?s()`.=20*=20=5F=5Fnew=5F=5F:=20=20It=20is=20now=20possible=20to?= =?utf8?q?=20give=20`**requestKwargs`=20to=20`diaspy.connection.Connection?= =?utf8?q?()`=20which=20will=20be=20used=20for=20every=20`request`=20unles?= =?utf8?q?s=20directly=20overwritten=20by=20given=20the=20function=20you?= =?utf8?q?=20call=20different=20key-worded=20arguments.=20*=20=5F=5Fnew=5F?= =?utf8?q?=5F:=20=20`diaspy.connection.Connection()`=20now=20does=20check?= =?utf8?q?=20if=20the=20`pod`=20you=20are=20connecting=20to=20has=20`Camo`?= =?utf8?q?=20enabled=20or=20not.=20Call=20`diaspy.connection.Connection.ca?= =?utf8?q?mo()`=20to=20receive=20`True`=20or=20`False`.=20*=20=5F=5Fnew=5F?= =?utf8?q?=5F:=20=20`diaspy.models.Comment.authordata()`=20which=20will=20?= =?utf8?q?return=20all=20author=20data=20instead=20of=20`diaspy.models.Com?= =?utf8?q?ment.author()`=20which=20will=20only=20return=20data=20for=20a?= =?utf8?q?=20certain=20key.=20*=20=5F=5Fnew=5F=5F:=20=20`diaspy.streams.Pu?= =?utf8?q?blic()`=20*=20=5F=5Fnew=5F=5F:=20=20`diaspy.models.Post.fetchlik?= =?utf8?q?es()`.=20*=20=5F=5Fnew=5F=5F:=20=20`diaspy.models.Post.fetchresh?= =?utf8?q?ares()`=20*=20=5F=5Frem=5F=5F:=20=20`diaspy.streams.FollowedTags?= =?utf8?q?.get()`=20since=20it=20wasn=E2=80=99t=20doing=20anything=20usefu?= =?utf8?q?ll.=20*=20=5F=5Fdep=5F=5F:=20=20`diaspy.streams.FollowedTags.rem?= =?utf8?q?ove()`=20Use=20`diaspy.tagFollowings.TagFollowings[=E2=80=9CtagN?= =?utf8?q?ame=E2=80=9D].delete()`=20instead.=20*=20=5F=5Fdep=5F=5F:=20=20`?= =?utf8?q?diaspy.streams.FollowedTags.add()`=20Use=20diaspy.tagFollowings.?= =?utf8?q?TagFollowings.follow()=20instead.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- Changelog.markdown | 25 +++++++++++ diaspy/__init__.py | 3 +- diaspy/connection.py | 37 ++++++++++++++-- diaspy/models.py | 97 ++++++++++++++++++++++++++++++++--------- diaspy/streams.py | 81 +++++++++++++++++++++++----------- diaspy/tagFollowings.py | 50 +++++++++++++++++++++ docs/source/conf.py | 4 +- setup.py | 2 +- 8 files changed, 245 insertions(+), 54 deletions(-) create mode 100644 diaspy/tagFollowings.py diff --git a/Changelog.markdown b/Changelog.markdown index 8b11250..e7b0af1 100644 --- a/Changelog.markdown +++ b/Changelog.markdown @@ -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 diff --git a/diaspy/__init__.py b/diaspy/__init__.py index fee78fd..aa470b6 100644 --- a/diaspy/__init__.py +++ b/diaspy/__init__.py @@ -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' diff --git a/diaspy/connection.py b/diaspy/connection.py index 2001c45..24a6e95 100644 --- a/diaspy/connection.py +++ b/diaspy/connection.py @@ -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. diff --git a/diaspy/models.py b/diaspy/models.py index 9468ddc..d256c24 100644 --- a/diaspy/models.py +++ b/diaspy/models.py @@ -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)) diff --git a/diaspy/streams.py b/diaspy/streams.py index 5a6a95a..c76946d 100644 --- a/diaspy/streams.py +++ b/diaspy/streams.py @@ -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 index 0000000..65daa94 --- /dev/null +++ b/diaspy/tagFollowings.py @@ -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()) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3e71fe4..d2d1fe3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -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. diff --git a/setup.py b/setup.py index 34f03ed..471d1fd 100644 --- 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', -- 2.25.1