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 get_profile(user_id): """Returns a user dict with add post_count, follow_count and following.""" profile = m.db.users.find_one({'_id': user_id}) if profile: # Count the users posts in MongoDB profile['post_count'] = m.db.posts.find( {'user_id': user_id, 'reply_to': {'$exists': False}} ).count() # Count followers and folowees in Redis profile['followers_count'] = r.zcard(k.USER_FOLLOWERS.format(user_id)) profile['following_count'] = r.zcard(k.USER_FOLLOWING.format(user_id)) return profile if profile else None
def test_alertmanager(self): """ Test the alert manager. Similar to the above a very simple test. Will check that it can alert users and one can be created. """ # Create our alert manager am = AlertManager() # Try and load a non-existant alert self.assertIsNone(am.get(k.NIL_VALUE)) # Try and alert on something which is not an alert self.assertRaises(ValueError, lambda: am.alert('ALERT', k.NIL_VALUE)) # Test that alerting a single users does not work, they need to be # iterable # Create an alert alert = BaseAlert(k.NIL_VALUE) self.assertRaises(TypeError, lambda: am.alert(alert, 1)) # Create a couple of users user1 = create_account('user1', '*****@*****.**', 'Password') user2 = create_account('user2', '*****@*****.**', 'Password') # Ensure the length of user1's alert feed is 0 self.assertEqual(r.zcard(k.USER_ALERTS.format(user1)), 0) # Create an alert from user2 alert = BaseAlert(user2) # Alert user1 am.alert(alert, [user1]) # Ensure the length of user1's alert feed is 1 self.assertEqual(r.zcard(k.USER_ALERTS.format(user1)), 1) # Get alerts for user1, user Redis directly alerts = r.zrange(k.USER_ALERTS.format(user1), 0, 0) # Get the alert from alerts alert = am.get(alerts[0]) self.assertIsNotNone(alert) self.assertEqual(alert.user.get('username'), 'user2') self.assertEqual(alert.user.get('email'), '*****@*****.**') # Delete test2 and ensure getting the alert returns None delete_account(user2) alert = am.get(alerts[0]) self.assertIsNone(alert)
def get_profile(user_id): """Returns a user dict with add post_count, follow_count and following.""" profile = m.db.users.find_one({'_id': user_id}) if profile: # Count the users posts in MongoDB profile['post_count'] = m.db.posts.find({ 'user_id': user_id, 'reply_to': { '$exists': False } }).count() # Count followers and folowees in Redis profile['followers_count'] = r.zcard(k.USER_FOLLOWERS.format(user_id)) profile['following_count'] = r.zcard(k.USER_FOLLOWING.format(user_id)) return profile if profile else None
def get_following(uid, page=1): """Returns a list of users uid is following as a pagination object.""" per_page = app.config.get('PROFILE_ITEMS_PER_PAGE') total = r.zcard(k.USER_FOLLOWING.format(uid)) fids = r.zrevrange(k.USER_FOLLOWING.format(uid), (page - 1) * per_page, (page * per_page) - 1) users = [] for fid in fids: user = get_user(fid) if user: users.append(user) else: # Self cleaning sorted sets r.zrem(k.USER_FOLLOWING.format(uid), fid) total = r.zcard(k.USER_FOLLOWING.format(id)) return Pagination(users, total, page, per_page)
def get_following(uid, page=1, per_page=None): """Returns a list of users uid is following as a pagination object.""" if per_page is None: per_page = app.config.get('FEED_ITEMS_PER_PAGE') total = r.zcard(k.USER_FOLLOWING.format(uid)) fids = r.zrevrange(k.USER_FOLLOWING.format(uid), (page - 1) * per_page, (page * per_page) - 1) users = [] for fid in fids: user = get_user(fid) if user: users.append(user) else: # Self cleaning sorted sets r.zrem(k.USER_FOLLOWING.format(uid), fid) total = r.zcard(k.USER_FOLLOWING.format(id)) return Pagination(users, total, page, per_page)
def get_followers(uid, page=1, per_page=None): """Returns a list of users who follow user with uid as a pagination object. """ if per_page is None: per_page = app.config.get('FEED_ITEMS_PER_PAGE') total = r.zcard(k.USER_FOLLOWERS.format(uid)) fids = r.zrevrange(k.USER_FOLLOWERS.format(uid), (page - 1) * per_page, (page * per_page) - 1) users = [] for fid in fids: user = get_user(fid) if user: users.append(user) else: # Self cleaning sorted sets r.zrem(k.USER_FOLLOWERS.format(uid), fid) total = r.zcard(k.USER_FOLLOWERS.format(uid)) return Pagination(users, total, page, per_page)
def get_feed(user_id, page=1): """Returns a users feed as a pagination object. .. note: The feed is stored inside Redis still as this requires fan-out to update all the users who are following you. """ per_page = app.config.get('FEED_ITEMS_PER_PAGE') total = r.zcard(k.USER_FEED.format(user_id)) pids = r.zrevrange(k.USER_FEED.format(user_id), (page - 1) * per_page, (page * per_page) - 1) posts = [] for pid in pids: # Get the post post = get_post(pid) if post: posts.append(post) else: # Self cleaning lists r.zrem(k.USER_FEED.format(user_id), pid) total = r.zcard(k.USER_FEED.format(user_id)) return Pagination(posts, total, page, per_page)
def get_feed(user_id, page=1, per_page=None): """Returns all the posts in a users feed as a pagination object. .. note: The feed is stored inside Redis still as this requires fan-out to update all the users who are following you. """ if per_page is None: per_page = app.config.get('FEED_ITEMS_PER_PAGE') # Get the total number of item in the feed and the subset for the current # page. total = r.zcard(k.USER_FEED.format(user_id)) pids = r.zrevrange(k.USER_FEED.format(user_id), (page - 1) * per_page, (page * per_page) - 1) # Get all the posts in one call to MongoDB posts = [] cursor = m.db.posts.find({ '_id': { '$in': pids } }).sort('created', pymongo.DESCENDING) for post in cursor: posts.append(post) # Get a list of unique `user_id`s from all the post. user_ids = list(set([post.get('user_id') for post in posts])) cursor = m.db.users.find({'_id': {'$in': user_ids}}, {'avatar': True}) # Create a lookup dict `{username: email}` user_avatars = \ dict((user.get('_id'), user.get('avatar')) for user in cursor) # Add the e-mails to the posts processed_posts = [] for post in posts: post['user_avatar'] = user_avatars.get(post.get('user_id')) processed_posts.append(post) # Clean up the list in Redis if the if len(processed_posts) < len(pids): diff_pids = list( set(pids) - set([post.get('_id') for post in processed_posts])) r.zrem(k.USER_FEED.format(user_id), *diff_pids) return Pagination(processed_posts, total, page, per_page)
def get_feed(user_id, page=1, per_page=None): """Returns all the posts in a users feed as a pagination object. .. note: The feed is stored inside Redis still as this requires fan-out to update all the users who are following you. """ if per_page is None: per_page = app.config.get('FEED_ITEMS_PER_PAGE') # Get the total number of item in the feed and the subset for the current # page. total = r.zcard(k.USER_FEED.format(user_id)) pids = r.zrevrange(k.USER_FEED.format(user_id), (page - 1) * per_page, (page * per_page) - 1) # Get all the posts in one call to MongoDB posts = [] cursor = m.db.posts.find({'_id': {'$in': pids}}).sort( 'created', pymongo.DESCENDING) for post in cursor: posts.append(post) # Get a list of unique `user_id`s from all the post. user_ids = list(set([post.get('user_id') for post in posts])) cursor = m.db.users.find({'_id': {'$in': user_ids}}, {'avatar': True}) # Create a lookup dict `{username: email}` user_avatars = \ dict((user.get('_id'), user.get('avatar')) for user in cursor) # Add the e-mails to the posts processed_posts = [] for post in posts: post['user_avatar'] = user_avatars.get(post.get('user_id')) processed_posts.append(post) # Clean up the list in Redis if the if len(processed_posts) < len(pids): diff_pids = list( set(pids) - set([post.get('_id') for post in processed_posts])) r.zrem(k.USER_FEED.format(user_id), *diff_pids) return Pagination(processed_posts, total, page, per_page)
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)