Beispiel #1
0
def check_token(tid, preserve=False):
    """Look up token with tid and return the data which was stored or None.

    """
    # Stop malformed tokens making calls to Redis
    if not TOKEN_RE.match(tid):
        return None

    # Get the pickled data from Redis
    data = r.get(K.TOKEN.format(tid))

    if data:
        try:
            # Attempt to get the pickled object back
            data = jsonpickle.decode(data)
            result = data
        except (TypeError, ValueError):
            # There was a problem pulling the data out of Redis.
            result = None
        finally:
            if not preserve:
                # Delete the token if preserve is False
                r.delete(K.TOKEN.format(tid))
        return result

    # If we didn't get data in the first place no need to delete.
    return None
Beispiel #2
0
def check_token(tid, preserve=False):
    """Look up token with tid and return the data which was stored or None.

    """
    # Stop malformed tokens making calls to Redis
    if not TOKEN_RE.match(tid):
        return None

    # Get the pickled data from Redis
    data = r.get(K.TOKEN.format(tid))

    if data:
        try:
            # Attempt to get the pickled object back
            data = jsonpickle.decode(data)
            return data
        except (TypeError, ValueError):
            # There was a problem pulling the data out of Redis.
            return None
        finally:
            # What the actual f**k coverage? It says this is a partial yet
            # there is a specific test for this. Must be something to do with
            # the finally block. The else is POINTLESS but works.
            if not preserve:
                # Delete the token if preserve is False
                r.delete(K.TOKEN.format(tid))
            else:
                pass

    # If we didn't get data in the first place no need to delete.
    return None
Beispiel #3
0
def delete_post(post_id):
    """Deletes a post

    """
    post = get_post(post_id)

    # In some situations a post may be in a cursor (deleting account) but have
    # already been deleted by this function in a previous run.
    if post is not None:
        # Delete votes and subscribers from Redis
        r.delete(k.POST_VOTES.format(post.get('_id')))

        # Delete the post from MongoDB
        m.db.posts.remove({'_id': post_id})

        if 'upload' in post:
            # If there is an upload, delete it!
            delete_upload(post['upload'])

        if 'reply_to' in post:
            m.db.posts.update({'_id': post['reply_to']},
                              {'$inc': {'comment_count': -1}})
        else:
            # Trigger deletion all posts comments if this post isn't a reply
            r.delete(k.POST_SUBSCRIBERS.format(post.get('_id')))
            delete_post_replies(post_id)
Beispiel #4
0
def check_token(tid, preserve=False):
    """Look up token with tid and return the data which was stored or None.

    """
    # Stop malformed tokens making calls to Redis
    if not TOKEN_RE.match(tid):
        return None

    # Get the pickled data from Redis
    data = r.get(K.TOKEN.format(tid))

    if data:
        try:
            # Attempt to get the pickled object back
            data = jsonpickle.decode(data)
            return data
        except (TypeError, ValueError):
            # There was a problem pulling the data out of Redis.
            return None
        finally:
            # What the actual f**k coverage? It says this is a partial yet
            # there is a specific test for this. Must be something to do with
            # the finally block. The else is POINTLESS but works.
            if not preserve:
                # Delete the token if preserve is False
                r.delete(K.TOKEN.format(tid))
            else:
                pass

    # If we didn't get data in the first place no need to delete.
    return None
