예제 #1
0
 def sort_results(k):
     """Allow sorting of the search results by closest matchng
     then by date the item was created."""
     if k.get('hashtags'):
         for hashtag in k.get('hashtags'):  # pragma: no branch
             if hashtag.get('hashtag', '').startswith(query):
                 return (hashtag.get('hashtag'),
                         timestamp() - k.get('created', 0))
     else:
         return (k.get('username'), timestamp() - k.get('created', 0))
예제 #2
0
파일: backend.py 프로젝트: Velody/pjuu
 def sort_results(k):
     """Allow sorting of the search results by closest matchng
     then by date the item was created."""
     if k.get('hashtags'):
         for hashtag in k.get('hashtags'):  # pragma: no branch
             if hashtag.get('hashtag', '').startswith(query):
                 return (hashtag.get('hashtag'),
                         timestamp() - k.get('created', 0))
     else:
         return (k.get('username'),
                 timestamp() - k.get('created', 0))
예제 #3
0
    def test_timeify_filter(self):
        """Test the timeify filter

        """
        self.assertEqual(timeify_filter(timestamp()), 'Less than a second ago')
        # Check one year ago
        time_yearago = timestamp() - 31536000
        self.assertEqual(timeify_filter(time_yearago), '1 year ago')
        # Check two months ago
        time_yearago = timestamp() - 5184000
        self.assertEqual(timeify_filter(time_yearago), '2 months ago')
        # Check 3 weeks ago
        time_yearago = timestamp() - 1814400
        self.assertEqual(timeify_filter(time_yearago), '3 weeks ago')
예제 #4
0
    def test_timeify_filter(self):
        """Test the timeify filter

        """
        self.assertEqual(timeify_filter(timestamp()), 'Less than a second ago')
        # Check one year ago
        time_yearago = timestamp() - 31536000
        self.assertEqual(timeify_filter(time_yearago), '1 year ago')
        # Check two months ago
        time_yearago = timestamp() - 5184000
        self.assertEqual(timeify_filter(time_yearago), '2 months ago')
        # Check 3 weeks ago
        time_yearago = timestamp() - 1814400
        self.assertEqual(timeify_filter(time_yearago), '3 weeks ago')
예제 #5
0
파일: backend.py 프로젝트: hnk/pjuu
def signin(user_id):
    """Logs the user with uid in by adding the uid to the session.

    """
    session["user_id"] = user_id
    # update last login
    m.db.users.update({"_id": user_id}, {"$set": {"last_login": timestamp()}})
예제 #6
0
def timeify_filter(time):
    """Takes integer epoch time and returns a DateTime string for display.

    If this conversion fails this function will return "Err"
    """
    try:
        # Please not that time is now a floating point value for extra
        # precision. We don't really need this when displaying it to the users
        # however.
        # Time can't be coverted directly to a int as it is a float point repr
        time = int(timestamp() - float(time))

        multiples = [(31536000, 'year'), (2592000, 'month'), (604800, 'week'),
                     (86400, 'day'), (3600, 'hour'), (60, 'minute'),
                     (1, 'second')]

        # Find the closest time multiple since this post was posted
        # Work out the number of these multiples and return the string
        for multiple in multiples:
            if time < multiple[0]:
                continue
            number_of = math.floor(time / multiple[0])
            if number_of > 1:
                time_frame = multiple[1] + 's'
            else:
                time_frame = multiple[1]

            return "{0} {1} ago".format(int(number_of), time_frame)

        # Default return means that this was checked less than a second ago
        return "Less than a second ago"

    except (TypeError, ValueError):
        return "Err"
예제 #7
0
파일: backend.py 프로젝트: sydneyjd/pjuu
def signin(user_id):
    """Logs the user with uid in by adding the uid to the session.

    """
    session['user_id'] = user_id
    # update last login
    m.db.users.update({'_id': user_id}, {'$set': {'last_login': timestamp()}})
예제 #8
0
파일: backend.py 프로젝트: Velody/pjuu
def signin(user_id):
    """Logs the user with uid in by adding the uid to the session.

    """
    session['user_id'] = user_id
    # update last login
    m.db.users.update({'_id': user_id}, {'$set': {'last_login': timestamp()}})
