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
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
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)
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)
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)
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))
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)
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))
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))
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))
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
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))
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)
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))
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)
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)
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))