Beispiel #5
0
def get_alerts(user_id, page=1, per_page=None):
    """Return a list of alert objects as a pagination.

    """
    if per_page is None:
        per_page = app.config.get('ALERT_ITEMS_PER_PAGE')

    # Get the last time the users checked the alerts
    # Try and cast the value to an int so we can boolean compare them
    try:
        alerts_last_checked = m.db.users.find_one({
            '_id': user_id
        }).get('alerts_last_checked')
    except (AttributeError, TypeError, ValueError):
        alerts_last_checked = 0

    # Get total number of elements in the sorted set
    total = r.zcard(k.USER_ALERTS.format(user_id))
    aids = r.zrevrange(k.USER_ALERTS.format(user_id), (page - 1) * per_page,
                       (page * per_page) - 1)

    # Create AlertManager to load the alerts
    am = AlertManager()

    alerts = []

    for aid in aids:
        # Load the alert in to the alert manager
        alert = am.get(aid)
        if alert:
            # Check to see if the alert is newer than the time we last checked.
            # This allows us to highlight in the template
            # This will assign a new property to the object: `new`
            if int(alert.timestamp) > alerts_last_checked:
                alert.new = True

            # Add the entire alert from the manager on the list
            alerts.append(alert)
        else:
            # Self cleaning zset
            r.zrem(k.USER_ALERTS.format(user_id), aid)
            total = r.zcard(k.USER_ALERTS.format(user_id))
            # May as well delete the alert if there is one
            r.delete(k.ALERT.format(aid))

    # Update the last time the user checked there alerts
    # This will allow us to alert a user too new alerts with the /i-has-alerts
    # url
    m.db.users.update({'_id': user_id},
                      {'$set': {
                          'alerts_last_checked': timestamp()
                      }})

    return Pagination(alerts, total, page, per_page)
Beispiel #6
0
def get_alerts(user_id, page=1, per_page=None):
    """Return a list of alert objects as a pagination.

    """
    if per_page is None:
        per_page = app.config.get('ALERT_ITEMS_PER_PAGE')

    # Get the last time the users checked the alerts
    # Try and cast the value to an int so we can boolean compare them
    try:
        alerts_last_checked = m.db.users.find_one(
            {'_id': user_id}
        ).get('alerts_last_checked')
    except (AttributeError, TypeError, ValueError):
        alerts_last_checked = 0

    # Get total number of elements in the sorted set
    total = r.zcard(k.USER_ALERTS.format(user_id))
    aids = r.zrevrange(k.USER_ALERTS.format(user_id), (page - 1) * per_page,
                       (page * per_page) - 1)

    # Create AlertManager to load the alerts
    am = AlertManager()

    alerts = []

    for aid in aids:
        # Load the alert in to the alert manager
        alert = am.get(aid)
        if alert:
            # Check to see if the alert is newer than the time we last checked.
            # This allows us to highlight in the template
            # This will assign a new property to the object: `new`
            if int(alert.timestamp) > alerts_last_checked:
                alert.new = True

            # Add the entire alert from the manager on the list
            alerts.append(alert)
        else:
            # Self cleaning zset
            r.zrem(k.USER_ALERTS.format(user_id), aid)
            total = r.zcard(k.USER_ALERTS.format(user_id))
            # May as well delete the alert if there is one
            r.delete(k.ALERT.format(aid))

    # Update the last time the user checked there alerts
    # This will allow us to alert a user too new alerts with the /i-has-alerts
    # url
    m.db.users.update({'_id': user_id},
                      {'$set': {'alerts_last_checked': timestamp()}})

    return Pagination(alerts, total, page, per_page)
Beispiel #7
0
def delete_post_comments(pid):
    """Delete ALL comments on post with pid

    """
    # Get a list of all cids on the post
    cids = r.lrange(K.POST_COMMENTS.format(pid), 0, -1)

    for cid in cids:
        # Make a call to delete comment
        delete_comment(cid)

    # Finally delete the comment list. It should already be gone as there will
    # be nothing left within it. We are just being careful.
    r.delete(K.POST_COMMENTS.format(pid))
Beispiel #8
0
def delete_comment(cid):
    """Delete a comment.

    """
    # Get the comment authors uid and the parents post pid
    [uid, pid] = r.hmget(K.COMMENT.format(cid), 'uid', 'pid')

    # Delete comment and votes
    r.delete(K.COMMENT.format(cid))
    r.delete(K.COMMENT_VOTES.format(cid))

    # Delete the comment from the users comment list
    r.lrem(K.USER_COMMENTS.format(uid), 0, cid)
    # Delete the comment from the posts comment list
    r.lrem(K.POST_COMMENTS.format(pid), 0, cid)