예제 #9
0
파일: backend.py 프로젝트: adzilla/pjuu
def login(uid):
    """Logs the user with uid in by adding the uid to the session.

    """
    session['uid'] = uid
    # update last login
    r.hset(K.USER.format(uid), 'last_login', timestamp())
예제 #10
0
 def display_time(response):
     """This is will write the time to the console in DEBUG mode"""
     if app.debug and not app.testing:  # pragma: no cover
         if request.endpoint != 'static':
             print request.path, request.endpoint, \
                 str((timestamp() - g.start_time) * 100) + 'ms'
     return response
예제 #11
0
파일: __init__.py 프로젝트: pjuu/pjuu
 def display_time(response):
     """This is will write the time to the console in DEBUG mode"""
     if app.debug and not app.testing:  # pragma: no cover
         if request.endpoint != 'static':
             print request.path, request.endpoint, \
                 str((timestamp() - g.start_time) * 100) + 'ms'
     return response
예제 #12
0
파일: backend.py 프로젝트: buskirka/pjuu
def create_account(username, email, password):
    """Creates a new user account.

    :param username: The new users user name
    :type username: str
    :param email: The new users e-mail address
    :type email: str
    :param password: The new users password un-hashed
    :type password: str
    :returns: The UID of the new user
    :rtype: str or None

    """
    username = username.lower()
    email = email.lower()
    try:
        if check_username(username) and check_username_pattern(username) and \
                check_email(email) and check_email_pattern(email):
            # Get a new UUID for the user
            uid = get_uuid()

            user = {
                '_id': uid,
                'username': username.lower(),
                'email': email.lower(),
                'password': generate_password(password,
                                              method='pbkdf2:sha256:2000',
                                              salt_length=20),
                'created': timestamp(),
                'last_login': -1,
                'active': False,
                'banned': False,
                'op': False,
                'muted': False,
                'about': "",
                'score': 0,
                'alerts_last_checked': -1,
                # Set the TTL for a newly created user, this has to be Datetime
                # object for MongoDB to recognise it. This is removed on
                # activation.
                'ttl': datetime.utcnow()
            }

            # Set all the tips for new users
            for tip_name in k.VALID_TIP_NAMES:
                user['tip_{}'.format(tip_name)] = True

            # Insert the new user in to Mongo. If this fails a None will be
            # returned
            result = m.db.users.insert(user)
            return uid if result else None
    except DuplicateKeyError:  # pragma: no cover
        # Oh no something went wrong. Pass over it. A None will be returned.
        pass

    return None
예제 #13
0
파일: backend.py 프로젝트: hnk/pjuu
def create_account(username, email, password):
    """Creates a new user account.

    :param username: The new users user name
    :type username: str
    :param email: The new users e-mail address
    :type email: str
    :param password: The new users password un-hashed
    :type password: str
    :returns: The UID of the new user
    :rtype: str or None

    """
    username = username.lower()
    email = email.lower()
    try:
        if (
            check_username(username)
            and check_username_pattern(username)
            and check_email(email)
            and check_email_pattern(email)
        ):
            # Get a new UUID for the user
            uid = get_uuid()

            user = {
                "_id": uid,
                "username": username.lower(),
                "email": email.lower(),
                "password": generate_password(password, method="pbkdf2:sha256:2000", salt_length=20),
                "created": timestamp(),
                "last_login": -1,
                "active": False,
                "banned": False,
                "op": False,
                "muted": False,
                "about": "",
                "score": 0,
                "alerts_last_checked": -1,
                # Set the TTL for a newly created user, this has to be Datetime
                # object for MongoDB to recognise it. This is removed on
                # activation.
                "ttl": datetime.utcnow(),
            }

            # Insert the new user in to Mongo. If this fails a None will be
            # returned
            result = m.db.users.insert(user)
            return uid if result else None
    except DuplicateKeyError:  # pragma: no cover
        # Oh no something went wrong. Pass over it. A None will be returned.
        pass

    return None
예제 #14
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)
예제 #15
0
def approve_user(who_uid, whom_uid):
    """Allow a user to approve a follower"""
    # Check that the user is actually following.
    # Fail if not
    if r.zrank(k.USER_FOLLOWERS.format(who_uid), whom_uid) is None:
        return False

    # Add the user to the approved list
    # No alert is generated
    r.zadd(k.USER_APPROVED.format(who_uid), timestamp(), whom_uid)

    return True
