Stuff done
[diaspy.git] / diaspy / models.py
index 8bf71a93d3a3375017b5f76e920004c3f8a4da5e..72041a52416ae272ca02b228cd6323b5c975962a 100644 (file)
 
 
 import json
+import re
 
 
-class Post:
+"""This module is only imported in other diaspy modules and
+MUST NOT import anything.
+"""
+
+
+class Aspect():
+    """This class represents an aspect.
+    """
+    def __init__(self, connection, id=-1):
+        self._connection = connection
+        self.id = id
+        self.name = ''
+
+    def getUsers(self):
+        """Returns list of users who are listed in this aspect.
+        """
+        return []
+
+    def addUser(self, user_id):
+        """Add user to current aspect.
+
+        :param user_id: user to add to aspect
+        :type user: int
+        """
+        data = {'authenticity_token': self._connection.get_token(),
+                'aspect_id': self.id,
+                'person_id': user_id}
+
+        request = self._connection.post('aspect_memberships.json', data=data)
+
+        if request.status_code != 201:
+            raise Exception('wrong status code: {0}'.format(request.status_code))
+        return request.json()
+
+    def removeUser(self, user_id):
+        """Remove user from current aspect.
+
+        :param user_id: user to remove from aspect
+        :type user: int
+        """
+        data = {'authenticity_token': self._connection.get_token(),
+                'aspect_id': self.id,
+                'person_id': user_id}
+
+        request = self.connection.delete('aspect_memberships/{0}.json'.format(self.id), data=data)
+
+        if request.status_code != 200:
+            raise Exception('wrong status code: {0}'.format(request.status_code))
+        return request.json()
+
+
+class Notification():
+    """This class represents single notification.
+    """
+    _who_regexp = re.compile(r'/people/[0-9a-z]+" class=\'hovercardable')
+    _when_regexp = re.compile(r'[0-9]{4,4}(-[0-9]{2,2}){2,2} [0-9]{2,2}(:[0-9]{2,2}){2,2} UTC')
+
+    def __init__(self, connection, data):
+        self._connection = connection
+        self.type = list(data.keys())[0]
+        self.data = data[self.type]
+        self.id = self.data['id']
+        self.unread = self.data['unread']
+
+    def __getitem__(self, key):
+        """Returns a key from notification data.
+        """
+        return self.data[key]
+
+    def __str__(self):
+        """Returns notification note.
+        """
+        string = re.sub('</?[a-z]+( *[a-z_-]+=["\'][\w():.,!?#/\- ]*["\'])* */?>', '', self.data['note_html'])
+        string = string.strip().split('\n')[0]
+        while '  ' in string: string = string.replace('  ', ' ')
+        return string
+
+    def __repr__(self):
+        """Returns notification note with more details.
+        """
+        return '{0}: {1}'.format(self.when(), str(self))
+
+    def who(self):
+        """Returns list of guids of the users who caused you to get the notification.
+        """
+        return [who[8:24] for who in self._who_regexp.findall(self.data['note_html'])]
+
+    def when(self):
+        """Returns UTC time as found in note_html.
+        """
+        return self._when_regexp.search(self.data['note_html']).group(0)
+
+    def mark(self, unread=False):
+        """Marks notification to read/unread.
+        Marks notification to read if `unread` is False.
+        Marks notification to unread if `unread` is True.
+
+        :param unread: which state set for notification
+        :type unread: bool
+        """
+        headers = {'x-csrf-token': self._connection.get_token()}
+        params = {'set_unread': json.dumps(unread)}
+        self._connection.put('notifications/{0}'.format(self['id']), params=params, headers=headers)
+        self.data['unread'] = unread
+
+
+class Post():
     """This class represents a post.
 
     .. note::
         Remember that you need to have access to the post.
-        Remember that you also need to be logged in.
     """
     def __init__(self, post_id, connection):
         """
@@ -21,14 +127,24 @@ class Post:
         self._connection = connection
         self.post_id = post_id
 
+    def __repr__(self):
+        """Returns string containing more information then str().
+        """
+        data = self.get_data()
+        return '{0} ({1}): {2}'.format(data['author']['name'], data['author']['diaspora_id'], data['text'])
+
+    def __str__(self):
+        """Returns text of a post.
+        """
+        return self.get_data()['text']
+
     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.
@@ -36,7 +152,7 @@ class Post:
 
         :returns: dict -- json formatted like object.
         """