Beispiel #9
0
    def test_get_comments(self):
        """
        Ensure a posts comments are stored correctly in post:$pid:comments list
        """
        # Create two test users
        user1 = create_user('user1', '*****@*****.**', 'Password')
        user2 = create_user('user2', '*****@*****.**', 'Password')
        # Ensure the comment lists are empty
        self.assertEqual(len(get_comments(user1).items), 0)
        self.assertEqual(len(get_comments(user2).items), 0)
        # Create a post for each user and a comment on each for both user
        post1 = create_post(user1, 'Test post')
        post2 = create_post(user2, 'Test post')
        comment1 = create_comment(user1, post1, 'Test comment')
        comment2 = create_comment(user1, post2, 'Test comment')
        comment3 = create_comment(user2, post1, 'Test comment')
        comment4 = create_comment(user2, post2, 'Test comment')
        # Ensure each comment appears in each users list
        self.assertEqual(len(get_comments(post1).items), 2)
        self.assertEqual(len(get_comments(post2).items), 2)
        # Ensure the totals are correct
        self.assertEqual(get_comments(post1).total, 2)
        self.assertEqual(get_comments(post2).total, 2)
        # Ensure the ids are in the Redis lists
        self.assertIn(comment1, r.lrange(K.POST_COMMENTS.format(post1), 0, -1))
        self.assertIn(comment2, r.lrange(K.POST_COMMENTS.format(post2), 0, -1))
        self.assertIn(comment3, r.lrange(K.POST_COMMENTS.format(post1), 0, -1))
        self.assertIn(comment4, r.lrange(K.POST_COMMENTS.format(post2), 0, -1))

        # Delete 1 comment from post1 and ensure it does not exist
        delete_comment(comment1)
        # Check that is has gone
        self.assertEqual(len(get_comments(post1).items), 1)
        self.assertEqual(get_comments(post1).total, 1)
        # Ensure it is missing from Redis
        self.assertNotIn(comment1,
                         r.lrange(K.POST_COMMENTS.format(user1), 0, -1))

        # Delete a comment from inside Redis. This will trigger the self
        # cleaning list feature. We call these orphaned cids.
        r.delete(K.COMMENT.format(comment2))
        # Check that it has gone when get_comments is called
        self.assertEqual(len(get_comments(post2).items), 1)
        self.assertEqual(get_comments(post2).total, 1)
        # Ensure it is missing from Redis
        self.assertNotIn(comment2,
                         r.lrange(K.POST_COMMENTS.format(user1), 0, -1))
Beispiel #10
0
    def test_get_posts(self):
        """
        Test users post list works correctly
        """
        # Create test user
        user1 = create_user('user1', '*****@*****.**', 'Password')
        # Ensure the users post list is empty
        self.assertEqual(len(get_posts(user1).items), 0)

        # Create a few test posts, ensure they appears in the users list
        post1 = create_post(user1, 'Test post 1')
        post2 = create_post(user1, 'Test post 2')
        post3 = create_post(user1, 'Test post 3')
        self.assertEqual(len(get_posts(user1).items), 3)
        self.assertEqual(get_posts(user1).total, 3)

        # Ensure the post ids are in the Redis list
        self.assertIn(post1, r.lrange(K.USER_POSTS.format(user1), 0, -1))
        self.assertIn(post2, r.lrange(K.USER_POSTS.format(user1), 0, -1))
        self.assertIn(post3, r.lrange(K.USER_POSTS.format(user1), 0, -1))

        # Delete one of the posts and ensure that it does not appear in the
        # list.
        delete_post(post1)

        # Ensure the above is now correct with post1 missing
        self.assertEqual(len(get_posts(user1).items), 2)
        self.assertEqual(get_posts(user1).total, 2)

        # Ensure the post ids are in the Redis list and post1 is NOT
        self.assertNotIn(post1, r.lrange(K.USER_POSTS.format(user1), 0, -1))
        self.assertIn(post2, r.lrange(K.USER_POSTS.format(user1), 0, -1))
        self.assertIn(post3, r.lrange(K.USER_POSTS.format(user1), 0, -1))

        # Delete a post from inside Redis. This will trigger the self cleaning
        # list feature. We call these orphaned pids
        r.delete(K.POST.format(post2))
        # Ensure the above is now correct with post2 missing
        self.assertEqual(len(get_posts(user1).items), 1)
        self.assertEqual(get_posts(user1).total, 1)

        # Ensure the post ids are not in the Redis list and post1 is NOT
        self.assertNotIn(post1, r.lrange(K.USER_POSTS.format(user1), 0, -1))
        self.assertNotIn(post2, r.lrange(K.USER_POSTS.format(user1), 0, -1))
        self.assertIn(post3, r.lrange(K.USER_POSTS.format(user1), 0, -1))
