def update_profile_settings(user_id, about="", hide_feed_images=False, feed_size=25, replies_size=25, alerts_size=50, reply_sort_order=-1, homepage='', location='', upload=None, permission=0): """Update all options on a users profile settings in MongoDB.""" # Ensure the homepage URL is as valid as it can be if homepage != '': homepage = fix_url(homepage) avatar = None if upload: filename = process_upload(upload, image_size=(96, 96), thumbnail=False) if filename is not None: # pragma: no cover avatar = filename update_dict = { 'about': about, 'hide_feed_images': hide_feed_images, 'feed_pagination_size': int(feed_size), 'replies_pagination_size': int(replies_size), 'alerts_pagination_size': int(alerts_size), 'reply_sort_order': reply_sort_order, 'homepage': homepage, 'location': location, 'default_permission': int(permission) } if avatar is not None: update_dict['avatar'] = avatar user = get_user(user_id) if user.get('avatar'): # Clean up any old avatars # There is no update in GridFS delete_upload(user.get('avatar')) # Update the users profile m.db.users.update({'_id': user_id}, {'$set': update_dict}) # Return the user object. We can update the current_user from this return get_user(user_id)
def activate(token): """ Activates the user account so long as the token is valid. """ # Attempt to get the data from the token data = check_token(token) if data is not None and data.get('action') == 'activate': # Attempt to activate the users account user = get_user(data.get('uid')) # This does not need a branching check as it should never fail! # The check is there for safety. An auth token can not live longer # than a newly created user. if user is not None: # pragma: no branch be_activate(user.get('_id')) # If we have got to this point. Send a welcome e-mail :) send_mail('Pjuu Account Notifcation - Welcome!', [user.get('email')], text_body=render_template('emails/welcome.txt'), html_body=render_template('emails/welcome.html')) flash('Your account has now been activated', 'success') return redirect(url_for('auth.signin')) # The token is either out of date or has been tampered with flash('Invalid token', 'error') return redirect(url_for('auth.signin'))
def forgot(): """Allow users to get a password reset link""" form = ForgotForm(request.form) # We always go to /signin after a POST if request.method == 'POST': if form.validate(): user = get_user(get_uid(form.username.data, non_active=True)) if user is not None: # Only send e-mails to user which exist. token = generate_token({ 'action': 'reset', 'uid': user.get('_id') }) send_mail( 'Pjuu Account Notification - Password Reset', [user.get('email')], text_body=render_template('emails/forgot.txt', token=token), html_body=render_template('emails/forgot.html', token=token) ) flash('If we\'ve found your account we\'ve e-mailed you', 'information') return redirect(url_for('auth.signin')) else: flash('Please enter a username or e-mail address', 'error') return render_template('forgot.html', form=form)
def activate(token): """ Activates the user account so long as the token is valid. """ # Attempt to get the data from the token data = check_token(token) if data is not None and data.get('action') == 'activate': # Attempt to activate the users account user = get_user(data.get('uid')) # This does not need a branching check as it should never fail! # The check is there for safety. An auth token can not live longer # than a newly created user. if user is not None: # pragma: no branch be_activate(user.get('_id')) # If we have got to this point. Send a welcome e-mail :) send_mail( 'Pjuu Account Notifcation - Welcome!', [user.get('email')], text_body=render_template('emails/welcome.txt'), html_body=render_template('emails/welcome.html') ) flash('Your account has now been activated', 'success') return redirect(url_for('auth.signin')) # The token is either out of date or has been tampered with flash('Invalid token', 'error') return redirect(url_for('auth.signin'))
def update_profile_settings(user_id, about="", hide_feed_images=False, feed_size=25, replies_size=25, alerts_size=50, reply_sort_order=-1, homepage='', location='', upload=None, permission=0): """Update all options on a users profile settings in MongoDB.""" # Ensure the homepage URL is as valid as it can be if homepage != '': homepage = fix_url(homepage) avatar = None if upload: filename, _ = process_upload(upload, image_size=(96, 96), thumbnail=False) if filename is not None: # pragma: no cover avatar = filename update_dict = { 'about': about, 'hide_feed_images': hide_feed_images, 'feed_pagination_size': int(feed_size), 'replies_pagination_size': int(replies_size), 'alerts_pagination_size': int(alerts_size), 'reply_sort_order': reply_sort_order, 'homepage': homepage, 'location': location, 'default_permission': int(permission) } if avatar is not None: update_dict['avatar'] = avatar user = get_user(user_id) if user.get('avatar'): # Clean up any old avatars # There is no update in GridFS delete_upload(user.get('avatar')) # Update the users profile m.db.users.update({'_id': user_id}, {'$set': update_dict}) # Return the user object. We can update the current_user from this return get_user(user_id)
def avatar(username): """Return the users avatar image or the dafault.""" # Get the user user = get_user(get_uid_username(username)) # If the user has an avatar set then get it from GridFS if user.get('avatar') is not None: return get_upload(user.get('avatar'), cache_for=0, collection='avatars') # The user doesn't have one send them the default return send_file('static/img/otter_avatar.png', cache_timeout=0)
def _load_user(): """Get the currently logged in user as a `dict` and store on the application context. This will be `None` if the user is not logged in. """ user = None if 'user_id' in session: # Fetch the user object from MongoDB user = get_user(session.get('user_id')) # Remove the uid from the session if the user is not logged in if not user: session.pop('user_id', None) _app_ctx_stack.top.user = user
def new_alerts(user_id): """Checks too see if user has any new alerts since they last got the them. """ # Get the stamp since last check from Redis # If this has not been called before make it 0 alerts_last_checked = get_user(user_id).get('alerts_last_checked', 0) # Do the check. This will just see if there is anything returned from the # sorted set newer than the last_checked timestamp, SIMPLES. # # Note: zrevrangebyscore has max and min the wrong way round :P return bool(r.zrevrangebyscore(k.USER_ALERTS.format(user_id), '+inf', alerts_last_checked, start=0, num=1))
def new_alerts(user_id): """Checks too see if user has any new alerts since they last got the them. """ # Get the stamp since last check from Redis # If this has not been called before make it 0 alerts_last_checked = get_user(user_id).get('alerts_last_checked', 0) # Do the check. This will just see if there is anything returned from the # sorted set newer than the last_checked timestamp, SIMPLES. # # Note: zrevrangebyscore has max and min the wrong way round :P return len( r.zrevrangebyscore(k.USER_ALERTS.format(user_id), '+inf', alerts_last_checked))
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 forgot(): """Allow users to get a password reset link""" form = ForgotForm(request.form) # We always go to /signin after a POST if request.method == 'POST': user = get_user(get_uid(form.username.data, non_active=True)) if user is not None: # Only send e-mails to user which exist. token = generate_token({'action': 'reset', 'uid': user.get('_id')}) send_mail('Pjuu Account Notification - Password Reset', [user.get('email')], text_body=render_template('emails/forgot.txt', token=token), html_body=render_template('emails/forgot.html', token=token)) flash('If we\'ve found your account we\'ve e-mailed you', 'information') return redirect(url_for('auth.signin')) return render_template('forgot.html', form=form)
def update_profile_settings(user_id, about="", hide_feed_images=False, feed_size=25, replies_size=25, alerts_size=50, homepage='', location='', upload=None): """Update all options on a users profile settings in MongoDB.""" # Ensure the homepage URL is as valid as it can be if homepage != '': homepage = fix_url(homepage) avatar = None if upload: filename = process_upload(user_id, upload, collection='avatars', image_size=(96, 96)) if filename is not None: # pragma: no cover avatar = filename update_dict = { 'about': about, 'hide_feed_images': hide_feed_images, 'feed_pagination_size': int(feed_size), 'replies_pagination_size': int(replies_size), 'alerts_pagination_size': int(alerts_size), 'homepage': homepage, 'location': location, } if avatar is not None: # Add the avatar to the dict update_dict['avatar'] = avatar # Clean up any old avatars # There is no update in GridFS grid = gridfs.GridFS(m.db, collection='avatars') # Get all old ones cursor = grid.find( {'filename': avatar}).sort('uploadDate', -1).skip(1) for f in cursor: grid.delete(f._id) # Update the users profile m.db.users.update({'_id': user_id}, {'$set': update_dict}) # Return the user object. We can update the current_user from this return get_user(user_id)
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 view_post(username, post_id): """Displays a post along with its comments paginated. I am not sure if this should be here or in the 'posts' app. .. note: Viewable to the public if the post is public! """ if not check_post(get_uid(username), post_id): return abort(404) # Get post and comments for the current page _post = get_post(post_id) # Stop a reply from ever being shown here if 'reply_to' in _post: return abort(404) _user = get_user(get_uid(username)) # Only get the permission if the post is not owned by the current user if current_user: current_user_id = current_user.get('_id') else: current_user_id = None permission = get_user_permission(_user.get('_id'), current_user_id) if permission < _post.get('permission', k.PERM_PUBLIC): return abort(403) # Pagination page = handle_page(request) # Handle explicit sort order # Fall back to user default else default sort = request.args.get('sort', None) if sort is None: if current_user: sort = current_user.get('reply_sort_order', -1) else: sort = -1 else: try: sort = 1 if int(sort) > 0 else -1 except ValueError: if current_user: sort = current_user.get('reply_sort_order', -1) else: sort = -1 # Get the page sizes taking in to account non-logged in users if current_user: page_size = current_user.get( 'replies_pagination_size', app.config.get('REPLIES_ITEMS_PER_PAGE', 25) ) else: page_size = app.config.get('REPLIES_ITEMS_PER_PAGE', 25) pagination = get_replies(post_id, page, page_size, sort) post_form = PostForm() return render_template('view_post.html', post=_post, pagination=pagination, post_form=post_form, sort=sort)
def test_votes(self): """ Test that the voting mechanism will adjust the relevant score counters on users, posts and comments, etc... """ # Create three test users user1 = create_account('user1', '*****@*****.**', 'Password') user2 = create_account('user2', '*****@*****.**', 'Password') user3 = create_account('user3', '*****@*****.**', 'Password') # Create a post by user 1 post1 = create_post(user1, 'user1', 'Test post') # Get user 3 to downvote, this will test that the user can not go # negative yet the post can self.assertIsNone(vote_post(user3, post1, amount=-1)) # Ensure post score has been adjusted self.assertEqual(get_post(post1).get('score'), -1) # Ensure user score has NOT been adjusted self.assertEqual(get_user(user1).get('score'), 0) # Get user 2 to upvote self.assertIsNone(vote_post(user2, post1)) # Ensure post score has been adjusted self.assertEqual(get_post(post1).get('score'), 0) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 1) # Ensure user 1 can not vote on there own post self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, post1)) # Ensure the scores have not been adjusted self.assertEqual(get_post(post1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 1) # Check to see if a user can vote twice on a post self.assertRaises(AlreadyVoted, lambda: vote_post(user2, post1)) # Ensure the scores have not been adjusted self.assertEqual(get_post(post1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 1) # Repeat the same tests on a comment # Create a comment by user 1 comment1 = create_post(user1, 'user1', 'Test comment', post1) # Let's cheat and set user1's score back to 0 so we can check it will # not be lowered in the user3 downvote m.db.users.update({'_id': user1}, {'$set': {'score': 0}}) # Get user 3 to downvote self.assertIsNone(vote_post(user3, comment1, amount=-1)) # Ensure post score has been adjusted self.assertEqual(get_post(comment1)['score'], -1) # Ensure user score has NOT been adjusted self.assertEqual(get_user(user1)['score'], 0) # Get user 2 to upvote self.assertIsNone(vote_post(user2, comment1)) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), 0) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 1) # Ensure user 1 can not vote on there own comment self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, comment1)) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), 0) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 1) # Check to see if a user can vote twice on a comment self.assertRaises(AlreadyVoted, lambda: vote_post(user2, comment1)) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), 0) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 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 """ # 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))
def test_votes(self): """ Test that the voting mechanism will adjust the relevant score counters on users, posts and comments, etc... """ # Create three test users user1 = create_account('user1', '*****@*****.**', 'Password') user2 = create_account('user2', '*****@*****.**', 'Password') user3 = create_account('user3', '*****@*****.**', 'Password') # Create a post by user 1 post1 = create_post(user1, 'user1', 'Test post') # Get user 3 to downvote self.assertEqual(vote_post(user3, post1, amount=-1), -1) # Ensure post score has been adjusted self.assertEqual(get_post(post1).get('score'), -1) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), -1) # Check that a user can reverse their vote within TIMEOUT self.assertEqual(vote_post(user3, post1, amount=-1), 0) self.assertEqual(get_post(post1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) # Get user 2 to upvote self.assertEqual(vote_post(user2, post1), 1) # Ensure post score has been adjusted self.assertEqual(get_post(post1).get('score'), 1) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 1) # Ensure user 1 can not vote on there own post self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, post1)) # Ensure the scores have not been adjusted self.assertEqual(get_post(post1).get('score'), 1) self.assertEqual(get_user(user1).get('score'), 1) # Ensure the user has voted self.assertTrue(has_voted(user2, post1)) # Check that a user can reverse their vote within TIMEOUT self.assertEqual(vote_post(user2, post1), 0) self.assertEqual(get_post(post1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) # Ensure the user has voted self.assertFalse(has_voted(user2, post1)) # Check that the score reflects an opposite vote within TIMEOUT self.assertEqual(vote_post(user2, post1, 1), 1) self.assertEqual(get_post(post1).get('score'), 1) self.assertEqual(get_user(user1).get('score'), 1) self.assertTrue(has_voted(user2, post1)) self.assertEqual(vote_post(user2, post1, -1), -1) self.assertEqual(get_post(post1).get('score'), -1) self.assertEqual(get_user(user1).get('score'), -1) self.assertTrue(has_voted(user2, post1)) # Check that a user can not reverse there vote after the TIMEOUT self.assertRaises(AlreadyVoted, lambda: vote_post(user2, post1, -1, timestamp() + K.VOTE_TIMEOUT + 1)) self.assertRaises(AlreadyVoted, lambda: vote_post(user2, post1, 1, timestamp() + K.VOTE_TIMEOUT + 1)) # Repeat the same tests on a comment # Create a comment by user 1 comment1 = create_post(user1, 'user1', 'Test comment', post1) # Let's cheat and set user1's score back to 0 m.db.users.update({'_id': user1}, {'$set': {'score': 0}}) # Get user 3 to downvote self.assertEqual(vote_post(user3, comment1, amount=-1), -1) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), -1) self.assertEqual(get_user(user1).get('score'), -1) # Reverse user3's vote just so it's not confusing self.assertEqual(vote_post(user3, comment1, amount=-1), 0) self.assertEqual(get_post(comment1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) # Ensure user 1 can not vote on there own comment self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, comment1)) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), 0) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 0) # Get user 2 to upvote self.assertEqual(vote_post(user2, comment1), 1) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), 1) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 1) self.assertTrue(has_voted(user2, comment1)) # Check that a user can reverse their vote within TIMEOUT self.assertEqual(vote_post(user2, comment1), 0) self.assertEqual(get_post(comment1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) self.assertFalse(has_voted(user2, comment1)) # Check that the score reflects an opposite vote within TIMEOUT self.assertEqual(vote_post(user2, comment1, -1), -1) self.assertEqual(get_post(comment1).get('score'), -1) self.assertEqual(get_user(user1).get('score'), -1) self.assertTrue(has_voted(user2, comment1)) self.assertEqual(vote_post(user2, comment1, 1), 1) self.assertEqual(get_post(comment1).get('score'), 1) self.assertEqual(get_user(user1).get('score'), 1) self.assertTrue(has_voted(user2, comment1)) # Check that a user can not reverse there vote after the TIMEOUT self.assertRaises(AlreadyVoted, lambda: vote_post(user2, comment1, -1, timestamp() + K.VOTE_TIMEOUT + 1)) self.assertRaises(AlreadyVoted, lambda: vote_post(user2, comment1, 1, timestamp() + K.VOTE_TIMEOUT + 1))
def view_post(username, post_id): """Displays a post along with its comments paginated. I am not sure if this should be here or in the 'posts' app. .. note: Viewable to the public if the post is public! """ if not check_post(get_uid(username), post_id): return abort(404) # Get post and comments for the current page _post = get_post(post_id) # Stop a reply from ever being shown here if 'reply_to' in _post: return abort(404) _user = get_user(get_uid(username)) # Only get the permission if the post is not owned by the current user if current_user: current_user_id = current_user.get('_id') else: current_user_id = None permission = get_user_permission(_user.get('_id'), current_user_id) if permission < _post.get('permission', k.PERM_PUBLIC): return abort(403) # Pagination page = handle_page(request) # Handle explicit sort order # Fall back to user default else default sort = request.args.get('sort', None) if sort is None: if current_user: sort = current_user.get('reply_sort_order', -1) else: sort = -1 else: try: sort = 1 if int(sort) > 0 else -1 except ValueError: if current_user: sort = current_user.get('reply_sort_order', -1) else: sort = -1 # Get the page sizes taking in to account non-logged in users if current_user: page_size = current_user.get( 'replies_pagination_size', app.config.get('REPLIES_ITEMS_PER_PAGE', 25)) else: page_size = app.config.get('REPLIES_ITEMS_PER_PAGE', 25) pagination = get_replies(post_id, page, page_size, sort) post_form = PostForm() return render_template('view_post.html', post=_post, pagination=pagination, post_form=post_form, sort=sort)
def test_votes(self): """ Test that the voting mechanism will adjust the relevant score counters on users, posts and comments, etc... """ # Create three test users user1 = create_account('user1', '*****@*****.**', 'Password') user2 = create_account('user2', '*****@*****.**', 'Password') user3 = create_account('user3', '*****@*****.**', 'Password') # Create a post by user 1 post1 = create_post(user1, 'user1', 'Test post') # Get user 3 to downvote self.assertEqual(vote_post(user3, post1, amount=-1), -1) # Ensure post score has been adjusted self.assertEqual(get_post(post1).get('score'), -1) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), -1) # Check that a user can reverse their vote within TIMEOUT self.assertEqual(vote_post(user3, post1, amount=-1), 0) self.assertEqual(get_post(post1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) # Get user 2 to upvote self.assertEqual(vote_post(user2, post1), 1) # Ensure post score has been adjusted self.assertEqual(get_post(post1).get('score'), 1) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 1) # Ensure user 1 can not vote on there own post self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, post1)) # Ensure the scores have not been adjusted self.assertEqual(get_post(post1).get('score'), 1) self.assertEqual(get_user(user1).get('score'), 1) # Ensure the user has voted self.assertTrue(has_voted(user2, post1)) # Check that a user can reverse their vote within TIMEOUT self.assertEqual(vote_post(user2, post1), 0) self.assertEqual(get_post(post1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) # Ensure the user has voted self.assertFalse(has_voted(user2, post1)) # Check that the score reflects an opposite vote within TIMEOUT self.assertEqual(vote_post(user2, post1, 1), 1) self.assertEqual(get_post(post1).get('score'), 1) self.assertEqual(get_user(user1).get('score'), 1) self.assertTrue(has_voted(user2, post1)) self.assertEqual(vote_post(user2, post1, -1), -1) self.assertEqual(get_post(post1).get('score'), -1) self.assertEqual(get_user(user1).get('score'), -1) self.assertTrue(has_voted(user2, post1)) # Check that a user can not reverse there vote after the TIMEOUT self.assertRaises( AlreadyVoted, lambda: vote_post(user2, post1, -1, timestamp() + K.VOTE_TIMEOUT + 1)) self.assertRaises( AlreadyVoted, lambda: vote_post(user2, post1, 1, timestamp() + K.VOTE_TIMEOUT + 1)) # Repeat the same tests on a comment # Create a comment by user 1 comment1 = create_post(user1, 'user1', 'Test comment', post1) # Let's cheat and set user1's score back to 0 m.db.users.update({'_id': user1}, {'$set': {'score': 0}}) # Get user 3 to downvote self.assertEqual(vote_post(user3, comment1, amount=-1), -1) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), -1) self.assertEqual(get_user(user1).get('score'), -1) # Reverse user3's vote just so it's not confusing self.assertEqual(vote_post(user3, comment1, amount=-1), 0) self.assertEqual(get_post(comment1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) # Ensure user 1 can not vote on there own comment self.assertRaises(CantVoteOnOwn, lambda: vote_post(user1, comment1)) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), 0) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 0) # Get user 2 to upvote self.assertEqual(vote_post(user2, comment1), 1) # Ensure post score has been adjusted self.assertEqual(get_post(comment1).get('score'), 1) # Ensure user score has been adjusted self.assertEqual(get_user(user1).get('score'), 1) self.assertTrue(has_voted(user2, comment1)) # Check that a user can reverse their vote within TIMEOUT self.assertEqual(vote_post(user2, comment1), 0) self.assertEqual(get_post(comment1).get('score'), 0) self.assertEqual(get_user(user1).get('score'), 0) self.assertFalse(has_voted(user2, comment1)) # Check that the score reflects an opposite vote within TIMEOUT self.assertEqual(vote_post(user2, comment1, -1), -1) self.assertEqual(get_post(comment1).get('score'), -1) self.assertEqual(get_user(user1).get('score'), -1) self.assertTrue(has_voted(user2, comment1)) self.assertEqual(vote_post(user2, comment1, 1), 1) self.assertEqual(get_post(comment1).get('score'), 1) self.assertEqual(get_user(user1).get('score'), 1) self.assertTrue(has_voted(user2, comment1)) # Check that a user can not reverse there vote after the TIMEOUT self.assertRaises( AlreadyVoted, lambda: vote_post(user2, comment1, -1, timestamp() + K.VOTE_TIMEOUT + 1)) self.assertRaises( AlreadyVoted, lambda: vote_post(user2, comment1, 1, timestamp() + K.VOTE_TIMEOUT + 1))