-        data = {'authenticity_token': self._connection.getToken()}
+        data = {'authenticity_token': self._connection.get_token()}
 
         r = self._connection.post('posts/{0}/likes'.format(self.post_id),
                                   data=data,
@@ -51,7 +167,7 @@ class Post:
     def delete_like(self):
         """This function removes a like from a post
         """
-        data = {'authenticity_token': self._connection.getToken()}
+        data = {'authenticity_token': self._connection.get_token()}
 
         post_data = self.get_data()
 
@@ -72,7 +188,7 @@ class Post:
         post_data = self.get_data()
 
         data = {'root_guid': post_data['guid'],
-                'authenticity_token': self._connection.getToken()}
+                'authenticity_token': self._connection.get_token()}
 
         r = self._connection.post('reshares',
                                   data=data,
@@ -89,10 +205,9 @@ class Post:
 
         :param text: text to comment.
         :type text: str
-
         """
         data = {'text': text,
-                'authenticity_token': self._connection.getToken()}
+                'authenticity_token': self._connection.get_token()}
 
         r = self._connection.post('posts/{0}/comments'.format(self.post_id),
                                   data=data,
@@ -109,9 +224,8 @@ class Post:
 
         :param comment_id: id of the comment to remove.
         :type comment_id: str
-
         """
-        data = {'authenticity_token': self._connection.getToken()}
+        data = {'authenticity_token': self._connection.get_token()}
 
         r = self._connection.delete('posts/{0}/comments/{1}'
                                     .format(self.post_id,
@@ -126,123 +240,9 @@ class Post:
     def delete(self):
         """ This function deletes this post
         """
-        data = {'authenticity_token': self._connection.getToken()}
+        data = {'authenticity_token': self._connection.get_token()}
         r = self._connection.delete('posts/{0}'.format(self.post_id),
                                     data=data,
                                     headers={'accept': 'application/json'})
         if r.status_code != 204:
             raise Exception('{0}: Post could not be deleted'.format(r.status_code))
-
-
-class Stream:
-    """Object representing user's stream.
-    """
-    def __init__(self, connection):
-        """
-        :param connection: Connection() object
-        :param type: diaspy.connection.Connection
-        """
-        self._connection = connection
-        self._stream = []
-        self.fill()
-
-    def __contains__(self, post):
-        """Returns True if stream contains given post.
-        """
-        if type(post) is not Post:
-            raise TypeError('stream can contain only posts: checked for {0}'.format(type(post)))
-        return post in self._stream
-
-    def __iter__(self):
-        """Provides iterable interface for stream.
-        """
-        return iter(self._stream)
-
-    def __getitem__(self, n):
-        """Returns n-th item in Stream.
-        """
-        return self._stream[n]
-
-    def __len__(self):
-        """Returns length of the Stream.
-        """
-        return len(self._stream)
-
-    def _obtain(self):
-        """Obtains stream from pod.
-        """
-        request = self._connection.get('stream.json')
-        if request.status_code != 200:
-            raise Exception('wrong status code: {0}'.format(request.status_code))
-        return [Post(str(post['id']), self._connection) for post in request.json()]
-
-    def clear(self):
-        """Removes all posts from stream.
-        """
-        self._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
-
-    def fill(self):
-        """Fills the stream with posts.
-        """
-        self._stream = self._obtain()
-
-    def post(self, text, aspect_ids='public', photos=None):
-        """This function sends a post to an aspect
-
-        :param text: Text to post.
-        :type text: str
-        :param aspect_ids: Aspect ids to send post to.
-        :type aspect_ids: str
-
-        :returns: diaspy.models.Post -- the Post which has been created
-        """
-        data = {}
-        data['aspect_ids'] = aspect_ids
-        data['status_message'] = {'text': text}
-        if photos: data['photos'] = photos
-        request = self._connection.post('status_messages',
-                                        data=json.dumps(data),
-                                        headers={'content-type': 'application/json',
-                                                 'accept': 'application/json',
-                                                 'x-csrf-token': self._connection.getToken()})
-        if request.status_code != 201:
-            raise Exception('{0}: Post could not be posted.'.format(
-                            request.status_code))
-
-        post = Post(str(request.json()['id']), self._connection)
-        self.update()
-        return post
-
-    def post_picture(self, filename):
-        """This method posts a picture to D*.
-
-        :param filename: Path to picture file.
-        :type filename: str
-        """
-        aspects = self._connection.getUserInfo()['aspects']
-        params = {}
-        params['photo[pending]'] = 'true'
-        params['set_profile_image'] = ''
-        params['qqfile'] = filename
-        for i, aspect in enumerate(aspects):
-            params['photo[aspect_ids][%d]' % (i)] = aspect['id']
-
-        data = open(filename, 'rb')
-
-        headers = {'content-type': 'application/octet-stream',
-                   'x-csrf-token': self._connection.getToken(),
-                   'x-file-name': filename}
-        request = self._connection.post('photos', params=params, data=data, headers=headers)
-        data.close()
-        self.update()
-        return request