Beispiel #11
0
def delete_account(user_id):
    """Will delete a users account.

    This **REMOVES ALL** details, posts, replies, etc. Not votes though.

    .. note: Ensure the user has authenticated this request. This is going to
             be the most *expensive* task in Pjuu, be warned.

    :param user_id: The `user_id` of the user to delete
    :type user_id: str

    """
    # Delete the user from MongoDB
    m.db.users.remove({"_id": user_id})

    # Remove all posts a user has ever made. This includes all votes
    # on the posts and all comments of the posts.
    # This calls the backend function from posts to do the deed
    posts_cursor = m.db.posts.find({"user_id": user_id}, {})
    for post in posts_cursor:
        delete_post(post.get("_id"))

    # Remove all the following relationships from Redis

    # Delete all references to followers of the user.
    # This will remove the user from the other users following list

    # TODO Replace with ZSCAN
    follower_cursor = r.zrange(k.USER_FOLLOWERS.format(user_id), 0, -1)

    for follower_id in follower_cursor:
        # Clear the followers following list of the uid
        r.zrem(k.USER_FOLLOWING.format(follower_id), user_id)
    # Delete the followers list
    r.delete(k.USER_FOLLOWERS.format(user_id))

    # Delete all references to the users the user is following
    # This will remove the user from the others users followers list

    # TODO Replace with ZSCAN
    followee_cursor = r.zrange(k.USER_FOLLOWING.format(user_id), 0, -1)

    for followee_id in followee_cursor:
        # Clear the followers list of people uid is following
        r.zrem(k.USER_FOLLOWERS.format(followee_id), user_id)
    # Delete the following list
    r.delete(k.USER_FOLLOWING.format(user_id))

    # Delete the users feed, this may have been added too during this process.
    # Probably not but let's be on the safe side
    r.delete(k.USER_FEED.format(user_id))

    # Delete the users alert list
    # DO NOT DELETE ANY ALERTS AS THESE ARE GENERIC
    r.delete(k.USER_ALERTS.format(user_id))
Beispiel #12
0
    def get(self, aid):
        """Attempts to load an Alert from Redis and unpickle it.

        """
        # Try the unpickling process
        try:
            pickled_alert = r.get(k.ALERT.format(aid))
            alert = jsonpickle.decode(pickled_alert)
        except (TypeError, ValueError):
            # We failed to get an alert for whateva reason
            return None

        # Ensure we got an alert and that it verifies.
        if alert.verify():
            # Return the alert object
            return alert
        else:
            # If the alert did not verify delete it
            # This will stop this always being called
            r.delete(k.ALERT.format(aid))
            return None
Beispiel #13
0
    def get(self, aid):
        """Attempts to load an Alert from Redis and unpickle it.

        """
        # Try the unpickling process
        try:
            pickled_alert = r.get(k.ALERT.format(aid))
            alert = jsonpickle.decode(pickled_alert)
        except (TypeError, ValueError):
            # We failed to get an alert for whateva reason
            return None

        # Ensure we got an alert and that it verifies.
        if alert.verify():
            # Return the alert object
            return alert
        else:
            # If the alert did not verify delete it
            # This will stop this always being called
            r.delete(k.ALERT.format(aid))
            return None
