Mercurial > piecrust2
view piecrust/data/paginator.py @ 182:a54d3c0b5f4a
tests: Patch `os.path.exists` and improve patching for `open`.
You can specify additional modules for which to patch `open`.
Also, it was incorrectly updating the opened file, even when it was opened
for read only. Now it only updates the contents if the file was opened for
write, and supports appending to the end.
Last, it supports opening text files in binary mode.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 04 Jan 2015 14:55:41 -0800 |
parents | dc8a6ff88f37 |
children | f130365568ff |
line wrap: on
line source
import math import logging from werkzeug.utils import cached_property from piecrust.data.base import IPaginationSource from piecrust.data.filters import PaginationFilter from piecrust.data.iterators import PageIterator logger = logging.getLogger(__name__) class Paginator(object): debug_render = ['has_more', 'items', 'has_items', 'items_per_page', 'items_this_page', 'prev_page_number', 'this_page_number', 'next_page_number', 'prev_page', 'next_page', 'total_item_count', 'total_page_count', 'next_item', 'prev_item'] debug_render_invoke = ['has_more', 'items', 'has_items', 'items_per_page', 'items_this_page', 'prev_page_number', 'this_page_number', 'next_page_number', 'prev_page', 'next_page', 'total_item_count', 'total_page_count', 'next_item', 'prev_item'] def __init__(self, page, source, uri, page_num=1, pgn_filter=None, items_per_page=-1): self._parent_page = page self._source = source self._uri = uri self._page_num = page_num self._iterator = None self._pgn_filter = pgn_filter self._items_per_page = items_per_page self._pgn_set_on_ctx = False @property def is_loaded(self): return self._iterator is not None @property def has_more(self): return self.next_page_number is not None @property def unload(self): self._iterator = None # Backward compatibility with PieCrust 1.0 {{{ @property def posts(self): return self.items @property def has_posts(self): return self.has_items @property def posts_per_page(self): return self.items_per_page @property def posts_this_page(self): return self.items_this_page @property def total_post_count(self): return self.total_item_count @property def next_post(self): return self.next_item @property def prev_post(self): return self.prev_item # }}} @property def items(self): self._load() return self._iterator @property def has_items(self): return self.posts_this_page > 0 @cached_property def items_per_page(self): if self._items_per_page > 0: return self._items_per_page if self._parent_page: ipp = self._parent_page.config.get('items_per_page') if ipp is not None: return ipp if isinstance(self._source, IPaginationSource): return self._source.getItemsPerPage() raise Exception("No way to figure out how many items to display " "per page.") @property def items_this_page(self): self._load() return len(self._iterator) @property def prev_page_number(self): if self._page_num > 1: return self._page_num - 1 return None @property def this_page_number(self): return self._page_num @property def next_page_number(self): self._load() if self._iterator._has_more: return self._page_num + 1 return None @property def prev_page(self): num = self.prev_page_number if num is not None: return self._getPageUri(num) return None @property def this_page(self): return self._getPageUri(self._page_num) @property def next_page(self): num = self.next_page_number if num is not None: return self._getPageUri(num) return None @property def total_item_count(self): self._load() return self._iterator.total_count @property def total_page_count(self): total_count = self.total_item_count per_page = self.items_per_page return int(math.ceil(total_count / per_page)) @property def next_item(self): self._load() return self._iterator.prev_page @property def prev_item(self): self._load() return self._iterator.next_page def all_page_numbers(self, radius=-1): total_page_count = self.total_page_count if total_page_count == 0: return [] if radius <= 0 or total_page_count < (2 * radius + 1): return list(range(1, total_page_count + 1)) first_num = self._page_num - radius last_num = self._page_num + radius if first_num <= 0: last_num += 1 - first_num first_num = 1 elif last_num > total_page_count: first_num -= (last_num - total_page_count) last_num = total_page_count first_num = max(1, first_num) last_num = min(total_page_count, last_num) return list(range(first_num, last_num + 1)) def page(self, index): return self._getPageUri(index) def _load(self): if self._iterator is not None: return if self._source is None: raise Exception("Can't load pagination data: no source has been defined.") pag_filter = self._getPaginationFilter() offset = (self._page_num - 1) * self.items_per_page self._iterator = PageIterator(self._source, current_page=self._parent_page, pagination_filter=pag_filter, offset=offset, limit=self.items_per_page, locked=True) self._iterator._iter_event += self._onIteration def _getPaginationFilter(self): f = PaginationFilter() if self._pgn_filter is not None: f.addClause(self._pgn_filter.root_clause) if isinstance(self._source, IPaginationSource): sf = self._source.getPaginationFilter(self._parent_page) if sf is not None: f.addClause(sf) return f def _getPageUri(self, index): uri = self._uri if index > 1: if uri != '': uri += '/' uri += str(index) return uri def _onIteration(self): if self._parent_page is not None and not self._pgn_set_on_ctx: eis = self._parent_page.app.env.exec_info_stack eis.current_page_info.render_ctx.setPagination(self) self._pgn_set_on_ctx = True