예제 #16
0
파일: backend.py 프로젝트: pjuu/pjuu
def approve_user(who_uid, whom_uid):
    """Allow a user to approve a follower"""
    # Check that the user is actually following.
    # Fail if not
    if r.zrank(k.USER_FOLLOWERS.format(who_uid), whom_uid) is None:
        return False

    # Add the user to the approved list
    # No alert is generated
    r.zadd(k.USER_APPROVED.format(who_uid), timestamp(), whom_uid)

    return True
예제 #17
0
파일: backend.py 프로젝트: Velody/pjuu
def follow_user(who_uid, whom_uid):
    """Add whom to who's following zset and who to whom's followers zset.
    Generate an alert for this action.
    """
    # Check that we are not already following the user
    if r.zrank(k.USER_FOLLOWING.format(who_uid), whom_uid) is not None:
        return False

    # Follow user
    # Score is based on UTC epoch time
    r.zadd(k.USER_FOLLOWING.format(who_uid), timestamp(), whom_uid)
    r.zadd(k.USER_FOLLOWERS.format(whom_uid), timestamp(), who_uid)

    # Create an alert and inform whom that who is now following them
    alert = FollowAlert(who_uid)
    AlertManager().alert(alert, [whom_uid])

    # Back fill the who's feed with some posts from whom
    back_feed(who_uid, whom_uid)

    return True
예제 #18
0
def follow_user(who_uid, whom_uid):
    """Add whom to who's following zset and who to whom's followers zset.
    Generate an alert for this action.
    """
    # Check that we are not already following the user
    if r.zrank(k.USER_FOLLOWING.format(who_uid), whom_uid) is not None:
        return False

    # Follow user
    # Score is based on UTC epoch time
    r.zadd(k.USER_FOLLOWING.format(who_uid), timestamp(), whom_uid)
    r.zadd(k.USER_FOLLOWERS.format(whom_uid), timestamp(), who_uid)

    # Create an alert and inform whom that who is now following them
    alert = FollowAlert(who_uid)
    AlertManager().alert(alert, [whom_uid])

    # Back fill the who's feed with some posts from whom
    back_feed(who_uid, whom_uid)

    return True
예제 #19
0
파일: backend.py 프로젝트: Velody/pjuu
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)
예제 #20
0
파일: backend.py 프로젝트: adzilla/pjuu
def create_post(uid, body):
    """Creates a new post

    """
    # Get a new UUID for the pid
    pid = get_uuid()

    # Hash form for posts
    # TODO this needs expanding to include some form of image upload hook
    post = {
        'pid': pid,
        'uid': uid,
        'body': body,
        'created': timestamp(),
        'score': 0
    }

    # Add post
    r.hmset(K.POST.format(pid), post)
    # Add post to users post list
    r.lpush(K.USER_POSTS.format(uid), pid)
    # Add post to authors feed
    r.lpush(K.USER_FEED.format(uid), pid)
    # Ensure the feed does not grow to large
    r.ltrim(K.USER_FEED.format(uid), 0, 999)

    # Append to all followers feeds
    populate_feeds(uid, pid)

    # Subscribe the poster to there post
    subscribe(uid, pid, SubscriptionReasons.POSTER)

    # TAGGING

    # Create alert manager and alert
    alert = TaggingAlert(uid, pid)
    # Alert tagees
    tagees = parse_tags(body)
    # Store a list of uids which need to alerted to the tagging
    tagees_to_alert = []
    for tagee in tagees:
        # Don't allow tagging yourself
        if tagee[0] != uid:
            # Subscribe the tagee to the alert
            subscribe(tagee[0], pid, SubscriptionReasons.TAGEE)
            # Add the tagee's uid to the list to alert them
            tagees_to_alert.append(tagee[0])

    # Alert the required tagees
    AlertManager().alert(alert, tagees_to_alert)

    return pid