Beispiel #14
0
def delete_post_replies(post_id):
    """Delete ALL comments on post with pid.

    This can't be done in one single call to Mongo because we need to remove
    the votes from Redis!

    """
    # Get a cursor for all the posts comments
    cur = m.db.posts.find({'reply_to': post_id})

    # Iterate over the cursor and delete each one
    for reply in cur:
        reply_id = reply.get('_id')

        # Delete the comment itself from MongoDB
        m.db.posts.remove({'_id': reply_id})

        # Remove any uploaded files
        if 'upload' in reply:
            delete_upload(reply['upload'])

        # Delete votes from Redis
        r.delete(k.POST_VOTES.format(reply_id))
Beispiel #15
0
def delete_post(pid):
    """Deletes a post

    """
    # Get the post authors uid
    uid = r.hget(K.POST.format(pid), 'uid')

    # Delete post, votes and subscribers
    r.delete(K.POST.format(pid))
    r.delete(K.POST_VOTES.format(pid))
    r.delete(K.POST_SUBSCRIBERS.format(pid))

    # Delete the post from the users post list
    r.lrem(K.USER_POSTS.format(uid), 0, pid)

    # Trigger deletion all posts comments
    delete_post_comments(pid)
Beispiel #16
0
def delete_account(uid):
    """Will delete a users account.

    This _MUST_ _REMOVE_ _ALL_ details, comments, posts, etc.

    Note: Ensure the user has authenticated this request.
          This is going to be the most _expensive_ task in Pjuu, be warned.

    """
    # Get some information from the hashes to delete lookup keys
    username = r.hget(K.USER.format(uid), 'username')
    email = r.hget(K.USER.format(uid), 'email')

    # Clear the users lookup keys and user account. These are not needed
    pipe = r.pipeline()
    # Delete lookup keys. This will stop the user being found or logging in
    pipe.set(K.UID_USERNAME.format(username), K.NIL_VALUE)
    pipe.expire(K.UID_USERNAME.format(username), K.EXPIRE_SECONDS)
    pipe.set(K.UID_EMAIL.format(email), K.NIL_VALUE)
    pipe.expire(K.UID_EMAIL.format(email), K.EXPIRE_SECONDS)

    # Delete user account
    pipe.delete(K.USER.format(uid))
    pipe.execute()

    # Remove all posts a user has ever made. This includes all votes
    # on that post and all comments.
    pids = r.lrange(K.USER_POSTS.format(uid), 0, -1)
    for pid in pids:
        # Delete post
        r.delete(K.POST.format(pid))
        # Delete all the votes made on the post
        r.delete(K.POST_VOTES.format(pid))
        # Delete posts subscribers list
        r.delete(K.POST_SUBSCRIBERS.format(pid))

        cids = r.lrange(K.POST_COMMENTS.format(pid), 0, -1)
        for cid in cids:
            # Get author, ensure uid is an int
            cid_author = r.hget(K.COMMENT.format(cid), 'uid')
            # Delete comment
            r.delete(K.COMMENT.format(cid))
            # Delete comment votes
            r.delete(K.COMMENT_VOTES.format(cid))
            # Remove the cid from users comment list
            # This may remove some of ours. This will just make deleting
            # a bit quicker
            r.lrem(K.USER_COMMENTS.format(cid_author), 0, cid)
        # Delete the comments list
        r.delete(K.POST_COMMENTS.format(pid))
    # Delete the users post list
    r.delete(K.USER_POSTS.format(uid))

    # Delete all comments the user has every made. Including all votes on
    # those comments
    # This is a stripped down version of above for post comments.
    # We are not going to clean the lists related to the posts, they will
    # self clean. We also do not need to clear the comments from the users
    # comments list as it will be getting deleted straight after

    cids = r.lrange(K.USER_COMMENTS.format(uid), 0, -1)
    for cid in cids:
        # Get author, ensure uid is an int
        cid_author = r.hget(K.COMMENT.format(cid), 'uid')
        # Delete comment
        r.delete(K.COMMENT.format(cid))
        # Delete comment votes
        r.delete(K.COMMENT_VOTES.format(cid))
    # Delete the comments list
    r.delete(K.USER_COMMENTS.format(uid))

    # Delete all references to followers of the the user.
    # This will remove the user from the other users following list

    fids = r.zrange(K.USER_FOLLOWERS.format(uid), 0, -1)

    for fid in fids:
        # Clear the followers following list of the uid
        r.zrem(K.USER_FOLLOWING.format(fid), uid)
    # Delete the followers list
    r.delete(K.USER_FOLLOWERS.format(uid))

    # Delete all references to the users the user is following
    # This will remove the user from the others users followers list

    fids = r.zrange(K.USER_FOLLOWING.format(uid), 0, -1)

    for fid in fids:
        # Clear the followers list of people uid is following
        r.zrem(K.USER_FOLLOWERS.format(fid), uid)
    # Delete the following list
    r.delete(K.USER_FOLLOWING.format(uid))

    # Finally delete the users feed, this may have been added too during this
    # process. Probably not but let's be on the safe side
    r.delete(K.USER_FEED.format(uid))

    # Delete the users alert list
    # DO NOT DELETE ANY ALERTS AS THESE ARE GENERIC
    r.delete(K.USER_ALERTS.format(uid))
