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