Mercurial > piecrust2
comparison piecrust/dataproviders/pageiterator.py @ 867:757fba54bfd3
refactor: Improve pagination and iterators to work with other sources.
This makes the assets work as a pagination source again.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 12 Jun 2017 22:20:58 -0700 |
parents | 9bb22bbe093c |
children | d6d35b2efd04 |
comparison
equal
deleted
inserted
replaced
866:d9059257743c | 867:757fba54bfd3 |
---|---|
1 import logging | 1 import logging |
2 from piecrust.data.filters import PaginationFilter | 2 from piecrust.data.filters import PaginationFilter |
3 from piecrust.data.paginationdata import PaginationData | 3 from piecrust.data.paginationdata import PaginationData |
4 from piecrust.events import Event | 4 from piecrust.events import Event |
5 from piecrust.dataproviders.base import DataProvider | 5 from piecrust.dataproviders.base import DataProvider |
6 from piecrust.sources.base import AbortedSourceUseError | 6 from piecrust.sources.base import ContentSource, AbortedSourceUseError |
7 | 7 |
8 | 8 |
9 logger = logging.getLogger(__name__) | 9 logger = logging.getLogger(__name__) |
10 | 10 |
11 | 11 |
12 class _ItInfo: | 12 class _ItInfo: |
13 def __init__(self): | 13 def __init__(self): |
14 self.it = None | 14 self.it = None |
15 self.iterated = False | 15 self.iterated = False |
16 self.source_name = None | |
16 | 17 |
17 | 18 |
18 class PageIteratorDataProvider(DataProvider): | 19 class PageIteratorDataProvider(DataProvider): |
19 """ A data provider that reads a content source as a list of pages. | 20 """ A data provider that reads a content source as a list of pages. |
20 | 21 |
49 self._its = [] | 50 self._its = [] |
50 for source in self._sources: | 51 for source in self._sources: |
51 i = _ItInfo() | 52 i = _ItInfo() |
52 i.it = PageIterator(source, current_page=self._page) | 53 i.it = PageIterator(source, current_page=self._page) |
53 i.it._iter_event += self._onIteration | 54 i.it._iter_event += self._onIteration |
55 i.source_name = source.name | |
54 self._its.append(i) | 56 self._its.append(i) |
55 | 57 |
56 def _onIteration(self, it): | 58 def _onIteration(self, it): |
57 ii = next(filter(lambda i: i.it == it, self._its)) | 59 ii = next(filter(lambda i: i.it == it, self._its)) |
58 if not ii.iterated: | 60 if not ii.iterated: |
59 rcs = self._app.env.render_ctx_stack | 61 rcs = self._app.env.render_ctx_stack |
60 rcs.current_ctx.addUsedSource(self._source.name) | 62 rcs.current_ctx.addUsedSource(ii.source_name) |
61 ii.iterated = True | 63 ii.iterated = True |
62 | 64 |
63 def _debugRenderDoc(self): | 65 def _debugRenderDoc(self): |
64 return 'Provides a list of %d items' % len(self) | 66 return 'Provides a list of %d items' % len(self) |
65 | 67 |
66 | 68 |
67 class PageIterator: | 69 class PageIterator: |
68 def __init__(self, source, *, current_page=None): | 70 def __init__(self, source, *, current_page=None): |
69 self._source = source | 71 self._source = source |
72 self._is_content_source = isinstance(source, ContentSource) | |
70 self._cache = None | 73 self._cache = None |
71 self._pagination_slicer = None | 74 self._pagination_slicer = None |
72 self._has_sorter = False | 75 self._has_sorter = False |
73 self._next_page = None | 76 self._next_page = None |
74 self._prev_page = None | 77 self._prev_page = None |
75 self._locked = False | 78 self._locked = False |
76 self._iter_event = Event() | 79 self._iter_event = Event() |
77 self._current_page = current_page | 80 self._current_page = current_page |
78 self._it = PageContentSourceIterator(self._source) | 81 self._initIterator() |
79 | 82 |
80 @property | 83 @property |
81 def total_count(self): | 84 def total_count(self): |
82 self._load() | 85 self._load() |
83 if self._pagination_slicer is not None: | 86 if self._pagination_slicer is not None: |
213 "can't be modified anymore.") | 216 "can't be modified anymore.") |
214 | 217 |
215 def _ensureSorter(self): | 218 def _ensureSorter(self): |
216 if self._has_sorter: | 219 if self._has_sorter: |
217 return | 220 return |
218 self._it = DateSortIterator(self._it, reverse=True) | 221 if self._is_content_source: |
222 self._it = DateSortIterator(self._it, reverse=True) | |
219 self._has_sorter = True | 223 self._has_sorter = True |
220 | 224 |
225 def _initIterator(self): | |
226 if self._is_content_source: | |
227 self._it = PageContentSourceIterator(self._source) | |
228 else: | |
229 self._it = GenericSourceIterator(self._source) | |
230 | |
221 def _unload(self): | 231 def _unload(self): |
222 self._it = PageContentSourceIterator(self._source) | 232 self._initIterator() |
223 self._cache = None | 233 self._cache = None |
224 self._paginationSlicer = None | 234 self._paginationSlicer = None |
225 self._has_sorter = False | 235 self._has_sorter = False |
226 self._next_page = None | 236 self._next_page = None |
227 self._prev_page = None | 237 self._prev_page = None |
228 | 238 |
229 def _load(self): | 239 def _load(self): |
230 if self._cache is not None: | 240 if self._cache is not None: |
231 return | 241 return |
232 | 242 |
233 if self._source.app.env.abort_source_use: | 243 if self._is_content_source: |
234 if self._current_page is not None: | 244 if self._source.app.env.abort_source_use: |
235 logger.debug("Aborting iteration of '%s' from: %s." % | 245 if self._current_page is not None: |
236 (self._source.name, | 246 logger.debug("Aborting iteration of '%s' from: %s." % |
237 self._current_page.content_spec)) | 247 (self._source.name, |
238 else: | 248 self._current_page.content_spec)) |
239 logger.debug("Aborting iteration of '%s'." % | 249 else: |
240 self._source.name) | 250 logger.debug("Aborting iteration of '%s'." % |
241 raise AbortedSourceUseError() | 251 self._source.name) |
252 raise AbortedSourceUseError() | |
242 | 253 |
243 self._ensureSorter() | 254 self._ensureSorter() |
244 | 255 |
245 tail_it = PaginationDataBuilderIterator(self._it, self._source.route) | 256 if self._is_content_source: |
246 self._cache = list(tail_it) | 257 self._it = PaginationDataBuilderIterator(self._it) |
258 | |
259 self._cache = list(self._it) | |
247 | 260 |
248 if (self._current_page is not None and | 261 if (self._current_page is not None and |
249 self._pagination_slicer is not None): | 262 self._pagination_slicer is not None): |
250 pn = [self._pagination_slicer.prev_page, | 263 pn = [self._pagination_slicer.prev_page, |
251 self._pagination_slicer.next_page] | 264 self._pagination_slicer.next_page] |
252 pn_it = PaginationDataBuilderIterator(iter(pn), | 265 pn_it = PaginationDataBuilderIterator(iter(pn)) |
253 self._source.route) | |
254 self._prev_page, self._next_page = (list(pn_it)) | 266 self._prev_page, self._next_page = (list(pn_it)) |
255 | 267 |
256 self._iter_event.fire(self) | 268 self._iter_event.fire(self) |
257 | 269 |
258 def _debugRenderDoc(self): | 270 def _debugRenderDoc(self): |
365 for item in source.getAllContents(): | 377 for item in source.getAllContents(): |
366 yield app.getPage(source, item) | 378 yield app.getPage(source, item) |
367 | 379 |
368 | 380 |
369 class PaginationDataBuilderIterator: | 381 class PaginationDataBuilderIterator: |
370 def __init__(self, it, route): | 382 def __init__(self, it): |
371 self.it = it | 383 self.it = it |
372 self.route = route | |
373 | 384 |
374 def __iter__(self): | 385 def __iter__(self): |
375 for page in self.it: | 386 for page in self.it: |
376 if page is not None: | 387 if page is not None: |
377 yield PaginationData(page) | 388 yield PaginationData(page) |
378 else: | 389 else: |
379 yield None | 390 yield None |
380 | 391 |
392 | |
393 class GenericSourceIterator: | |
394 def __init__(self, source): | |
395 self.source = source | |
396 self.it = None | |
397 | |
398 def __iter__(self): | |
399 yield from self.source |