예제 #21
0
파일: backend.py 프로젝트: sydneyjd/pjuu
def flag_post(user_id, post_id):
    """Flags a post for moderator review.

    :returns: True if flagged, false if removed.
              `CantFlagOwn` in case of error.
    """
    # Get the comment so we can check who the author is
    post = get_post(post_id)

    if post.get('user_id') != user_id:
        if not has_flagged(user_id, post_id):
            # Increment the flag count by one and store the user name
            r.zadd(k.POST_FLAGS.format(post_id), timestamp(), user_id)
            m.db.posts.update({'_id': post_id},
                              {'$inc': {'flags': 1}})
        else:
            raise AlreadyFlagged
    else:
        raise CantFlagOwn
예제 #22
0
파일: backend.py 프로젝트: adzilla/pjuu
def create_user(username, email, password):
    """Creates a user account

    """
    username = username.lower()
    email = email.lower()
    if check_username(username) and check_email(email) and \
       check_username_pattern(username) and check_email_pattern(email):
        # Create the user lookup keys. This LUA script ensures
        # that the name can not be taken at the same time causing a race
        # condition. This is also passed a UUID and will only return it if
        # successful
        uid = L.create_user(keys=[K.UID_USERNAME.format(username),
                                  K.UID_EMAIL.format(email)],
                            args=[get_uuid()])
        # Create user dictionary ready for HMSET only if uid is not None
        # This will only be None in the event of a race condition which we cant
        # really test for.
        if uid is not None:  # pragma: no branch
            user = {
                'uid': uid,
                'username': username,
                'email': email,
                'password': generate_password(password),
                'created': timestamp(),
                'last_login': -1,
                'active': 0,
                'banned': 0,
                'op': 0,
                'muted': 0,
                'about': "",
                'score': 0,
                'alerts_last_checked': 0
            }
            r.hmset(K.USER.format(uid), user)
            # Set the TTL for the user account
            r.expire(K.USER.format(uid), K.EXPIRE_24HRS)
            return uid

    # If none of this worked return nothing
    return None
예제 #23
0
파일: views.py 프로젝트: hnk/pjuu
def timeify_filter(time):
    """Takes integer epoch time and returns a DateTime string for display.

    If this conversion fails this function will return "Err"
    """
    try:
        # Please not that time is now a floating point value for extra
        # precision. We don't really need this when displaying it to the users
        # however.
        # Time can't be coverted directly to a int as it is a float point repr
        time = int(timestamp() - float(time))

        multiples = [
            (31536000, 'year'),
            (2592000, 'month'),
            (604800, 'week'),
            (86400, 'day'),
            (3600, 'hour'),
            (60, 'minute'),
            (1, 'second')
        ]

        # Find the closest time multiple since this post was posted
        # Work out the number of these multiples and return the string
        for multiple in multiples:
            if time < multiple[0]:
                continue
            number_of = math.floor(time / multiple[0])
            if number_of > 1:
                time_frame = multiple[1] + 's'
            else:
                time_frame = multiple[1]

            return "{0} {1} ago".format(int(number_of), time_frame)

        # Default return means that this was checked less than a second ago
        return "Less than a second ago"

    except (TypeError, ValueError):
        return "Err"
예제 #24
0
파일: backend.py 프로젝트: adzilla/pjuu
def create_comment(uid, pid, body):
    """Create a new comment

    """
    # Get a new UUID for the cid
    cid = get_uuid()

    # Form for comment hash
    comment = {
        'cid': cid,
        'uid': uid,
        'pid': pid,
        'body': body,
        'created': timestamp(),
        'score': 0
    }

    # Add comment
    r.hmset(K.COMMENT.format(cid), comment)
    # Add comment to posts comment list
    r.lpush(K.POST_COMMENTS.format(pid), cid)
    # Add comment to users comment list
    # This may seem redundant but it allows for perfect account deletion
    # Please see Issue #3 on Github
    r.lpush(K.USER_COMMENTS.format(uid), cid)

    # COMMENT ALERTING

    # Alert all subscribers to the post that a new comment has been added.
    # We do this before subscribing anyone new
    # Create alert manager and alert
    alert = CommentingAlert(uid, pid)

    subscribers = []
    # Iterate through subscribers and let them know about the comment
    for subscriber in get_subscribers(pid):
        # Ensure we don't get alerted for our own comments
        if subscriber != uid:
            subscribers.append(subscriber)

    # 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(uid, pid, SubscriptionReasons.COMMENTER)

    # TAGGING

    # Create alert
    alert = TaggingAlert(uid, pid)

    # Subscribe tagees
    tagees = parse_tags(body)
    tagees_to_alert = []
    for tagee in tagees:
        # Don't allow tagging yourself
        if tagee[0] != uid:
            subscribe(tagee[0], pid, SubscriptionReasons.TAGEE)
            tagees_to_alert.append(tagee[0])

    # Get an alert manager to notify all tagees
    AlertManager().alert(alert, tagees_to_alert)

    return cid