Beispiel #17
0
    def test_follow_unfollow_get_followers_following_is_following(self):
        """
        Test everything about following. There is not that much to it to
        deserve 3 seperate methods.
        """
        # Create two test users
        user1 = create_user('user1', '*****@*****.**', 'Password')
        user2 = create_user('user2', '*****@*****.**', 'Password')
        # Ensure is_following() is false atm
        self.assertFalse(is_following(user1, user2))
        self.assertFalse(is_following(user2, user1))
        # Ensure user 1 can follow user 2
        self.assertTrue(follow_user(user1, user2))
        # Ensure the user can't follow them again
        self.assertFalse(follow_user(user1, user2))
        # And visa-versa
        self.assertTrue(follow_user(user2, user1))
        # Ensre the user can't follow them again
        self.assertFalse(follow_user(user2, user1))
        # Ensure the id's are in the Redis sorted sets, followers and following
        self.assertIn(user2, r.zrange(K.USER_FOLLOWING.format(user1), 0, -1))
        self.assertIn(user2, r.zrange(K.USER_FOLLOWERS.format(user1), 0, -1))
        self.assertIn(user1, r.zrange(K.USER_FOLLOWING.format(user2), 0, -1))
        self.assertIn(user1, r.zrange(K.USER_FOLLOWERS.format(user2), 0, -1))
        # Ensure the get_followers and get_following functions return
        # the correct data
        self.assertEqual(len(get_following(user1).items), 1)
        self.assertEqual(len(get_following(user1).items), 1)
        self.assertEqual(len(get_following(user2).items), 1)
        self.assertEqual(len(get_following(user2).items), 1)
        # Ensure the totals are correct
        self.assertEqual(get_following(user1).total, 1)
        self.assertEqual(get_followers(user1).total, 1)
        self.assertEqual(get_following(user2).total, 1)
        self.assertEqual(get_followers(user2).total, 1)

        # Make sure is_following() returns correctly
        self.assertTrue(is_following(user1, user2))
        self.assertTrue(is_following(user2, user1))

        # User 1 unfollow user 2 and ensure the sorted sets are updated
        self.assertTrue(unfollow_user(user1, user2))
        self.assertNotIn(user2,
                         r.zrange(K.USER_FOLLOWING.format(user1), 0, -1))
        self.assertNotIn(user1,
                         r.zrange(K.USER_FOLLOWERS.format(user2), 0, -1))

        # Ensure the user can't unfollow the user again
        self.assertFalse(unfollow_user(user1, user2))
        # Ensure is_following shows correctly
        self.assertFalse(is_following(user1, user2))

        # Test what happens when we delete an account.

        # Ensure user 2 is still following user1
        self.assertTrue(is_following(user2, user1))

        # This should clean
        delete_account(user1)

        # Ensure this has cleaned user2 following list
        self.assertFalse(is_following(user2, user1))

        # Ensure get_followers and get_following return the correct value for
        # user2
        self.assertEqual(len(get_following(user2).items), 0)
        # Ensure the totals are correct
        self.assertEqual(get_following(user2).total, 0)
        self.assertEqual(get_followers(user2).total, 0)
        # Make sure is_following() returns correctly
        self.assertFalse(is_following(user1, user2))
        self.assertFalse(is_following(user2, user1))

        # I don't want to play with the above testing to much. I am adding
        # in a test for self cleaning lists. I am going to reset this test
        # case by manually flushing the Redis database :)
        r.flushdb()
        # Back to normal, I don't like artificially uping the number of tests.

        # Test the self cleaning lists in case there is an issue with Redis
        # during an account deletion. We need 2 new users.
        user1 = create_user('user1', '*****@*****.**', 'Password')
        user2 = create_user('user2', '*****@*****.**', 'Password')

        # Follow each other.
        self.assertTrue(follow_user(user1, user2))
        self.assertTrue(follow_user(user2, user1))

        # Manually delete user1's hash inside Redis
        r.delete(K.USER.format(user1))

        # Ensure user1 appears in both user2's followers and following lists
        self.assertIn(user1, r.zrange(K.USER_FOLLOWERS.format(user2), 0, -1))
        self.assertIn(user1, r.zrange(K.USER_FOLLOWING.format(user2), 0, -1))

        # Ensure if we actuallt get the lists from the backend functions user1
        # is not there
        self.assertEqual(get_followers(user2).total, 0)
        self.assertEqual(get_following(user2).total, 0)
