Browse Source

#630: PostsPaginator

Rafał Pitoń 8 years ago
parent
commit
9f1178669f
2 changed files with 96 additions and 0 deletions
  1. 34 0
      misago/threads/paginator.py
  2. 62 0
      misago/threads/tests/test_paginator.py

+ 34 - 0
misago/threads/paginator.py

@@ -0,0 +1,34 @@
+from math import ceil
+
+from django.core.paginator import Paginator
+from django.utils.functional import cached_property
+
+
+class PostsPaginator(Paginator):
+    """
+    Paginator that returns that makes last item on 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)
+        hits += int(ceil(hits / float(self.per_page)))
+        return int(ceil(hits / float(self.per_page)))
+
+    def page(self, number):
+        """
+        Returns a Page object for the given 1-based page number.
+        """
+        number = self.validate_number(number)
+        bottom = (number - 1) * self.per_page
+        if number > 1:
+            bottom -= number - 1
+        top = bottom + self.per_page
+        if top + self.orphans >= self.count:
+            top = self.count
+        return self._get_page(self.object_list[bottom:top], number, self)

+ 62 - 0
misago/threads/tests/test_paginator.py

@@ -0,0 +1,62 @@
+from django.test import TestCase
+
+from ..paginator import PostsPaginator
+
+
+class PostsPaginatorTests(TestCase):
+    def test_paginator(self):
+        """pages share first and last items with each other"""
+        items = [i + 1 for i in range(30)]
+
+        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])
+
+    def test_paginator_orphans(self):
+        """paginator handles orphans"""
+        items = [i + 1 for i in range(20)]
+
+        paginator = PostsPaginator(items, 8, 6)
+        self.assertEqual(paginator.num_pages, 2)
+
+        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, 4)
+        self.assertEqual(paginator.num_pages, 3)
+
+        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])
+
+        paginator = PostsPaginator(items, 8, 6)
+        self.assertEqual(paginator.num_pages, 2)
+
+        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, 6, 5)
+        self.assertEqual(paginator.num_pages, 3)
+
+        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])