From: Marek Marecki Date: Thu, 2 May 2013 07:53:00 +0000 (+0200) Subject: Further work being done on streams; all basic streams implemented X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=505fc964a37518556ae1962fc3675cc3be07c93e;p=diaspy.git Further work being done on streams; all basic streams implemented --- diff --git a/diaspy/client.py b/diaspy/client.py index 83829f6..8eaab44 100644 --- a/diaspy/client.py +++ b/diaspy/client.py @@ -121,7 +121,7 @@ class Client: :type aspect_id: str """ - data = {'authenticity_token': self.get_token(), + data = {'authenticity_token': self.connection.getToken(), 'aspect_id': aspect_id, 'person_id': user_id} @@ -134,15 +134,8 @@ class Client: def add_aspect(self, aspect_name, visible=0): """ This function adds a new aspect. """ - - data = {'authenticity_token': self.get_token(), - 'aspect[name]': aspect_name, - 'aspect[contacts_visible]': visible} - - r = self.connection.post('aspects', data=data) - - if r.status_code != 200: - raise Exception('wrong status code: {0}'.format(r.status_code)) + aspects = diaspy.streams.Aspects(self.connection) + aspects.add(aspect_name, visible) def remove_user_from_aspect(self, user_id, aspect_id): """ this function removes a user from an aspect. @@ -153,7 +146,7 @@ class Client: :type aspect_id: str """ - data = {'authenticity_token': self.get_token(), + data = {'authenticity_token': self.connection.getToken(), 'aspect_id': aspect_id, 'person_id': user_id} @@ -168,7 +161,7 @@ class Client: def remove_aspect(self, aspect_id): """ This function adds a new aspect. """ - data = {'authenticity_token': self.get_token()} + data = {'authenticity_token': self.connection.getToken()} r = self.connection.delete('aspects/{}'.format(aspect_id), data=data) diff --git a/diaspy/models.py b/diaspy/models.py index 8b71eb5..66624f3 100644 --- a/diaspy/models.py +++ b/diaspy/models.py @@ -1,9 +1,6 @@ #!/usr/bin/env python3 -import json - - class Post: """This class represents a post. @@ -24,11 +21,10 @@ class Post: def get_data(self): """This function retrieves data of the post. """ - r = self._connection.get('posts/{1}.json'.format(self.post_id)) - if r.status_code == 200: - return r.json() - else: + r = self._connection.get('posts/{0}.json'.format(self.post_id)) + if r.status_code != 200: raise Exception('wrong status code: {0}'.format(r.status_code)) + return r.json() def like(self): """This function likes a post. diff --git a/diaspy/streams.py b/diaspy/streams.py index 8800688..d0b4b25 100644 --- a/diaspy/streams.py +++ b/diaspy/streams.py @@ -1,18 +1,27 @@ import json from diaspy.models import Post +"""Docstrings for this module are taken from: +https://gist.github.com/MrZYX/01c93096c30dc44caf71 + +Documentation for D* JSON API taken from: +http://pad.spored.de/ro/r.qWmvhSZg7rk4OQam +""" class Generic: - """Object representing generic stream. Used in Tag(), + """Object representing generic stream. Used in Tag(), Stream(), Activity() etc. """ - def __init__(self, connection, location): + def __init__(self, connection, location=''): """ :param connection: Connection() object - :param type: diaspy.connection.Connection + :type connection: diaspy.connection.Connection + :param location: location of json + :type location: str """ self._connection = connection - self._location = location + self._setlocation() + if location: self._location = location self._stream = [] self.fill() @@ -38,6 +47,27 @@ class Generic: """ return len(self._stream) + def _setlocation(self): + """Sets location of the stream. + Location defaults to 'stream.json' + + NOTICE: inheriting objects should override this method + and set their own value to the `location`. + However, it is possible to override default location by + passing the desired one to the constructor. + For example: + + def _setlocation(self): + self._location = 'foo.json' + + + :param location: url of the stream + :type location: str + + :returns: str + """ + self._location = 'stream.json' + def _obtain(self): """Obtains stream from pod. """ @@ -51,15 +81,34 @@ class Generic: """ self._stream = [] + def purge(self): + """Removes all unexistent posts from stream. + """ + stream = [] + for post in self._stream: + deleted = False + try: + post.get_data() + stream.append(post) + except Exception: + deleted = True + finally: + if not deleted: stream.append(post) + self._stream = stream + def update(self): """Updates stream. """ - stream = self._obtain() - _stream = self._stream - for i in range(len(stream)): - if stream[-i] not in _stream: - _stream = [stream[-i]] + _stream - self._stream = _stream + new_stream = self._obtain() + ids = [post.post_id for post in self._stream] + + stream = self._stream + for i in range(len(new_stream)): + if new_stream[-i].post_id not in ids: + stream = [new_stream[-i]] + stream + ids.append(new_stream[-i].post_id) + + self._stream = stream def fill(self): """Fills the stream with posts. @@ -68,8 +117,13 @@ class Generic: class Stream(Generic): - """Object representing user's stream. + """The main stream containing the combined posts of the + followed users and tags and the community spotlights posts + if the user enabled those. """ + def _setlocation(self): + self._location = 'stream.json' + def post(self, text, aspect_ids='public', photos=None): """This function sends a post to an aspect @@ -123,4 +177,107 @@ class Stream(Generic): class Activity(Generic): """Stream representing user's activity. """ - pass + def _setlocation(self): + self._location = 'activity.json' + + def _delid(self, id): + """Deletes post with given id. + """ + post = None + for p in self._stream: + if p['id'] == id: + post = p + break + if post is not None: post.delete() + + def delete(self, post): + """Deletes post from users activity. + `post` can be either post id or Post() + object which will be identified and deleted. + After deleting post the stream will be filled. + + :param post: post identifier + :type post: str, diaspy.models.Post + """ + if type(post) == str: self._delid(post) + elif type(post) == Post: post.delete() + else: + raise TypeError('this method accepts only int, str or Post: {0} given') + self.fill() + + +class Aspects(Generic): + """This stream contains the posts filtered by + the specified aspect IDs. You can choose the aspect IDs with + the parameter `aspect_ids` which value should be + a comma seperated list of aspect IDs. + If the parameter is ommitted all aspects are assumed. + An example call would be `aspects.json?aspect_ids=23,5,42` + """ + def _setlocation(self): + self._location = 'aspects.json' + + def add(self, aspect_name, visible=0): + """This function adds a new aspect. + """ + data = {'authenticity_token': self._connection.getToken(), + 'aspect[name]': aspect_name, + 'aspect[contacts_visible]': visible} + + r = self._connection.post('aspects', data=data) + if r.status_code != 200: + raise Exception('wrong status code: {0}'.format(r.status_code)) + + +class Commented(Generic): + """This stream contains all posts + the user has made a comment on. + """ + def _setlocation(self): + self._location = 'commented.json' + + +class Liked(Generic): + """This stream contains all posts the user liked. + """ + def _setlocation(self): + self._location = 'liked.json' + + +class Mentions(Generic): + """This stream contains all posts + the user is mentioned in. + """ + def _setlocation(self): + self._location = 'mentions.json' + + +class FollowedTags(Generic): + """This stream contains all posts + containing a tag the user is following. + """ + def _setlocation(self): + self._location = 'followed_tags.json' + + def add(self, tag_name): + """Follow new tag. + """ + data = {'authenticity_token': self._connection.getToken(), + 'tag_name': tag_name} + r = self._connection.post('tags', data=data) + if r.status_code != 200: + raise Exception('wrong status code: {0}'.format(r.status_code)) + + def create(self, tag_name): + """Follow new tag. + + :param tag_name: tag name + :type tag_name: str + """ + data = {'tag_name':tag_name, + 'authenticity_token':self._connection.getToken(), + } + request = self._connection.post('followed_tags', data=data, headers=headers) + + if request.status_code != 200: + raise Exception('wrong error code: {0}'.format(request.status_code)) diff --git a/tests.py b/tests.py index b059333..8379924 100644 --- a/tests.py +++ b/tests.py @@ -16,40 +16,49 @@ __pod__ = testconf.__pod__ __username__ = testconf.__username__ __passwd__ = testconf.__passwd__ +__connection__ = diaspy.connection.Connection(pod=__pod__, username=__username__, password=__passwd__) +__connection__.login() + class StreamTest(unittest.TestCase): def testGetting(self): c = diaspy.connection.Connection(pod=__pod__, username=__username__, password=__passwd__) c.login() - stream = diaspy.models.Stream(c) + stream = diaspy.streams.Stream(c) stream.update() def testGettingLength(self): c = diaspy.connection.Connection(pod=__pod__, username=__username__, password=__passwd__) c.login() - stream = diaspy.models.Stream(c) + stream = diaspy.streams.Stream(c) stream.update() len(stream) def testClearing(self): - c = diaspy.connection.Connection(pod=__pod__, username=__username__, password=__passwd__) - c.login() - stream = diaspy.models.Stream(c) + stream = diaspy.streams.Stream(__connection__) stream.update() stream.clear() self.assertEqual(0, len(stream)) + def testPurging(self): + stream = diaspy.streams.Stream(__connection__) + post = stream.post('#diaspy test') + stream.update() + post.delete() + stream.purge() + self.assertNotIn(post.post_id, [post.post_id for post in stream]) + def testPostingText(self): c = diaspy.connection.Connection(pod=__pod__, username=__username__, password=__passwd__) c.login() - stream = diaspy.models.Stream(c) + stream = diaspy.streams.Stream(c) post = stream.post('#diaspy test') self.assertEqual(diaspy.models.Post, type(post)) def testPostingImage(self): c = diaspy.connection.Connection(pod=__pod__, username=__username__, password=__passwd__) c.login() - stream = diaspy.models.Stream(c) + stream = diaspy.streams.Stream(c) stream.post_picture('./test-image.png')