Browse Source

fix #432 - rehashed readtracker

Rafał Pitoń 10 years ago
parent
commit
25035c2ded

+ 5 - 5
docs/developers/settings.rst

@@ -223,6 +223,11 @@ MISAGO_DYNAMIC_AVATAR_DRAWER
 Function used to create unique avatar for this user. Allows for customization of algorithm used to generate those.
 
 
+MISAGO_FRESH_CONTENT_PERIOD
+---------------------------
+Controls amount of data used in building "New threads" and "Threads with unread replies" lists. Active forums can try lowering this value while less active ones may wish to increase this number.
+
+
 MISAGO_MAILER_BATCH_SIZE
 ------------------------
 
@@ -260,11 +265,6 @@ MISAGO_RANKING_SIZE
 Maximum number of items on ranking page.
 
 
-MISAGO_READ_RECORD_LENGTH
--------------------------
-Controls amount of data used in resolving read/unread states of threads and forums. Any activity older than number of days specified in this setting is assumed to be read and not tracked anymore. Active forums can try lowering this value while less active ones may wish to increase this number.
-
-
 MISAGO_SENDFILE_HEADER
 ----------------------
 

+ 6 - 5
misago/conf/defaults.py

@@ -285,11 +285,12 @@ MISAGO_RANKING_LENGTH = 30
 MISAGO_RANKING_SIZE = 30
 
 
-# Controls amount of data used in resolving read/unread states of threads and
-# forums. Any activity older than number of days below is assumed to be read
-# and not tracked anymore. Active forums can try lowering this value while
-# less active ones may wish to increase this number
-MISAGO_READ_RECORD_LENGTH = 28
+# Controls amount of data used for new threads/replies lists
+# Only unread threads younger than number of days specified in this setting
+# will be considered fresh for "new threads" list
+# Only unread threads with last reply younger than number of days specified
+# there will be confidered fresh for "Threads with unread replies" list
+MISAGO_FRESH_CONTENT_PERIOD = 40
 
 
 # X-Sendfile

+ 2 - 6
misago/readtracker/dates.py

@@ -4,12 +4,8 @@ from django.conf import settings
 from django.utils import timezone
 
 
-def cutoff_date():
-    return timezone.now() - timedelta(days=settings.MISAGO_READ_RECORD_LENGTH)
-
-
-def is_date_tracked(date):
+def is_date_tracked(user, date):
     if date:
-        return date > cutoff_date()
+        return date > user.joined_on
     else:
         return False

+ 6 - 6
misago/readtracker/forumstracker.py

@@ -4,7 +4,7 @@ from django.utils import timezone
 from misago.threads.permissions import exclude_invisible_threads
 
 from misago.readtracker import signals
-from misago.readtracker.dates import cutoff_date, is_date_tracked
+from misago.readtracker.dates import is_date_tracked
 
 
 __all__ = ['make_read_aware', 'sync_record']
@@ -17,7 +17,7 @@ def make_read_aware(user, forums):
 
     forums_dict = {}
     for forum in forums:
-        forum.is_read = not is_date_tracked(forum.last_post_on)
+        forum.is_read = not is_date_tracked(user, forum.last_post_on)
         forums_dict[forum.pk] = forum
 
     for record in user.forumread_set.filter(forum__in=forums_dict.keys()):
@@ -32,13 +32,13 @@ def make_read(forums):
 
 
 def sync_record(user, forum):
-    recorded_threads = forum.thread_set.filter(last_post_on__gt=cutoff_date())
+    recorded_threads = forum.thread_set.filter(last_post_on__gt=user.joined_on)
     recorded_threads = exclude_invisible_threads(user, forum, recorded_threads)
 
     all_threads_count = recorded_threads.count()
 
     read_threads = user.threadread_set.filter(
-        forum=forum, last_read_on__gt=cutoff_date())
+        forum=forum, last_read_on__gt=user.joined_on)
     read_threads_count = read_threads.filter(
         thread__last_post_on__lte=F("last_read_on")).count()
 
@@ -53,13 +53,13 @@ def sync_record(user, forum):
         if forum_is_read:
             forum_record.last_cleared_on = forum_record.last_updated_on
         else:
