Mercurial > public > sg101
view forums/unread.py @ 1200:b9514abc2a67
Initial commit of ssg101.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sat, 24 Jun 2023 16:06:51 -0500 |
parents | ee87ea74d46b |
children |
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 import logging from django.db import IntegrityError from forums.models import ForumLastVisit, TopicLastVisit, Topic, Forum THRESHOLD = datetime.timedelta(days=14) ####################################################################### 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 # There is a race condition and sometimes another thread # saves a record before we do; just log this if it happens. try: flv.save() except IntegrityError: logging.exception('get_forum_unread_status') 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, update_date__gt=flv.begin_date) tracked_topics = TopicLastVisit.objects.filter( user=user, topic__forum=forum, last_visit__gt=flv.begin_date).select_related('topic') # 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.topic.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 break else: forum.has_unread = True break else: # If we made it through the above loop without breaking out, # 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 # There is a race condition and sometimes another thread # saves a record before we do; just log this if it happens. try: flv.save() except IntegrityError: logging.exception('get_topic_unread_status') 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 ####################################################################### def get_unread_topics(user): """Returns a list of topics the user hasn't read yet.""" # This is only available to authenticated users if not user.is_authenticated(): return [] now = datetime.datetime.now() # Obtain list of forums the user can view forums = Forum.objects.forums_for_user(user) # Get forum last visit records for the forum ids flvs = ForumLastVisit.objects.filter(user=user, forum__in=forums).select_related() flvs = dict([(flv.forum.id, flv) for flv in flvs]) unread_topics = [] topics = Topic.objects.none() for forum in forums: # if the user hasn't visited the forum, create a last # visit record set to "now" if not forum.id in flvs: flv = ForumLastVisit(user=user, forum=forum, begin_date=now, end_date=now) flv.save() else: flv = flvs[forum.id] topics |= Topic.objects.filter(forum=forum, update_date__gt=flv.begin_date).order_by('-update_date').select_related( 'forum', 'user', 'last_post', 'last_post__user') if topics is not None: # get all topic last visit records for the topics of interest tlvs = TopicLastVisit.objects.filter(user=user, topic__in=topics) tlvs = dict([(tlv.topic.id, tlv) for tlv in tlvs]) for topic in topics: if topic.id in tlvs: tlv = tlvs[topic.id] if topic.update_date > tlv.last_visit: unread_topics.append(topic) else: unread_topics.append(topic) return unread_topics