Browse Source

#732: easy fix, but lossing pages overlap

Rafał Pitoń 8 years ago
parent
commit
48b843abfa

+ 3 - 13
misago/threads/paginator.py

@@ -1,4 +1,4 @@
-from math import ceil
+from math import floor, ceil
 
 
 from django.core.paginator import Paginator
 from django.core.paginator import Paginator
 from django.utils.functional import cached_property
 from django.utils.functional import cached_property
@@ -9,23 +9,13 @@ class PostsPaginator(Paginator):
     Paginator that returns that makes last item on page
     Paginator that returns that makes last item on page
     repeat as first item on next page.
     repeat as first item on next page.
     """
     """
-    @cached_property
-    def num_pages(self):
-        """
-        Returns the total number of pages.
-        """
-        if self.count == 0 and not self.allow_empty_first_page:
-            return 0
-        hits = max(1, self.count - self.orphans)
-        return int(ceil(hits / float(self.per_page - 1)))
-
     def page(self, number):
     def page(self, number):
         """
         """
         Returns a Page object for the given 1-based page number.
         Returns a Page object for the given 1-based page number.
         """
         """
         number = self.validate_number(number)
         number = self.validate_number(number)
-        bottom = (number - 1) * (self.per_page - 1)
-        top = bottom + self.per_page
+        bottom = (number - 1) * self.per_page
+        top = bottom + self.per_page + 1
         if top + self.orphans >= self.count:
         if top + self.orphans >= self.count:
             top = self.count
             top = self.count
         return self._get_page(self.object_list[bottom:top], number, self)
         return self._get_page(self.object_list[bottom:top], number, self)

+ 43 - 38
misago/threads/tests/test_paginator.py

@@ -9,54 +9,59 @@ class PostsPaginatorTests(TestCase):
         items = [i + 1 for i in range(30)]
         items = [i + 1 for i in range(30)]
 
 
         paginator = PostsPaginator(items, 5)
         paginator = PostsPaginator(items, 5)
-        self.assertEqual(paginator.num_pages, 8)
-
-        self.assertEqual(paginator.page(1).object_list, [1, 2, 3, 4, 5])
-        self.assertEqual(paginator.page(2).object_list, [5, 6, 7, 8, 9])
-        self.assertEqual(paginator.page(3).object_list, [9, 10, 11, 12, 13])
-        self.assertEqual(paginator.page(4).object_list, [13, 14, 15, 16, 17])
-        self.assertEqual(paginator.page(5).object_list, [17, 18, 19, 20, 21])
-        self.assertEqual(paginator.page(6).object_list, [21, 22, 23, 24, 25])
-        self.assertEqual(paginator.page(7).object_list, [25, 26, 27, 28, 29])
-        self.assertEqual(paginator.page(8).object_list, [29, 30])
+        self.assertEqual(self.get_paginator_items_list(paginator), [
+            [1, 2, 3, 4, 5, 6],
+            [6, 7, 8, 9, 10, 11],
+            [11, 12, 13, 14, 15, 16],
+            [16, 17, 18, 19, 20, 21],
+            [21, 22, 23, 24, 25, 26],
+            [26, 27, 28, 29, 30],
+        ])
 
 
     def test_paginator_orphans(self):
     def test_paginator_orphans(self):
         """paginator handles orphans"""
         """paginator handles orphans"""
         items = [i + 1 for i in range(20)]
         items = [i + 1 for i in range(20)]
 
 
         paginator = PostsPaginator(items, 8, 6)
         paginator = PostsPaginator(items, 8, 6)
