def test_uploads(self): """Simply tests the backend functions in `lib.uploads`. Also tests `posts.get_upload` since this is only a simple wrapper around the backend function. """ test_upload_dir = 'tests/upload_test_files/' test_upload_files = [ join(test_upload_dir, f) for f in listdir(test_upload_dir) if isfile(join(test_upload_dir, f)) ] # Create a GridFS object to test image deletion grid = gridfs.GridFS(m.db, collection='uploads') # Test each file in the upload directory for f in test_upload_files: # Don't read non image files in the directory _, ext = splitext(f) if ext not in ('.gif', '.jpg', '.jpeg', '.png'): continue image = io.BytesIO( open(f).read() ) filename, animated = process_upload(image) # Get the upload these are designed for being served directly by # Flask. This is a Flask/Werkzeug response object image = get_upload(filename) self.assertTrue(grid.exists({'filename': filename})) self.assertEqual(image.headers['Content-Type'], 'image/png') if animated: image = get_upload(animated) self.assertTrue(grid.exists({'filename': animated})) self.assertEqual(image.headers['Content-Type'], 'image/gif') # Test deletion # Ensure file is present (it will be) self.assertTrue(grid.exists({'filename': filename})) # Delete the file and ensure it is not there through GridFS delete_upload(filename) # Ensure the file has gone self.assertFalse(grid.exists({'filename': filename})) # Ensure that if we load a non-image file a None value is returned image = io.BytesIO() self.assertEqual(process_upload(image), (None, None))
def test_uploads(self): """Simply tests the backend functions in `lib.uploads`. Also tests `posts.get_upload` since this is only a simple wrapper around the backend function. """ test_upload_dir = 'tests/upload_test_files/' test_upload_files = [ join(test_upload_dir, f) for f in listdir(test_upload_dir) if isfile(join(test_upload_dir, f)) ] # Create a GridFS object to test image deletion grid = gridfs.GridFS(m.db, collection='uploads') # Test each file in the upload directory for f in test_upload_files: image = io.BytesIO(open(f).read()) filename = process_upload(image) # Get the upload these are designed for being served directly by # Flask. This is a Flask/Werkzeug response object image = get_upload(filename) self.assertTrue(grid.exists({'filename': filename})) self.assertEqual(image.headers['Content-Type'], 'image/png') # Test deletion # Ensure file is present (it will be) self.assertTrue(grid.exists({'filename': filename})) # Delete the file and ensure it is not there through GridFS delete_upload(filename) # Ensure the file has gone self.assertFalse(grid.exists({'filename': filename})) # Ensure that if we load a non-image file a None value is returned image = io.BytesIO() self.assertIsNone(process_upload(image))
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 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 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 create_post(user_id, username, body, reply_to=None, upload=None, permission=k.PERM_PUBLIC): """Creates a new post This handled both posts and what used to be called comments. If the reply_to field is not None then the post will be treat as a comment. You will need to make sure the reply_to post exists. :param user_id: The user id of the user posting the post :type user_id: str :param username: The user name of the user posting (saves a lookup) :type username: str :param body: The content of the post :type body: str :param reply_to: The post id of the post this is a reply to if any :type reply_to: str :param upload: :returns: The post id of the new post :param permission: Who can see/interact with the post you are posting :type permission: int :rtype: str or None """ # Get a new UUID for the post_id ("_id" in MongoDB) post_id = get_uuid() # Get the timestamp, we will use this to populate users feeds post_time = timestamp() post = { '_id': post_id, # Newly created post id 'user_id': user_id, # User id of the poster 'username': username, # Username of the poster 'body': body, # Body of the post 'created': post_time, # Unix timestamp for this moment in time 'score': 0, # Atomic score counter } if reply_to is not None: # If the is a reply it must have this property post['reply_to'] = reply_to else: # Replies don't need a comment count post['comment_count'] = 0 # Set the permission a user needs to view post['permission'] = permission # TODO: Make the upload process better at dealing with issues if upload: # If there is an upload along with this post it needs to go for # processing. # process_upload() can throw an Exception of UploadError. We will let # it fall through as a 500 is okay I think. # TODO: Turn this in to a Queue task at some point filename = process_upload(upload) if filename is not None: # If the upload process was okay attach the filename to the doc post['upload'] = filename else: # Stop the image upload process here if something went wrong. return None # Process everything thats needed in a post links, mentions, hashtags = parse_post(body) # Only add the fields if we need too. if links: post['links'] = links if mentions: post['mentions'] = mentions if hashtags: post['hashtags'] = hashtags # Add the post to the database # If the post isn't stored, result will be None result = m.db.posts.insert(post) # Only carry out the rest of the actions if the insert was successful if result: if reply_to is None: # Add post to authors feed r.zadd(k.USER_FEED.format(user_id), post_time, post_id) # Ensure the feed does not grow to large r.zremrangebyrank(k.USER_FEED.format(user_id), 0, -1000) # Subscribe the poster to there post subscribe(user_id, post_id, SubscriptionReasons.POSTER) # Alert everyone tagged in the post alert_tagees(mentions, user_id, post_id) # Append to all followers feeds or approved followers based # on the posts permission if permission < k.PERM_APPROVED: populate_followers_feeds(user_id, post_id, post_time) else: populate_approved_followers_feeds(user_id, post_id, post_time) else: # To reduce database look ups on the read path we will increment # the reply_to's comment count. m.db.posts.update({'_id': reply_to}, {'$inc': {'comment_count': 1}}) # Alert all subscribers to the post that a new comment has been # added. We do this before subscribing anyone new alert = CommentingAlert(user_id, reply_to) subscribers = [] # Iterate through subscribers and let them know about the comment for subscriber_id in get_subscribers(reply_to): # Ensure we don't get alerted for our own comments if subscriber_id != user_id: subscribers.append(subscriber_id) # Push the comment alert out to all subscribers AlertManager().alert(alert, subscribers) # Subscribe the user to the post, will not change anything if they # are already subscribed subscribe(user_id, reply_to, SubscriptionReasons.COMMENTER) # Alert everyone tagged in the post alert_tagees(mentions, user_id, reply_to) return post_id # If there was a problem putting the post in to Mongo we will return None return None # pragma: no cover