예제 #25
0
    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))
예제 #26
0
파일: alerts.py 프로젝트: pjuu/pjuu
 def __init__(self, user_id):
     self.alert_id = get_uuid()
     self.timestamp = timestamp()
     self.user_id = user_id
예제 #27
0
파일: __init__.py 프로젝트: pjuu/pjuu
 def gather_time():
     """This is used to measure the request time for each page"""
     if app.debug and not app.testing:  # pragma: no cover
         if request.endpoint != 'static':
             g.start_time = timestamp()
예제 #28
0
    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))
예제 #29
0
파일: backend.py 프로젝트: sydneyjd/pjuu
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
예제 #30
0
def reversable_filter(vote):
    """The time voted `vote` has to be newer than VOTE_TIMEOUT

    """
    vote = 0 if not vote else vote
    return abs(vote) + k.VOTE_TIMEOUT > timestamp()
예제 #31
0
파일: views.py 프로젝트: Velody/pjuu
def display_time(response):
    """This is will write the time to the console in DEBUG mode"""
    if app.debug and not app.testing:  # pragma: no cover
        print timestamp() - g.start_time, 'secs'
    return response
예제 #32
0
 def __init__(self, user_id):
     self.alert_id = get_uuid()
     self.timestamp = timestamp()
     self.user_id = user_id
예제 #33
0
파일: backend.py 프로젝트: sydneyjd/pjuu
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
예제 #34
0
파일: backend.py 프로젝트: sydneyjd/pjuu
def create_account(username, email, password):
    """Creates a new user account.

    :param username: The new users user name
    :type username: str
    :param email: The new users e-mail address
    :type email: str
    :param password: The new users password un-hashed
    :type password: str
    :returns: The UID of the new user
    :rtype: str or None

    """
    username = username.lower()
    email = email.lower()
    try:
        if check_username(username) and check_username_pattern(username) and \
                check_email(email) and check_email_pattern(email):
            # Get a new UUID for the user
            uid = get_uuid()

            user = {
                '_id':
                uid,
                'username':
                username.lower(),
                'email':
                email.lower(),
                'password':
                generate_password(password,
                                  method='pbkdf2:sha256:2000',
                                  salt_length=20),
                'created':
                timestamp(),
                'last_login':
                -1,
                'active':
                False,
                'banned':
                False,
                'op':
                False,
                'muted':
                False,
                'about':
                "",
                'score':
                0,
                'alerts_last_checked':
                -1,
                # Set the TTL for a newly created user, this has to be Datetime
                # object for MongoDB to recognise it. This is removed on
                # activation.
                'ttl':
                datetime.utcnow()
            }

            # Set all the tips for new users
            for tip_name in k.VALID_TIP_NAMES:
                user['tip_{}'.format(tip_name)] = True

            # Insert the new user in to Mongo. If this fails a None will be
            # returned
            result = m.db.users.insert(user)
            return uid if result else None
    except DuplicateKeyError:  # pragma: no cover
        # Oh no something went wrong. Pass over it. A None will be returned.
        pass

    return None
예제 #35
0
파일: views.py 프로젝트: buskirka/pjuu
def reversable_filter(vote):
    """The time voted `vote` has to be newer than VOTE_TIMEOUT

    """
    vote = 0 if not vote else vote
    return abs(vote) + k.VOTE_TIMEOUT > timestamp()
예제 #36
0
 def gather_time():
     """This is used to measure the request time for each page"""
     if app.debug and not app.testing:  # pragma: no cover
         if request.endpoint != 'static':
             g.start_time = timestamp()