-            forum_record.last_cleared_on = cutoff_date()
+            forum_record.last_cleared_on = user.joined_on
         forum_record.save(update_fields=['last_updated_on', 'last_cleared_on'])
     except IndexError:
         if forum_is_read:
             cleared_on = timezone.now()
         else:
-            cleared_on = cutoff_date()
+            cleared_on = user.joined_on
 
         forum_record = user.forumread_set.create(
             forum=forum,

+ 13 - 9
misago/readtracker/tests/test_dates.py

@@ -3,17 +3,21 @@ from datetime import timedelta
 from django.test import TestCase
 from django.utils import timezone
 
-from misago.readtracker.dates import cutoff_date, is_date_tracked
+from misago.readtracker.dates import is_date_tracked
 
 
-class ReadTrackerDatesTests(TestCase):
-    def test_cutoff_date(self):
-        """cutoff_date returns cut off date"""
-        cutoff = cutoff_date()
-        self.assertTrue(cutoff < timezone.now())
+class MockUser(object):
+    def __init__(self):
+        self.joined_on = timezone.now()
+
 
+class ReadTrackerDatesTests(TestCase):
     def test_is_date_tracked(self):
         """is_date_tracked validates dates"""
-        self.assertFalse(is_date_tracked(None))
-        self.assertFalse(is_date_tracked(cutoff_date() - timedelta(seconds=1)))
-        self.assertTrue(is_date_tracked(cutoff_date() + timedelta(minutes=1)))
+        self.assertFalse(is_date_tracked(MockUser(), None))
+
+        past_date = timezone.now() - timedelta(minutes=10)
+        self.assertFalse(is_date_tracked(MockUser(), past_date))
+
+        future_date = timezone.now() + timedelta(minutes=10)
+        self.assertTrue(is_date_tracked(MockUser(), future_date))

+ 30 - 21
misago/readtracker/tests/test_readtracker.py

@@ -10,7 +10,6 @@ from misago.threads import testutils
 from misago.users.models import AnonymousUser
 
 from misago.readtracker import forumstracker, threadstracker
-from misago.readtracker.dates import cutoff_date
 
 
 class ReadTrackerTests(TestCase):
@@ -54,14 +53,14 @@ class ForumsTrackerTests(ReadTrackerTests):
 
     def test_make_read_aware_sets_read_flag_for_forum_with_old_thread(self):
         """make_read_aware sets read flag on forum with old thread"""
-        self.forum.last_post_on = cutoff_date() - timedelta(days=1)
+        self.forum.last_post_on = self.user.joined_on - timedelta(days=1)
 
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertTrue(self.forum.is_read)
 
     def test_make_read_aware_sets_unread_flag_for_forum_with_new_thread(self):
         """make_read_aware sets unread flag on forum with new thread"""
-        self.forum.last_post_on = cutoff_date() + timedelta(days=1)
+        self.forum.last_post_on = self.user.joined_on + timedelta(days=1)
 
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertFalse(self.forum.is_read)
@@ -80,7 +79,7 @@ class ForumsTrackerTests(ReadTrackerTests):
         sync_record sets read flag on forum with old thread,
         then changes flag to unread when new reply is posted
         """
-        self.post_thread(cutoff_date() - timedelta(days=1))
+        self.post_thread(self.user.joined_on - timedelta(days=1))
 
         add_acl(self.user, self.forums)
         forumstracker.sync_record(self.user, self.forum)
@@ -89,7 +88,7 @@ class ForumsTrackerTests(ReadTrackerTests):
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertTrue(self.forum.is_read)
 
-        thread = self.post_thread(cutoff_date() + timedelta(days=1))
+        thread = self.post_thread(self.user.joined_on + timedelta(days=1))
         forumstracker.sync_record(self.user, self.forum)
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertFalse(self.forum.is_read)
@@ -99,7 +98,7 @@ class ForumsTrackerTests(ReadTrackerTests):
         sync_record sets read flag on forum with old thread,
         then keeps flag to unread when new reply is posted
         """
-        self.post_thread(cutoff_date() + timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
 
         add_acl(self.user, self.forums)
         forumstracker.sync_record(self.user, self.forum)
@@ -108,16 +107,16 @@ class ForumsTrackerTests(ReadTrackerTests):
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertFalse(self.forum.is_read)
 
-        self.post_thread(cutoff_date() + timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
         forumstracker.sync_record(self.user, self.forum)
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertFalse(self.forum.is_read)
 
     def test_sync_record_for_forum_with_deleted_threads(self):
         """unread forum reverts to read after its emptied"""
-        self.post_thread(cutoff_date() + timedelta(days=1))
-        self.post_thread(cutoff_date() + timedelta(days=1))
-        self.post_thread(cutoff_date() + timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
 
         add_acl(self.user, self.forums)
         forumstracker.sync_record(self.user, self.forum)
@@ -133,10 +132,10 @@ class ForumsTrackerTests(ReadTrackerTests):
 
     def test_sync_record_for_forum_with_many_threads(self):
         """sync_record sets unread flag on forum with many threads"""
-        self.post_thread(cutoff_date() + timedelta(days=1))
-        self.post_thread(cutoff_date() - timedelta(days=1))
-        self.post_thread(cutoff_date() + timedelta(days=1))
-        self.post_thread(cutoff_date() - timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
+        self.post_thread(self.user.joined_on - timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
+        self.post_thread(self.user.joined_on - timedelta(days=1))
 
         add_acl(self.user, self.forums)
         forumstracker.sync_record(self.user, self.forum)
@@ -145,7 +144,7 @@ class ForumsTrackerTests(ReadTrackerTests):
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertFalse(self.forum.is_read)
 
-        self.post_thread(cutoff_date() + timedelta(days=1))
+        self.post_thread(self.user.joined_on + timedelta(days=1))
         forumstracker.sync_record(self.user, self.forum)
         forumstracker.make_read_aware(self.user, self.forums)
         self.assertFalse(self.forum.is_read)
@@ -156,13 +155,13 @@ class ThreadsTrackerTests(ReadTrackerTests):
         super(ThreadsTrackerTests, self).setUp()
 
         self.thread = self.post_thread(timezone.now() - timedelta(days=10))
-        self.reply_thread()
 
     def reply_thread(self, is_hidden=False, is_moderated=False):
         self.post = testutils.reply_thread(
             thread=self.thread,
             is_hidden=is_hidden,
-            is_moderated=is_moderated)
+            is_moderated=is_moderated,
+            posted_on=timezone.now())
         return self.post
 
     def test_thread_read_for_guest(self):
@@ -170,16 +169,26 @@ class ThreadsTrackerTests(ReadTrackerTests):
         threadstracker.make_read_aware(self.anon, self.thread)
         self.assertTrue(self.thread.is_read)
 
+        self.reply_thread()
         threadstracker.make_read_aware(self.anon, [self.thread])
         self.assertTrue(self.thread.is_read)
 
-    def test_thread_unread_for_user(self):
-        """thread is unread for user"""
+    def test_thread_read_for_user(self):
+        """thread is read for user"""
+        threadstracker.make_read_aware(self.user, self.thread)
+        self.assertTrue(self.thread.is_read)
+
+    def test_thread_replied_unread_for_user(self):
+        """replied thread is unread for user"""
+        self.reply_thread(self.thread)
+
         threadstracker.make_read_aware(self.user, self.thread)
         self.assertFalse(self.thread.is_read)
 
-    def test_thread_read(self):
+    def _test_thread_read(self):
         """thread read flag is set for user, then its set as unread by reply"""
+        self.reply_thread(self.thread)
+
         add_acl(self.user, self.forums)
         threadstracker.make_read_aware(self.user, self.thread)
         self.assertFalse(self.thread.is_read)
@@ -202,7 +211,7 @@ class ThreadsTrackerTests(ReadTrackerTests):
         self.assertFalse(self.forum.is_read)
 
         posts = [post for post in self.thread.post_set.order_by('id')]
-        threadstracker.make_posts_read_aware(self.thread, posts)
+        threadstracker.make_posts_read_aware(self.user, self.thread, posts)
 
         for post in posts[:-1]:
             self.assertTrue(post.is_read)

+ 16 - 16
misago/readtracker/threadstracker.py

@@ -1,5 +1,5 @@
 from misago.readtracker import forumstracker, signals
-from misago.readtracker.dates import cutoff_date, is_date_tracked
+from misago.readtracker.dates import is_date_tracked
 
 
 __all__ = ['make_read_aware', 'read_thread']
@@ -19,7 +19,7 @@ def make_threads_read_aware(user, threads):
 
     threads_dict = {}
     for thread in threads:
-        thread.is_read = not is_date_tracked(thread.last_post_on)
+        thread.is_read = not is_date_tracked(user, thread.last_post_on)
         if thread.is_read:
             thread.unread_replies = 0
         else:
@@ -44,7 +44,7 @@ def make_read(threads):
 
 def make_thread_read_aware(user, thread):
     thread.is_read = True
-    if user.is_authenticated() and is_date_tracked(thread.last_post_on):
+    if user.is_authenticated() and is_date_tracked(user, thread.last_post_on):
         try:
             record = user.threadread_set.filter(thread=thread).all()[0]
             thread.last_read_on = record.last_read_on
@@ -53,10 +53,10 @@ def make_thread_read_aware(user, thread):
         except IndexError:
             thread.read_record = None
             thread.is_read = False
-            thread.last_read_on = cutoff_date()
+            thread.last_read_on = user.joined_on
 
 
-def make_posts_read_aware(thread, posts):
+def make_posts_read_aware(user, thread, posts):
     try:
         is_thread_read = thread.is_read
     except AttributeError:
@@ -68,7 +68,7 @@ def make_posts_read_aware(thread, posts):
             post.is_read = True
     else:
         for post in posts:
-            if is_date_tracked(post.updated_on):
+            if is_date_tracked(user, post.updated_on):
                 post.is_read = post.updated_on <= thread.last_read_on
             else:
                 post.is_read = True
@@ -80,16 +80,6 @@ def read_thread(user, thread, last_read_reply):
             sync_record(user, thread, last_read_reply)
 
 
-def count_read_replies(user, thread, last_read_reply):
-    if last_read_reply.updated_on >= thread.last_read_on:
-        return 0
-    else:
-        last_reply_date = last_read_reply.last_read_on
-        queryset = thread.post_set.filter(last_read_on__lte=last_reply_date)
-        queryset = queryset.filter(is_moderated=False)
-        return queryset.count()
-
-
 def sync_record(user, thread, last_read_reply):
     read_replies = count_read_replies(user, thread, last_read_reply)
     if thread.read_record:
@@ -106,3 +96,13 @@ def sync_record(user, thread, last_read_reply):
     if last_read_reply.updated_on == thread.last_post_on:
         signals.thread_read.send(sender=user, thread=thread)
         forumstracker.sync_record(user, thread.forum)
+
+
+def count_read_replies(user, thread, last_read_reply):
+    if last_read_reply.updated_on >= thread.last_read_on:
+        return 0
+    else:
+        last_reply_date = last_read_reply.last_read_on
+        queryset = thread.post_set.filter(last_read_on__lte=last_reply_date)
+        queryset = queryset.filter(is_moderated=False)
+        return queryset.count()

+ 7 - 0
misago/static/misago/css/misago/threadslists.less

@@ -42,8 +42,15 @@
         //==
         .item-title {
           font-size: @font-size-large;
+
+          .opacity(0.7);
         }
 
+        &.new {
+          .item-title {
+            .opacity(1);
+          }
+        }
 
         // Thread last reply
         //

+ 1 - 1
misago/threads/views/generic/thread.py

@@ -64,7 +64,7 @@ class ThreadView(ViewBase):
         threadstracker.make_read_aware(request.user, thread)
 
         page, posts = self.get_posts(request.user, forum, thread, kwargs)
-        threadstracker.make_posts_read_aware(thread, posts)
+        threadstracker.make_posts_read_aware(request.user, thread, posts)
         threadstracker.read_thread(request.user, thread, posts[-1])
 
         return self.render(request, {