859b60fb7983cbcd9cf71ac1bc600edbad49f20b
[mediagoblin.git] / mediagoblin / tools / pagination.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import urllib
18 import copy
19 from math import ceil, floor
20 from itertools import izip, count
21
22 PAGINATION_DEFAULT_PER_PAGE = 30
23
24 class Pagination(object):
25 """
26 Pagination class for mongodb queries.
27
28 Initialization through __init__(self, cursor, page=1, per_page=2),
29 get actual data slice through __call__().
30 """
31
32 def __init__(self, page, cursor, per_page=PAGINATION_DEFAULT_PER_PAGE,
33 jump_to_id=False):
34 """
35 Initializes Pagination
36
37 Args:
38 - page: requested page
39 - per_page: number of objects per page
40 - cursor: db cursor
41 - jump_to_id: ObjectId, sets the page to the page containing the object
42 with _id == jump_to_id.
43 """
44 self.page = page
45 self.per_page = per_page
46 self.cursor = cursor
47 self.total_count = self.cursor.count()
48 self.active_id = None
49
50 if jump_to_id:
51 cursor = copy.copy(self.cursor)
52
53 for (doc, increment) in izip(cursor, count(0)):
54 if doc['_id'] == jump_to_id:
55 self.page = 1 + int(floor(increment / self.per_page))
56
57 self.active_id = jump_to_id
58 break
59
60
61 def __call__(self):
62 """
63 Returns slice of objects for the requested page
64 """
65 return self.cursor.skip(
66 (self.page - 1) * self.per_page).limit(self.per_page)
67
68 @property
69 def pages(self):
70 return int(ceil(self.total_count / float(self.per_page)))
71
72 @property
73 def has_prev(self):
74 return self.page > 1
75
76 @property
77 def has_next(self):
78 return self.page < self.pages
79
80 def iter_pages(self, left_edge=2, left_current=2,
81 right_current=5, right_edge=2):
82 last = 0
83 for num in xrange(1, self.pages + 1):
84 if num <= left_edge or \
85 (num > self.page - left_current - 1 and \
86 num < self.page + right_current) or \
87 num > self.pages - right_edge:
88 if last + 1 != num:
89 yield None
90 yield num
91 last = num
92
93 def get_page_url_explicit(self, base_url, get_params, page_no):
94 """
95 Get a page url by adding a page= parameter to the base url
96 """
97 new_get_params = copy.copy(get_params or {})
98 new_get_params['page'] = page_no
99 return "%s?%s" % (
100 base_url, urllib.urlencode(new_get_params))
101
102 def get_page_url(self, request, page_no):
103 """
104 Get a new page url based of the request, and the new page number.
105
106 This is a nice wrapper around get_page_url_explicit()
107 """
108 return self.get_page_url_explicit(
109 request.path_info, request.GET, page_no)