Browse Source

#410: threads move/merge/sync api + tests

Rafał Pitoń 10 years ago
parent
commit
63ba5ec61b

+ 1 - 1
misago/forums/models.py

@@ -101,7 +101,7 @@ class Forum(MPTTModel):
         acl_version.invalidate()
         return super(Forum, self).delete(*args, **kwargs)
 
-    def recount(self):
+    def synchronize(self):
         counted_criteria = {'is_hidden':False, 'is_moderated':False}
         self.threads = self.thread_set.filter(**counted_criteria).count()
         self.posts = self.post_set.filter(**counted_criteria).count()

+ 25 - 11
misago/threads/models/thread.py

@@ -57,31 +57,42 @@ class Thread(models.Model):
             ['forum', 'weight', 'replies'],
         ]
 
+    def delete(self, *args, **kwargs):
+        from misago.threads.signals import delete_thread
+        delete_thread.send(sender=self)
+
+        super(Thread, self).delete(*args, **kwargs)
+
+    def merge(self, other_thread):
+        from misago.threads.signals import merge_thread
+        merge_thread.send(sender=self, other_thread=other_thread)
+
     def move(self, new_forum):
-        pass
+        from misago.threads.signals import move_thread
 
-    def merge(self, thread):
-        pass
+        self.forum = new_forum
+        move_thread.send(sender=self)
 
-    def recount(self):
+    def synchronize(self):
         counted_criteria = {'is_hidden':False, 'is_moderated':False}
         self.replies = self.post_set.filter(**counted_criteria).count()
         if self.replies > 0:
             self.replies -= 1
 
-        reported_posts_count = self.post_set.filter(is_reported=True).count()
-        self.has_reported_posts = reported_posts_count > 0
+        reported_post_qs = self.post_set.filter(is_reported=True)[:1]
+        self.has_reported_posts = reported_post_qs.exists()
 
-        moderated_posts_count = self.post_set.filter(is_moderated=True).count()
-        self.has_moderated_posts = moderated_posts_count > 0
+        moderated_post_qs = self.post_set.filter(is_moderated=True)[:1]
+        self.has_moderated_posts = moderated_post_qs.exists()
 
-        hidden_posts_count = self.post_set.filter(is_hidden=True).count()
-        self.has_hidden_posts = hidden_posts_count > 0
+        hidden_post_qs = self.post_set.filter(is_hidden=True)[:1]
+        self.has_hidden_posts = hidden_post_qs.exists()
 
         first_post = self.post_set.order_by('id')[:1][0]
         self.set_first_post(first_post)
 
-        last_post = self.post_set.filter(**counted_criteria).order_by('id')[:1]
+        last_post_qs = self.post_set.filter(**counted_criteria).order_by('-id')
+        last_post = last_post_qs[:1]
         if last_post:
             self.set_last_post(last_post[0])
         else:
@@ -135,6 +146,9 @@ class Thread(models.Model):
         else:
             self.starter_slug = slugify(post.poster_name)
 
+        self.is_moderated = post.is_moderated
+        self.is_hidden = post.is_hidden
+
     def set_last_post(self, post):
         self.last_post_on = post.posted_on
         self.last_post = post

+ 15 - 3
misago/threads/signals.py

@@ -7,13 +7,25 @@ from misago.forums.models import Forum
 from misago.threads.models import Thread, Post
 
 
-move_thread = django.dispatch.Signal()
 delete_thread = django.dispatch.Signal()
+merge_thread = django.dispatch.Signal()
+move_thread = django.dispatch.Signal()
 
 
 """
 Signal handlers
 """
+@receiver(merge_thread)
+def merge_threads_posts(sender, **kwargs):
+    other_thread = kwargs['other_thread']
+    other_thread.post_set.update(forum=sender.forum, thread=sender)
+
+
+@receiver(move_thread)
+def move_thread_posts(sender, **kwargs):
+    sender.post_set.update(forum=sender.forum)
+
+
 from misago.forums.signals import delete_forum_content, move_forum_content
 @receiver(delete_forum_content)
 def delete_forum_threads(sender, **kwargs):
@@ -46,12 +58,12 @@ def delete_user_threads(sender, **kwargs):
     if recount_threads:
         changed_threads_qs = Thread.objects.filter(id__in=recount_threads)
         for thread in batch_update(changed_threads_qs, 50):
-            thread.recount()
+            thread.synchronize()
             thread.save()
 
     if recount_forums:
         for forum in Forum.objects.filter(id__in=recount_forums):
-            forum.recount()
+            forum.synchronize()
             forum.save()
 
 

+ 262 - 0
misago/threads/tests/test_thread_model.py

