view gpp/forums/unread.py @ 160:2eb3984ccb15

Implement #45, add a who's online feature for the forums. Created middleware that caches usernames and guest session ids in the cache. Added a tag that displays this info.
author Brian Neal <bgneal@gmail.com>
date Tue, 22 Dec 2009 02:08:05 +0000
parents 35a0e6345815
children cf9f9d4c4d54
line wrap: on
line source
"""
This file contains routines for implementing the "has unread" feature. 
Forums, topics, and posts are displayed with a visual indication if they have
been read or not.
"""
import datetime

from forums.models import ForumLastVisit, TopicLastVisit, Topic


THRESHOLD = datetime.timedelta(days=7)

#######################################################################

def get_forum_unread_status(qs, user):
    if not user.is_authenticated():
        for forum in qs:
            forum.has_unread = False
        return

    now = datetime.datetime.now()
    min_date = now - THRESHOLD

    # retrieve ForumLastVisit records in one SQL query
    forum_ids = [forum.id for forum in qs]
    flvs = ForumLastVisit.objects.filter(user=user, 
            forum__in=forum_ids).select_related()
    flvs = dict([(flv.forum.id, flv) for flv in flvs])

    for forum in qs:
        # Edge case: forum has no posts
        if forum.last_post is None:
            forum.has_unread = False
            continue

        # Get the ForumLastVisit record
        if forum.id in flvs:
            flv = flvs[forum.id]
        else:
            # One doesn't exist, create a default one for next time,
            # mark it as having no unread topics, and bail.
            flv = ForumLastVisit(user=user, forum=forum)
            flv.begin_date = now
            flv.end_date = now
            flv.save()
            forum.has_unread = False
            continue

        # If the last visit record was too far in the past,
        # catch that user up and mark as no unreads.
        if now - flv.end_date > THRESHOLD:
            forum.catchup(user, flv)
            forum.has_unread = False
            continue

        # Check the easy cases first. Check the last_post in the
        # forum. If created after the end_date in our window, there
        # are new posts. Likewise, if before the begin_date in our window,
        # there are no new posts.
        if forum.last_post.creation_date > flv.end_date:
            forum.has_unread = True
        elif forum.last_post.creation_date < flv.begin_date:
            if not flv.is_caught_up():
                forum.catchup(user, flv)
            forum.has_unread = False
        else:
            # Going to have to examine the topics in our window.
            # First adjust our window if it is too old.
            if now - flv.begin_date > THRESHOLD:
                flv.begin_date = min_date
                flv.save()
                TopicLastVisit.objects.filter(user=user, topic__forum=forum,
                        last_visit__lt=min_date).delete()

            topics = Topic.objects.filter(forum=forum, 
                    creation_date__gt=flv.begin_date)
            tracked_topics = TopicLastVisit.objects.filter(user=user,
                    topic__forum=forum, last_visit__gt=flv.begin_date)

            # If the number of topics created since our window was started 
            # is greater than the tracked topic records, then there are new
            # posts.
            if topics.count() > tracked_topics.count():
                forum.has_unread = True
                continue

            tracked_dict = dict([(t.id, t) for t in tracked_topics])

            for topic in topics:
                if topic.id in tracked_dict:
                    if topic.update_date > tracked_dict[topic.id].last_visit:
                        forum.has_unread = True
                        continue
                else:
                    forum.has_unread = True
                    continue

            # If we made it through the above loop without continuing, then
            # we are all caught up.
            forum.catchup(user, flv)
            forum.has_unread = False

#######################################################################

def get_topic_unread_status(forum, topics, user):

    # Edge case: no topics 
    if forum.last_post is None:
        return

    # This service isn't provided to unauthenticated users
    if not user.is_authenticated():
        for topic in topics:
            topic.has_unread = False
        return

    now = datetime.datetime.now()

    # Get the ForumLastVisit record
    try:
        flv = ForumLastVisit.objects.get(forum=forum, user=user)
    except ForumLastVisit.DoesNotExist:
        # One doesn't exist, create a default one for next time,
        # mark it as having no unread topics, and bail.
        flv = ForumLastVisit(user=user, forum=forum)
        flv.begin_date = now
        flv.end_date = now
        flv.save()
        for topic in topics:
            topic.has_unread = False
        return

    # Are all the posts before our window? If so, all have been read.
    if forum.last_post.creation_date < flv.begin_date:
        for topic in topics:
            topic.has_unread = False
        return

    topic_ids = [topic.id for topic in topics]
    tlvs = TopicLastVisit.objects.filter(user=user, topic__id__in=topic_ids)
    tlvs = dict([(tlv.topic.id, tlv) for tlv in tlvs])

    # Otherwise we have to go through the topics one by one:
    for topic in topics:
        if topic.update_date < flv.begin_date:
            topic.has_unread = False
        elif topic.update_date > flv.end_date:
            topic.has_unread = True
        elif topic.id in tlvs:
            topic.has_unread = topic.update_date > tlvs[topic.id].last_visit
        else:
            topic.has_unread = True

#######################################################################

def get_post_unread_status(topic, posts, user):
    # This service isn't provided to unauthenticated users
    if not user.is_authenticated():
        for post in posts:
            post.unread = False
        return

    # Get the ForumLastVisit record
    try:
        flv = ForumLastVisit.objects.get(forum=topic.forum, user=user)
    except ForumLastVisit.DoesNotExist:
        # One doesn't exist, all posts are old.
        for post in posts:
            post.unread = False
        return

    # Are all the posts before our window? If so, all have been read.
    if topic.last_post.creation_date < flv.begin_date:
        for post in posts:
            post.unread = False
        return

    # Do we have a topic last visit record for this topic?

    try:
        tlv = TopicLastVisit.objects.get(user=user, topic=topic)
    except TopicLastVisit.DoesNotExist:
        # No we don't, we could be all caught up, or all are new
        for post in posts:
            post.unread = post.creation_date > flv.end_date
    else:
        for post in posts:
            post.unread = post.creation_date > tlv.last_visit