Permalink
Browse files

add pagination utilities to utils.py

  • Loading branch information...
1 parent 7b0d549 commit 034a909877b0b01c9007f4533f77a8abfdb35a95 @jmoiron committed Apr 26, 2011
Showing with 99 additions and 0 deletions.
  1. 0 tests/__init__.py
  2. +39 −0 tests/test_utils.py
  3. +60 −0 utils.py
View
No changes.
View
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+""" """
+
+from unittest import TestCase
+from utils import *
+
+class UtilsTest(TestCase):
+
+ def test_make_window(self):
+ """Make sure that pagination windows work."""
+ self.assertEqual(make_window(17, 4), [16, 17, 18, 19])
+ self.assertEqual(make_window(6, 5), [4, 5, 6, 7, 8])
+
+ def test_page_context(self):
+ """Test page context's for pagination displays."""
+ self.assertEqual(page_context(1, total=5), [1, 2, 3, 4, 5])
+ self.assertEqual(page_context(4, window=10, total=15),
+ [1, 2, 3, 4, 5, 6, 7, 8, '...', 14, 15])
+ self.assertEqual(page_context(9, window=9, total=15),
+ [1, 2, '...', 7, 8, 9, 10, 11, '...', 14, 15])
+ self.assertEqual(page_context(9, window=9, total=15, inter='..'),
+ [1, 2, '..', 7, 8, 9, 10, 11, '..', 14, 15])
+ self.assertEqual(page_context(11, total=14),
+ [1, 2, '...', 9, 10, 11, 12, 13, 14])
+ self.assertEqual(page_context(4, total=35),
+ [1, 2, 3, 4, 5, 6, '...', 34, 35])
+
+ def test_page_object(self):
+ """Test a page object for proper behavior."""
+ p = Page(1, per_page=10, total_objects=101, window=5)
+ self.assertEqual(p.context(), [1, 2, 3, '...', 10, 11])
+ p = Page(19, per_page=10, total_objects=1001, window=10)
+ self.assertEqual(p.context(),
+ [1, 2, '...', 17, 18, 19, 20, 21, 22, '...', 100, 101])
+ p = Page(20, per_page=10, total_objects=1001, window=10)
+ self.assertEqual(p.context(),
+ [1, 2, '...', 18, 19, 20, 21, 22, 23, '...', 100, 101])
View
@@ -3,6 +3,8 @@
"""utils for jmoiron.net."""
+from math import ceil
+
def humansize(bytesize, persec=False):
"""Humanize size string for bytesize bytes."""
units = ['Bps', 'KBps', 'MBps', 'GBps'] if persec else ['B', 'KB', 'MB', 'GB']
@@ -13,3 +15,61 @@ def humansize(bytesize, persec=False):
oom += 1
return '%0.2f %s' % (bytesize/reduce_factor**oom, units[oom])
+# -- pagination --
+
+class Page(object):
+ def __init__(self, number, per_page, total_objects, window=8):
+ self.number = number
+ self.per_page = per_page
+ self.begin_offset = (number-1) * per_page
+ self.end_offset = number * per_page
+ self.exists = total_objects > self.begin_offset
+ self.has_prev = number > 1
+ self.has_next = self.end_offset < total_objects
+ self.num_pages = (total_objects / per_page)
+ self.num_pages += 1 if (total_objects % per_page) else 0
+ self.window = window
+
+ def slice(self):
+ return slice(self.begin_offset, self.end_offset)
+
+ def context(self):
+ return page_context(self, window=self.window, total=self.num_pages)
+
+def make_window(center, size):
+ """Make a number window of size with the 'center' in the middle."""
+ if size % 2:
+ lpad = rpad = size/2
+ else:
+ lpad = size/2 - 1
+ rpad = size/2
+ return range(center - lpad, center + rpad + 1)
+
+
+def page_context(page, window=8, total=None, inter='...'):
+ """Takes a page (a Page object, or a number w/ total specified) and returns
+ a window with `inter` interspersed between the longer gaps in the context.
+ """
+ if isinstance(page, (int, long)):
+ assert(total)
+ current = page
+ else:
+ current = page.number
+ if total is None:
+ total = page.paginator.num_pages
+
+ # the pad area is the area around the edges of the total window where the
+ # ellipsis algorithm would make the page hit the edge anyway. We pad the
+ # window with 2 on either side, which makes the middle (window/2)
+ pad_area = 2 + (ceil(float(window - 4)/2))
+ middle = window - 4
+
+ if total < window:
+ return range(1, total+1)
+ if current <= pad_area:
+ return range(1, 2 + middle + 1) + [inter, total-1, total]
+ if total - pad_area <= current <= total:
+ return [1, 2, inter] + range(total - middle - 1, total+1)
+ else:
+ return [1, 2, inter] + make_window(current, middle) + [inter, total-1, total]
+

0 comments on commit 034a909

Please sign in to comment.