Beispiel #18
0
    def test_alerts(self):
        """
        Tests for the 2 functions which are used on the side to get alerts and
        also test FollowAlert from here.
        """
        # Create 2 test users
        user1 = create_account('user1', '*****@*****.**', 'Password')
        user2 = create_account('user2', '*****@*****.**', 'Password')

        # Ensure that get_alerts pagination object is empty
        self.assertEqual(get_alerts(user1).total, 0)
        self.assertEqual(len(get_alerts(user1).items), 0)

        # Get user 2 to follow user 1
        follow_user(user2, user1)

        # Check that i_has_alerts is True
        self.assertTrue(new_alerts(user1))

        # Ensure that there is an alert in the get_alerts
        self.assertEqual(get_alerts(user1).total, 1)
        self.assertEqual(len(get_alerts(user1).items), 1)

        # Check that i_has_alerts is False, we have read them with get_alerts
        self.assertFalse(new_alerts(user1))

        # Get the alert and check that the alert is the follow alert
        alert = get_alerts(user1).items[0]
        self.assertTrue(isinstance(alert, FollowAlert))
        # Also check it's still a BaseAlert
        self.assertTrue(isinstance(alert, BaseAlert))
        # Check its from test2
        self.assertEqual(alert.user.get('username'), 'user2')
        self.assertEqual(alert.user.get('email'), '*****@*****.**')
        self.assertIn('has started following you', alert.prettify())

        # Delete test2 and ensure we get no alerts
        delete_account(user2)

        # Ensure the alert is still inside Redis
        self.assertEqual(r.zcard(k.USER_ALERTS.format(user1)), 1)

        # Get the alerts, should be none and should also clear the alert from
        # Redis
        self.assertEqual(get_alerts(user1).total, 0)
        self.assertEqual(len(get_alerts(user1).items), 0)
        self.assertEqual(r.zcard(k.USER_ALERTS.format(user1)), 0)

        # Do the same as above to ensure we can delete an alert ourselves
        # Create another user
        user3 = create_account('user3', '*****@*****.**', 'Password')

        follow_user(user1, user3)

        # Check the alerts are there
        alert = get_alerts(user3).items[0]
        self.assertTrue(isinstance(alert, FollowAlert))
        # Also check it's still a BaseAlert
        self.assertTrue(isinstance(alert, BaseAlert))
        # Check its from test2
        self.assertEqual(alert.user.get('username'), 'user1')
        self.assertEqual(alert.user.get('email'), '*****@*****.**')
        self.assertIn('has started following you', alert.prettify())

        # Delete the alert with aid from the alert
        delete_alert(user3, alert.alert_id)

        # Get the alerts and ensure the list is empty
        self.assertEqual(get_alerts(user3).total, 0)
        self.assertEqual(len(get_alerts(user3).items), 0)
        self.assertEqual(r.zcard(k.USER_ALERTS.format(user3)), 0)

        # Unfollow the user3 and then follow them again
        unfollow_user(user1, user3)
        follow_user(user1, user3)

        alert = get_alerts(user3).items[0]
        self.assertIn('has started following you', alert.prettify())

        # Manually delete the alert
        r.delete(k.ALERT.format(alert.alert_id))

        # Get the alerts again and ensure the length is 0
        # Ensure that the alert is not pulled down
        alerts = get_alerts(user3)
        self.assertEqual(len(alerts.items), 0)

        # Get alerts for a non-existant user
        # This will not fail but will have an empty pagination
        alerts = get_alerts(k.NIL_VALUE)
        self.assertEqual(len(alerts.items), 0)
