bgneal@113: """
bgneal@113: This file contains routines for implementing the "has unread" feature. 
bgneal@113: Forums, topics, and posts are displayed with a visual indication if they have
bgneal@113: been read or not.
bgneal@113: """
bgneal@113: import datetime
bgneal@113: 
bgneal@113: from forums.models import ForumLastVisit, TopicLastVisit, Topic
bgneal@113: 
bgneal@113: 
bgneal@113: THRESHOLD = datetime.timedelta(days=7)
bgneal@113: 
bgneal@113: 
bgneal@113: def get_forum_unread_status(qs, user):
bgneal@113:     if not user.is_authenticated():
bgneal@113:         for forum in qs:
bgneal@113:             forum.has_unread = False
bgneal@113:         return
bgneal@113: 
bgneal@113:     now = datetime.datetime.now()
bgneal@113:     min_date = now - THRESHOLD
bgneal@113: 
bgneal@113:     # retrieve ForumLastVisit records in one SQL query
bgneal@113:     forum_ids = [forum.id for forum in qs]
bgneal@113:     flvs = ForumLastVisit.objects.filter(user=user, 
bgneal@113:             forum__in=forum_ids).select_related()
bgneal@113:     flvs = dict([(flv.forum.id, flv) for flv in flvs])
bgneal@113: 
bgneal@113:     for forum in qs:
bgneal@113:         # Edge case: forum has no posts
bgneal@113:         if forum.last_post is None:
bgneal@113:             forum.has_unread = False
bgneal@113:             continue
bgneal@113: 
bgneal@113:         # Get the ForumLastVisit record
bgneal@113:         if forum.id in flvs:
bgneal@113:             flv = flvs[forum.id]
bgneal@113:         else:
bgneal@113:             # One doesn't exist, create a default one for next time,
bgneal@113:             # mark it as having no unread topics, and bail.
bgneal@113:             flv = ForumLastVisit(user=user, forum=forum)
bgneal@113:             flv.begin_date = now
bgneal@113:             flv.end_date = now
bgneal@113:             flv.save()
bgneal@113:             forum.has_unread = False
bgneal@113:             continue
bgneal@113: 
bgneal@113:         # If the last visit record was too far in the past,
bgneal@113:         # catch that user up and mark as no unreads.
bgneal@113:         if now - flv.end_date > THRESHOLD:
bgneal@113:             forum.catchup(user, flv)
bgneal@113:             forum.has_unread = False
bgneal@113:             continue
bgneal@113: 
bgneal@113:         # Check the easy cases first. Check the last_post in the
bgneal@113:         # forum. If created after the end_date in our window, there
bgneal@113:         # are new posts. Likewise, if before the begin_date in our window,
bgneal@113:         # there are no new posts.
bgneal@113:         if forum.last_post.creation_date > flv.end_date:
bgneal@113:             forum.has_unread = True
bgneal@113:         elif forum.last_post.creation_date < flv.begin_date:
bgneal@113:             if not flv.is_caught_up():
bgneal@113:                 forum.catchup(user, flv)
bgneal@113:             forum.has_unread = False
bgneal@113:         else:
bgneal@113:             # Going to have to examine the topics in our window.
bgneal@113:             # First adjust our window if it is too old.
bgneal@113:             if now - flv.begin_date > THRESHOLD:
bgneal@113:                 flv.begin_date = min_date
bgneal@113:                 flv.save()
bgneal@113:                 TopicLastVisit.objects.filter(user=user, topic__forum=forum,
bgneal@113:                         last_visit__lt=min_date).delete()
bgneal@113: 
bgneal@113:             topics = Topic.objects.filter(creation_date__gt=flv.begin_date)
bgneal@113:             tracked_topics = TopicLastVisit.objects.filter(user=user,
bgneal@113:                     topic__forum=forum, last_visit__gt=flv.begin_date)
bgneal@113: 
bgneal@113:             # If the number of topics created since our window was started 
bgneal@113:             # is greater than the tracked topic records, then there are new
bgneal@113:             # posts.
bgneal@113:             if topics.count() > tracked_topics.count():
bgneal@113:                 forum.has_unread = True
bgneal@113:                 continue
bgneal@113: 
bgneal@113:             tracked_dict = dict([(t.id, t) for t in tracked_topics])
bgneal@113: 
bgneal@113:             for topic in topics:
bgneal@113:                 if topic.id in tracked_dict:
bgneal@113:                     if topic.update_date > tracked_dict[topic.id].last_visit:
bgneal@113:                         forum.has_unread = True
bgneal@113:                         continue
bgneal@113:                 else:
bgneal@113:                     forum.has_unread = True
bgneal@113:                     continue
bgneal@113: 
bgneal@113:             # If we made it through the above loop without continuing, then
bgneal@113:             # we are all caught up.
bgneal@113:             forum.catchup(user, flv)
bgneal@113:             forum.has_unread = False