Mercurial > piecrust2
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 |