Exemplo n.º 1
0
def back_feed(who_id, whom_id):
    """Takes 5 lastest posts from user with ``who_id`` places them in user
    with ``whom_id`` feed.

    The reason behind this is that new users may follow someone but still have
    and empty feed, which makes them sad :( so we'll give them some. If the
    posts are to old for a non user they will be removed when the feed is
    trimmed, but they may make it in to the feed but not at the top.

    :param who_id: user who just followed ``who_id``
    :type who_id: str
    :param whom_id: user who was just followed by ``whom_id``
    :type whom_id: str
    :returns: None

    """
    # Get followee's last 5 un-approved posts (doesn't matter if isn't any)
    # We only need the IDs and the created time
    posts = m.db.posts.find(
        {'user_id': whom_id, 'reply_to': None,
         'permission': {'$lte': k.PERM_PJUU}},
        {'_id': True, 'created': True},
    ).sort('created', -1).limit(5)

    # Iterate the cursor and append the posts to the users feed
    for post in posts:
        timestamp = post.get('created')
        post_id = post.get('_id')
        # Place on the feed
        r.zadd(k.USER_FEED.format(who_id), timestamp, post_id)
        # Trim the feed to the 1000 max
        r.zremrangebyrank(k.USER_FEED.format(who_id), 0, -1000)
Exemplo n.º 2
0
def populate_approved_followers_feeds(user_id, post_id, timestamp):
    """Fan out a post_id to all the users approved followers."""
    # Get a list of ALL users who are following a user
    followers = r.zrange(k.USER_APPROVED.format(user_id), 0, -1)
    # This is not transactional as to not hold Redis up.
    for follower_id in followers:
        # Add the pid to the list
        r.zadd(k.USER_FEED.format(follower_id), timestamp, post_id)
        # Stop followers feeds from growing to large, doesn't matter if it
        # doesn't exist
        r.zremrangebyrank(k.USER_FEED.format(follower_id), 0, -1000)
Exemplo n.º 3
0
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