comparison piecrust/data/paginator.py @ 853:f070a4fc033c

core: Continue PieCrust3 refactor, simplify pages. The asset pipeline is still the only function pipeline at this point. * No more `QualifiedPage`, and several other pieces of code deleted. * Data providers are simpler and more focused. For instance, the page iterator doesn't try to support other types of items. * Route parameters are proper known source metadata to remove the confusion between the two. * Make the baker and pipeline more correctly manage records and record histories. * Add support for record collapsing and deleting stale outputs in the asset pipeline.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 21 May 2017 00:06:59 -0700
parents 4850f8c21b6e
children 08e02c2a2a1a
comparison
equal deleted inserted replaced
852:4850f8c21b6e 853:f070a4fc033c
1 import math 1 import math
2 import logging 2 import logging
3 from werkzeug.utils import cached_property 3 from werkzeug.utils import cached_property
4 from piecrust.data.filters import PaginationFilter, page_value_accessor
5 from piecrust.data.iterators import PageIterator
6 from piecrust.sources.interfaces import IPaginationSource
7 4
8 5
9 logger = logging.getLogger(__name__) 6 logger = logging.getLogger(__name__)
10 7
11 8
21 'items_this_page', 'prev_page_number', 'this_page_number', 18 'items_this_page', 'prev_page_number', 'this_page_number',
22 'next_page_number', 'prev_page', 'next_page', 19 'next_page_number', 'prev_page', 'next_page',
23 'total_item_count', 'total_page_count', 20 'total_item_count', 'total_page_count',
24 'next_item', 'prev_item'] 21 'next_item', 'prev_item']
25 22
26 def __init__(self, qualified_page, source, *, 23 def __init__(self, source, current_page, sub_num, *,
27 pgn_filter=None, items_per_page=-1): 24 pgn_filter=None, items_per_page=-1):
28 self._parent_page = qualified_page
29 self._page_num = qualified_page.page_num
30 self._source = source 25 self._source = source
26 self._page = current_page
27 self._sub_num = sub_num
31 self._iterator = None 28 self._iterator = None
32 self._pgn_filter = pgn_filter 29 self._pgn_filter = pgn_filter
33 self._items_per_page = items_per_page 30 self._items_per_page = items_per_page
34 self._pgn_set_on_ctx = False 31 self._pgn_set_on_ctx = False
35 32
86 83
87 @cached_property 84 @cached_property
88 def items_per_page(self): 85 def items_per_page(self):
89 if self._items_per_page > 0: 86 if self._items_per_page > 0:
90 return self._items_per_page 87 return self._items_per_page
91 if self._parent_page: 88
92 ipp = self._parent_page.page.config.get('items_per_page') 89 ipp = self._page.config.get('items_per_page')
93 if ipp is not None: 90 if ipp is not None:
94 return ipp 91 return ipp
95 if isinstance(self._source, IPaginationSource): 92
96 return self._source.getItemsPerPage()
97 raise Exception("No way to figure out how many items to display " 93 raise Exception("No way to figure out how many items to display "
98 "per page.") 94 "per page.")
99 95
100 @property 96 @property
101 def items_this_page(self): 97 def items_this_page(self):
102 self._load() 98 self._load()
103 return len(self._iterator) 99 return len(self._iterator)
104 100
105 @property 101 @property
106 def prev_page_number(self): 102 def prev_page_number(self):
107 if self._page_num > 1: 103 if self._sub_num > 1:
108 return self._page_num - 1 104 return self._sub_num - 1
109 return None 105 return None
110 106
111 @property 107 @property
112 def this_page_number(self): 108 def this_page_number(self):
113 return self._page_num 109 return self._sub_num
114 110
115 @property 111 @property
116 def next_page_number(self): 112 def next_page_number(self):
117 self._load() 113 self._load()
118 if self._iterator._has_more: 114 if self._iterator._has_more:
119 return self._page_num + 1 115 return self._sub_num + 1
120 return None 116 return None
121 117
122 @property 118 @property
123 def prev_page(self): 119 def prev_page(self):
124 num = self.prev_page_number 120 num = self.prev_page_number
126 return self._getPageUri(num) 122 return self._getPageUri(num)
127 return None 123 return None
128 124
129 @property 125 @property
130 def this_page(self): 126 def this_page(self):
131 return self._getPageUri(self._page_num) 127 return self._getPageUri(self._sub_num)
132 128
133 @property 129 @property
134 def next_page(self): 130 def next_page(self):
135 num = self.next_page_number 131 num = self.next_page_number
136 if num is not None: 132 if num is not None:
164 return [] 160 return []
165 161
166 if radius <= 0 or total_page_count < (2 * radius + 1): 162 if radius <= 0 or total_page_count < (2 * radius + 1):
167 return list(range(1, total_page_count + 1)) 163 return list(range(1, total_page_count + 1))
168 164
169 first_num = self._page_num - radius 165 first_num = self._sub_num - radius
170 last_num = self._page_num + radius 166 last_num = self._sub_num + radius
171 if first_num <= 0: 167 if first_num <= 0:
172 last_num += 1 - first_num 168 last_num += 1 - first_num
173 first_num = 1 169 first_num = 1
174 elif last_num > total_page_count: 170 elif last_num > total_page_count:
175 first_num -= (last_num - total_page_count) 171 first_num -= (last_num - total_page_count)
183 179
184 def _load(self): 180 def _load(self):
185 if self._iterator is not None: 181 if self._iterator is not None:
186 return 182 return
187 183
188 if self._source is None: 184 from piecrust.data.filters import PaginationFilter
189 raise Exception("Can't load pagination data: no source has " 185 from piecrust.dataproviders.page_iterator import PageIterator
190 "been defined.") 186
191 187 pag_filter = PaginationFilter()
192 pag_filter = self._getPaginationFilter() 188 if self._pgn_filter is not None:
193 offset = (self._page_num - 1) * self.items_per_page 189 pag_filter.addClause(self._pgn_filter.root_clause)
194 current_page = None 190
195 if self._parent_page:
196 current_page = self._parent_page.page
197 self._iterator = PageIterator( 191 self._iterator = PageIterator(
198 self._source, 192 self._source,
199 current_page=current_page, 193 current_page=self._page,
200 pagination_filter=pag_filter, 194 pagination_filter=pag_filter,
201 offset=offset, limit=self.items_per_page,
202 locked=True) 195 locked=True)
203 self._iterator._iter_event += self._onIteration 196 self._iterator._iter_event += self._onIteration
204 197
205 def _getPaginationFilter(self): 198 offset = (self._sub_num - 1) * self.items_per_page
206 f = PaginationFilter(value_accessor=page_value_accessor) 199 limit = self.items_per_page
207 200 self._iterator.slice(offset, limit)
208 if self._pgn_filter is not None:
209 f.addClause(self._pgn_filter.root_clause)
210
211 if self._parent_page and isinstance(self._source, IPaginationSource):
212 sf = self._source.getPaginationFilter(self._parent_page)
213 if sf is not None:
214 f.addClause(sf.root_clause)
215
216 return f
217 201
218 def _getPageUri(self, index): 202 def _getPageUri(self, index):
219 return self._parent_page.getUri(index) 203 return self._page.getUri(index)
220 204
221 def _onIteration(self): 205 def _onIteration(self):
222 if self._parent_page is not None and not self._pgn_set_on_ctx: 206 if not self._pgn_set_on_ctx:
223 eis = self._parent_page.app.env.exec_info_stack 207 eis = self._page.app.env.exec_info_stack
224 eis.current_page_info.render_ctx.setPagination(self) 208 eis.current_page_info.render_ctx.setPagination(self)
225 self._pgn_set_on_ctx = True 209 self._pgn_set_on_ctx = True
226 210