@@ -0,0 +1,262 @@
+from datetime import timedelta
+
+from django.contrib.auth import get_user_model
+from django.test import TestCase
+from django.utils import timezone
+
+from misago.forums.models import Forum
+
+from misago.threads.models import Thread, Post
+
+
+class ThreadModelTests(TestCase):
+    def setUp(self):
+        datetime = timezone.now()
+
+        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.thread = Thread(
+            forum=self.forum,
+            weight=0,
+            started_on=datetime,
+            starter_name='Tester',
+            starter_slug='tester',
+            last_post_on=datetime,
+            last_poster_name='Tester',
+            last_poster_slug='tester')
+
+        self.thread.set_title("Test thread")
+        self.thread.save()
+
+        post = Post.objects.create(
+            forum=self.forum,
+            thread=self.thread,
+            poster_name='Tester',
+            poster_ip='127.0.0.1',
+            original="Hello! I am test message!",
+            parsed="<p>Hello! I am test message!</p>",
+            checksum="nope",
+            posted_on=datetime,
+            updated_on=datetime)
+
+        self.thread.first_post = post
+        self.thread.last_post = post
+        self.thread.save()
+
+    def test_synchronize(self):
+        """synchronize method updates thread data to reflect its contents"""
+        User = get_user_model()
+        user = User.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
+
+        self.assertEqual(self.thread.replies, 0)
+
+        datetime = timezone.now() + timedelta(5)
+        post = Post.objects.create(
+            forum=self.forum,
+            thread=self.thread,
+            poster=user,
+            poster_name=user.username,
+            poster_ip='127.0.0.1',
+            original="Hello! I am test message!",
+            parsed="<p>Hello! I am test message!</p>",
+            checksum="nope",
+            posted_on=datetime,
+            updated_on=datetime)
+
+        # first sync call, updates last thread
+        self.thread.synchronize()
+
+        self.assertEqual(self.thread.last_post, post)
+        self.assertEqual(self.thread.last_post_on, post.posted_on)
+        self.assertEqual(self.thread.last_poster, user)
+        self.assertEqual(self.thread.last_poster_name, user.username)
+        self.assertEqual(self.thread.last_poster_slug, user.slug)
+        self.assertFalse(self.thread.has_reported_posts)
+        self.assertFalse(self.thread.has_moderated_posts)
+        self.assertFalse(self.thread.has_hidden_posts)
+        self.assertEqual(self.thread.replies, 1)
+
+        # add moderated post
+        moderated_post = Post.objects.create(
+            forum=self.forum,
+            thread=self.thread,
+            poster=user,
+            poster_name=user.username,
+            poster_ip='127.0.0.1',
+            original="Hello! I am test message!",
+            parsed="<p>Hello! I am test message!</p>",
+            checksum="nope",
+            posted_on=datetime + timedelta(5),
+            updated_on=datetime + timedelta(5),
+            is_moderated=True)
+
+        self.thread.synchronize()
+        self.assertEqual(self.thread.last_post, post)
+        self.assertEqual(self.thread.last_post_on, post.posted_on)
+        self.assertEqual(self.thread.last_poster, user)
+        self.assertEqual(self.thread.last_poster_name, user.username)
+        self.assertEqual(self.thread.last_poster_slug, user.slug)
+        self.assertFalse(self.thread.has_reported_posts)
+        self.assertTrue(self.thread.has_moderated_posts)
+        self.assertFalse(self.thread.has_hidden_posts)
+        self.assertEqual(self.thread.replies, 1)
+
+        # add hidden post
+        hidden_post = Post.objects.create(
+            forum=self.forum,
+            thread=self.thread,
+            poster=user,
+            poster_name=user.username,
+            poster_ip='127.0.0.1',
+            original="Hello! I am test message!",
+            parsed="<p>Hello! I am test message!</p>",
+            checksum="nope",
+            posted_on=datetime + timedelta(10),
+            updated_on=datetime + timedelta(10),
+            is_hidden=True)
+
+        self.thread.synchronize()
+        self.assertEqual(self.thread.last_post, post)
+        self.assertEqual(self.thread.last_post_on, post.posted_on)
+        self.assertEqual(self.thread.last_poster, user)
+        self.assertEqual(self.thread.last_poster_name, user.username)
+        self.assertEqual(self.thread.last_poster_slug, user.slug)
+        self.assertFalse(self.thread.has_reported_posts)
+        self.assertTrue(self.thread.has_moderated_posts)
+        self.assertTrue(self.thread.has_hidden_posts)
+        self.assertEqual(self.thread.replies, 1)
+
+        # unhide post
+        hidden_post.is_hidden = False
+        hidden_post.save()
+
+        # last post changed to unhidden one
+        self.thread.synchronize()
+        self.assertEqual(self.thread.last_post, hidden_post)
+        self.assertEqual(self.thread.last_post_on, hidden_post.posted_on)
+        self.assertEqual(self.thread.last_poster, user)
+        self.assertEqual(self.thread.last_poster_name, user.username)
+        self.assertEqual(self.thread.last_poster_slug, user.slug)
+        self.assertFalse(self.thread.has_reported_posts)
+        self.assertTrue(self.thread.has_moderated_posts)
+        self.assertFalse(self.thread.has_hidden_posts)
+        self.assertEqual(self.thread.replies, 2)
+
+        # unmoderate post
+        moderated_post.is_moderated = False
+        moderated_post.save()
+
+        # last post not changed, but flags and count did
+        self.thread.synchronize()
+        self.assertEqual(self.thread.last_post, hidden_post)
+        self.assertEqual(self.thread.last_post_on, hidden_post.posted_on)
+        self.assertEqual(self.thread.last_poster, user)
+        self.assertEqual(self.thread.last_poster_name, user.username)
+        self.assertEqual(self.thread.last_poster_slug, user.slug)
+        self.assertFalse(self.thread.has_reported_posts)
+        self.assertFalse(self.thread.has_moderated_posts)
+        self.assertFalse(self.thread.has_hidden_posts)
+        self.assertEqual(self.thread.replies, 3)
+
+    def test_set_first_post(self):
+        """set_first_post sets first post and poster data on thread"""
+        User = get_user_model()
+        user = User.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
+
+        datetime = timezone.now() + timedelta(5)
+
+        post = Post.objects.create(
+            forum=self.forum,
+            thread=self.thread,
+            poster=user,
+            poster_name=user.username,
+            poster_ip='127.0.0.1',
+            original="Hello! I am test message!",
+            parsed="<p>Hello! I am test message!</p>",
+            checksum="nope",
+            posted_on=datetime,
+            updated_on=datetime)
+
+        self.thread.set_first_post(post)
+        self.assertEqual(self.thread.first_post, post)
+        self.assertEqual(self.thread.started_on, post.posted_on)
+        self.assertEqual(self.thread.starter, user)
+        self.assertEqual(self.thread.starter_name, user.username)
+        self.assertEqual(self.thread.starter_slug, user.slug)
+
+    def test_set_last_post(self):
+        """set_last_post sets first post and poster data on thread"""
+        User = get_user_model()
+        user = User.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
+
+        datetime = timezone.now() + timedelta(5)
+
+        post = Post.objects.create(
+            forum=self.forum,
+            thread=self.thread,
+            poster=user,
+            poster_name=user.username,
+            poster_ip='127.0.0.1',
+            original="Hello! I am test message!",
+            parsed="<p>Hello! I am test message!</p>",
+            checksum="nope",
+            posted_on=datetime,
+            updated_on=datetime)
+
+        self.thread.set_last_post(post)
+        self.assertEqual(self.thread.last_post, post)
+        self.assertEqual(self.thread.last_post_on, post.posted_on)
+        self.assertEqual(self.thread.last_poster, user)
+        self.assertEqual(self.thread.last_poster_name, user.username)
+        self.assertEqual(self.thread.last_poster_slug, user.slug)
+
+    def test_move(self):
+        """move(new_forum) moves thread to other forum"""
+        # pick category instead of forum (so we don't have to create one)
+        new_forum = Forum.objects.filter(role="category")[:1][0]
+
+        self.thread.move(new_forum)
+        self.assertEqual(self.thread.forum, new_forum)
+
+        for post in self.thread.post_set.all():
+            self.assertEqual(post.forum_id, new_forum.id)
+
+    def test_merge(self):
+        """merge(other_thread) moves other thread content to this thread"""
+        datetime = timezone.now() + timedelta(5)
+
+        other_thread = Thread(
+            forum=self.forum,
+            weight=0,
+            started_on=datetime,
+            starter_name='Tester',
+            starter_slug='tester',
+            last_post_on=datetime,
+            last_poster_name='Tester',
+            last_poster_slug='tester')
+
+        other_thread.set_title("Other thread")
+        other_thread.save()
+
+        post = Post.objects.create(
+            forum=self.forum,
+            thread=other_thread,
+            poster_name='Admin',
+            poster_ip='127.0.0.1',
+            original="Hello! I am other message!",
+            parsed="<p>Hello! I am other message!</p>",
+            checksum="nope",
+            posted_on=datetime,
+            updated_on=datetime)
+
+        other_thread.first_post = post
+        other_thread.last_post = post
+        other_thread.save()
+
+        self.thread.merge(other_thread)
+
+        self.thread.synchronize()
+        self.assertEqual(self.thread.replies, 1)
+        self.assertEqual(self.thread.last_post, post)
+        self.assertEqual(self.thread.last_post_on, post.posted_on)
+        self.assertEqual(self.thread.last_poster_name, "Admin")
+        self.assertEqual(self.thread.last_poster_slug, "admin")