Beispiel #19
0
def delete_account(user_id):
    """Will delete a users account.

    This **REMOVES ALL** details, posts, replies, etc. Not votes though.

    .. note: Ensure the user has authenticated this request. This is going to
             be the most *expensive* task in Pjuu, be warned.

    :param user_id: The `user_id` of the user to delete
    :type user_id: str

    """
    # Get the user object we will need this to remove the avatar
    user = get_user(user_id)

    # Delete the user from MongoDB
    m.db.users.remove({'_id': user_id})

    # If the user has an avatar remove it
    if user.get('avatar'):
        delete_upload(user.get('avatar'))

    # Remove all posts a user has ever made. This includes all votes
    # on the posts and all comments of the posts.
    # This calls the backend function from posts to do the deed
    posts_cursor = m.db.posts.find({'user_id': user_id}, {})
    for post in posts_cursor:
        delete_post(post.get('_id'))

    # Remove all the following relationships from Redis

    # Delete all references to followers of the user.
    # This will remove the user from the other users following list

    # TODO Replace with ZSCAN
    follower_cursor = r.zrange(k.USER_FOLLOWERS.format(user_id), 0, -1)

    for follower_id in follower_cursor:
        # Clear the followers following list of the uid
        r.zrem(k.USER_FOLLOWING.format(follower_id), user_id)
    # Delete the followers list
    r.delete(k.USER_FOLLOWERS.format(user_id))

    # Delete all references to the users the user is following
    # This will remove the user from the others users followers list

    # TODO Replace with ZSCAN
    followee_cursor = r.zrange(k.USER_FOLLOWING.format(user_id), 0, -1)

    for followee_id in followee_cursor:
        # Clear the followers list of people uid is following
        r.zrem(k.USER_FOLLOWERS.format(followee_id), user_id)
    # Delete the following list
    r.delete(k.USER_FOLLOWING.format(user_id))

    # Delete the users feed, this may have been added too during this process.
    # Probably not but let's be on the safe side
    r.delete(k.USER_FEED.format(user_id))

    # Delete the users alert list
    # DO NOT DELETE ANY ALERTS AS THESE ARE GENERIC
    r.delete(k.USER_ALERTS.format(user_id))