-        self.assertEqual(paginator.num_pages, 2)
+        self.assertEqual(self.get_paginator_items_list(paginator), [
+            [1, 2, 3, 4, 5, 6, 7, 8, 9],
+            [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
+        ])
 
 
-        self.assertEqual(
-            paginator.page(1).object_list, [1, 2, 3, 4, 5, 6, 7, 8])
-        self.assertEqual(
-            paginator.page(2).object_list,
-            [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
+        paginator = PostsPaginator(items, 8, 5)
+        self.assertEqual(paginator.num_pages, 2)
+        self.assertEqual(self.get_paginator_items_list(paginator), [
+            [1, 2, 3, 4, 5, 6, 7, 8, 9],
+            [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
+        ])
 
 
-        paginator = PostsPaginator(items, 8, 4)
-        self.assertEqual(paginator.num_pages, 3)
+        paginator = PostsPaginator(items, 9, 3)
+        self.assertEqual(paginator.num_pages, 2)
+        self.assertEqual(self.get_paginator_items_list(paginator), [
+            [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+            [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
+        ])
 
 
-        self.assertEqual(
-            paginator.page(1).object_list, [1, 2, 3, 4, 5, 6, 7, 8])
-        self.assertEqual(
-            paginator.page(2).object_list, [8, 9, 10, 11, 12, 13, 14, 15])
-        self.assertEqual(
-            paginator.page(3).object_list, [15, 16, 17, 18, 19, 20])
+        # regression test for #732
+        items = [i + 1 for i in range(24)]
 
 
-        paginator = PostsPaginator(items, 8, 6)
-        self.assertEqual(paginator.num_pages, 2)
+        paginator = PostsPaginator(items, 18, 6)
+        self.assertEqual(paginator.num_pages, 1)
+        self.assertEqual(self.get_paginator_items_list(paginator), [
+            [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
+        ])
 
 
-        self.assertEqual(
-            paginator.page(1).object_list, [1, 2, 3, 4, 5, 6, 7, 8])
-        self.assertEqual(
-            paginator.page(2).object_list,
-            [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
+        # extra tests for catching issues in excessively long datasets
+        paginator = PostsPaginator([i + 1 for i in range(144)], 14, 6)
+        last_page = self.get_paginator_items_list(paginator)[-1]
+        self.assertEqual(last_page[-4:], [141, 142, 143, 144])
 
 
-        paginator = PostsPaginator(items, 6, 5)
-        self.assertEqual(paginator.num_pages, 3)
+        paginator = PostsPaginator([i + 1 for i in range(321)], 14, 6)
+        last_page = self.get_paginator_items_list(paginator)[-1]
+        self.assertEqual(last_page[-4:], [318, 319, 320, 321])
 
 
-        self.assertEqual(paginator.page(1).object_list, [1, 2, 3, 4, 5, 6])
-        self.assertEqual(paginator.page(2).object_list, [6, 7, 8, 9, 10, 11])
-        self.assertEqual(
-            paginator.page(3).object_list,
-            [11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
+    def get_paginator_items_list(self, paginator):
+        items_list = []
+        for page in paginator.page_range:
+            items_list.append(paginator.page(page).object_list)
+        return items_list

+ 5 - 8
misago/threads/viewmodels/posts.py

@@ -30,6 +30,10 @@ class ViewModel(object):
         posts = list(list_page.object_list)
         posts = list(list_page.object_list)
         posters = []
         posters = []
 
 
+        next_page_post = None
+        if list_page.has_next():
+            next_page_post, posts = posts[-1], next_page_post[:-1]
+
         for post in posts:
         for post in posts:
             post.category = thread.category
             post.category = thread.category
             post.thread = thread_model
             post.thread = thread_model
@@ -44,16 +48,9 @@ class ViewModel(object):
 
 
         # add events to posts
         # add events to posts
         if thread_model.has_events:
         if thread_model.has_events:
-            first_post = None
-            if list_page.has_previous():
-                first_post = posts[0]
-            last_post = None
-            if list_page.has_next():
-                last_post = posts[-1]
-
             events_limit = settings.MISAGO_EVENTS_PER_PAGE
             events_limit = settings.MISAGO_EVENTS_PER_PAGE
             posts += self.get_events_queryset(
             posts += self.get_events_queryset(
-                request, thread_model, events_limit, first_post, last_post)
+                request, thread_model, events_limit, posts[0], next_page_post)
 
 
             # sort both by pk
             # sort both by pk
             posts.sort(key=lambda p: p.pk)
             posts.sort(key=lambda p: p.pk)