Пример #1
0
def unapprove_user(who_uid, whom_uid):
    """Allow a user to un-approve a follower"""
    # Check the follower is actually approved
    if r.zrank(k.USER_APPROVED.format(who_uid), whom_uid) is None:
        return False

    # No alert for un-approved
    r.zrem(k.USER_APPROVED.format(who_uid), whom_uid)

    return True
Пример #2
0
def unapprove_user(who_uid, whom_uid):
    """Allow a user to un-approve a follower"""
    # Check the follower is actually approved
    if r.zrank(k.USER_APPROVED.format(who_uid), whom_uid) is None:
        return False

    # No alert for un-approved
    r.zrem(k.USER_APPROVED.format(who_uid), whom_uid)

    return True
Пример #3
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

    """
    # 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))
Пример #4
0
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)
Пример #5
0
def unfollow_user(who_uid, whom_uid):
    """Remove whom from who's following zset and who to whom's followers zset.
    """
    # Check that we are actually following the users
    if r.zrank(k.USER_FOLLOWING.format(who_uid), whom_uid) is None:
        return False

    # Delete uid from who following and whom followers
    r.zrem(k.USER_FOLLOWING.format(who_uid), whom_uid)
    r.zrem(k.USER_FOLLOWERS.format(whom_uid), who_uid)

    return True
Пример #6
0
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)
Пример #7
0
def unfollow_user(who_uid, whom_uid):
    """Remove whom from who's following zset and who to whom's followers zset.
    """
    # Check that we are actually following the users
    if r.zrank(k.USER_FOLLOWING.format(who_uid), whom_uid) is None:
        return False

    # Delete uid from who following and whom followers
    r.zrem(k.USER_FOLLOWING.format(who_uid), whom_uid)
    r.zrem(k.USER_FOLLOWERS.format(whom_uid), who_uid)

    # Delete the user from the approved list
    unapprove_user(whom_uid, who_uid)

    return True
Пример #8
0
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)
Пример #9
0
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)
Пример #10
0
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)
Пример #11
0
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)
Пример #12
0
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)
Пример #13
0
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)
Пример #14
0
def unsubscribe(user_id, post_id):
    """Unsubscribe a user from a post.

    """
    # Actually remove the uid from the subscribers list
    return bool(r.zrem(k.POST_SUBSCRIBERS.format(post_id), user_id))
Пример #15
0
def vote_post(user_id, post_id, amount=1, ts=None):
    """Handles voting on posts

    :param user_id: User who is voting
    :type user_id: str
    :param post_id: ID of the post the user is voting on
    :type post_id: int
    :param amount: The way to vote (-1 or 1)
    :type amount: int
    :param ts: Timestamp to use for vote (ONLY FOR TESTING)
    :type ts: int
    :returns: -1 if downvote, 0 if reverse vote and +1 if upvote

    """
    if ts is None:
        ts = timestamp()

    # Get the comment so we can check who the author is
    author_uid = get_post(post_id).get('user_id')

    # Votes can ONLY ever be -1 or 1 and nothing else
    # we use the sign to store the time and score in one zset score
    amount = 1 if amount >= 0 else -1

    voted = has_voted(user_id, post_id)

    if not voted:
        if author_uid != user_id:
            # Store the timestamp of the vote with the sign of the vote
            r.zadd(k.POST_VOTES.format(post_id), amount * timestamp(), user_id)

            # Update post score
            m.db.posts.update({'_id': post_id},
                              {'$inc': {'score': amount}})

            # Update user score
            m.db.users.update({'_id': author_uid},
                              {'$inc': {'score': amount}})

            return amount
        else:
            raise CantVoteOnOwn
    elif voted and abs(voted) + k.VOTE_TIMEOUT > ts:
        # No need to check if user is current user because it can't
        # happen in the first place
        # Remove the vote from Redis
        r.zrem(k.POST_VOTES.format(post_id), user_id)

        previous_vote = -1 if voted < 0 else 1

        # Calculate how much to increment/decrement the scores by
        # Saves multiple trips to Mongo
        if amount == previous_vote:
            if previous_vote < 0:
                amount = 1
                result = 0
            else:
                amount = -1
                result = 0
        else:
            # We will only register the new vote if it is NOT a vote reversal.
            r.zadd(k.POST_VOTES.format(post_id), amount * timestamp(), user_id)

            if previous_vote < 0:
                amount = 2
                result = 1
            else:
                amount = -2
                result = -1

        # Update post score
        m.db.posts.update({'_id': post_id},
                          {'$inc': {'score': amount}})

        # Update user score
        m.db.users.update({'_id': author_uid},
                          {'$inc': {'score': amount}})

        return result
    else:
        raise AlreadyVoted
Пример #16
0
def delete_alert(user_id, alert_id):
    """Removes an alert with aid from user with uid's alert feed. This does not
    delete the alert object, it may be on other users feeds.

    """
    return bool(r.zrem(k.USER_ALERTS.format(user_id), alert_id))
Пример #17
0
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))
Пример #18
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))
Пример #19
0
def remove_from_feed(post_id, user_id):
    """Remove ``post_id`` from ``user_id``s feed."""
    return bool(r.zrem(k.USER_FEED.format(user_id), post_id))
Пример #20
0
def delete_alert(user_id, alert_id):
    """Removes an alert with aid from user with uid's alert feed. This does not
    delete the alert object, it may be on other users feeds.

    """
    return bool(r.zrem(k.USER_ALERTS.format(user_id), alert_id))
Пример #21
0
def remove_from_feed(post_id, user_id):
    """Remove ``post_id`` from ``user_id``s feed."""
    return bool(r.zrem(k.USER_FEED.format(user_id), post_id))