comparison piecrust/data/paginator.py @ 3:f485ba500df3

Gigantic change to basically make PieCrust 2 vaguely functional. - Serving works, with debug window. - Baking works, multi-threading, with dependency handling. - Various things not implemented yet.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 10 Aug 2014 23:43:16 -0700
parents
children 474c9882decf
comparison
equal deleted inserted replaced
2:40fa08b261b9 3:f485ba500df3
1 import math
2 import logging
3 from werkzeug.utils import cached_property
4 from piecrust.data.filters import PaginationFilter
5 from piecrust.data.iterators import PageIterator
6
7
8 logger = logging.getLogger(__name__)
9
10
11 class Paginator(object):
12 debug_render = ['has_more', 'items', 'has_items', 'items_per_page',
13 'items_this_page', 'prev_page_number', 'this_page_number',
14 'next_page_number', 'prev_page', 'next_page',
15 'total_item_count', 'total_page_count',
16 'next_item', 'prev_item']
17 debug_render_invoke = ['has_more', 'items', 'has_items', 'items_per_page',
18 'items_this_page', 'prev_page_number', 'this_page_number',
19 'next_page_number', 'prev_page', 'next_page',
20 'total_item_count', 'total_page_count',
21 'next_item', 'prev_item']
22
23 def __init__(self, page, source, uri, page_num=1, pgn_filter=None):
24 self._parent_page = page
25 self._source = source
26 self._uri = uri
27 self._page_num = page_num
28 self._iterator = None
29 self._pgn_filter = pgn_filter
30 self._pgn_set_on_ctx = False
31
32 @property
33 def is_loaded(self):
34 return self._iterator is not None
35
36 @property
37 def has_more(self):
38 return self.next_page_number is not None
39
40 @property
41 def unload(self):
42 self._iterator = None
43
44 # Backward compatibility with PieCrust 1.0 {{{
45 @property
46 def posts(self):
47 return self.items
48
49 @property
50 def has_posts(self):
51 return self.has_items
52
53 @property
54 def posts_per_page(self):
55 return self.items_per_page
56
57 @property
58 def posts_this_page(self):
59 return self.items_this_page
60
61 @property
62 def total_post_count(self):
63 return self.total_item_count
64
65 @property
66 def next_post(self):
67 return self.next_item
68
69 @property
70 def prev_post(self):
71 return self.prev_item
72 # }}}
73
74 @property
75 def items(self):
76 self._load()
77 return self._iterator
78
79 @property
80 def has_items(self):
81 return self.posts_this_page > 0
82
83 @cached_property
84 def items_per_page(self):
85 return (self._parent_page.config.get('items_per_page') or
86 self._source.items_per_page)
87
88 @property
89 def items_this_page(self):
90 self._load()
91 return len(self._iterator)
92
93 @property
94 def prev_page_number(self):
95 if self._page_num > 1:
96 return self._page_num - 1
97 return None
98
99 @property
100 def this_page_number(self):
101 return self._page_num
102
103 @property
104 def next_page_number(self):
105 self._load()
106 if self._iterator._has_more:
107 return self._page_num + 1
108 return None
109
110 @property
111 def prev_page(self):
112 num = self.prev_page_number
113 if num is not None:
114 return self._getPageUri(num)
115 return None
116
117 @property
118 def this_page(self):
119 return self._getPageUri(self._page_num)
120
121 @property
122 def next_page(self):
123 num = self.next_page_number
124 if num is not None:
125 return self._getPageUri(num)
126 return None
127
128 @property
129 def total_item_count(self):
130 self._load()
131 return self._iterator.total_count
132
133 @property
134 def total_page_count(self):
135 total_count = self.total_item_count
136 per_page = self.items_per_page
137 return int(math.ceil(total_count / per_page))
138
139 @property
140 def next_item(self):
141 self._load()
142 return self._iterator.prev_page
143
144 @property
145 def prev_item(self):
146 self._load()
147 return self._iterator.next_page
148
149 def all_page_numbers(self, radius=-1):
150 total_page_count = self.total_page_count
151 if total_page_count == 0:
152 return []
153
154 if radius <= 0 or total_page_count < (2 * radius + 1):
155 return range(1, total_page_count)
156
157 first_num = self._page_num - radius
158 last_num = self._page_num + radius
159 if first_num <= 0:
160 last_num += 1 - first_num
161 first_num = 1
162 elif last_num > total_page_count:
163 first_num -= (last_num - total_page_count)
164 last_num = total_page_count
165 first_num = max(1, first_num)
166 last_num = min(total_page_count, last_num)
167 return range(first_num, last_num)
168
169 def page(self, index):
170 return self._getPageUri(index)
171
172 def _load(self):
173 if self._iterator is not None:
174 return
175
176 if self._source is None:
177 raise Exception("Can't load pagination data: no source has been defined.")
178
179 pag_filter = self._getPaginationFilter()
180 offset = (self._page_num - 1) * self.items_per_page
181 self._iterator = PageIterator(self._source,
182 current_page=self._parent_page,
183 pagination_filter=pag_filter,
184 offset=offset, limit=self.items_per_page,
185 locked=True)
186 self._iterator._iter_event += self._onIteration
187
188 def _getPaginationFilter(self):
189 f = PaginationFilter()
190
191 if self._pgn_filter is not None:
192 f.addClause(self._pgn_filter.root_clause)
193
194 conf = (self._parent_page.config.get('items_filters') or
195 self._parent_page.app.config.get('site/items_filters'))
196 if conf == 'none' or conf == 'nil' or conf == '':
197 conf = None
198 if conf is not None:
199 f.addClausesFromConfig(conf)
200
201 return f
202
203 def _getPageUri(self, index):
204 uri = self._uri
205 if index > 1:
206 if uri != '':
207 uri += '/'
208 uri += str(index)
209 return uri
210
211 def _onIteration(self):
212 if not self._pgn_set_on_ctx:
213 eis = self._parent_page.app.env.exec_info_stack
214 eis.current_page_info.render_ctx.setPagination(self)
215 self._pgn_set_on_ctx = True
216