Mercurial > piecrust2
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/piecrust/data/paginator.py Sun Aug 10 23:43:16 2014 -0700 @@ -0,0 +1,216 @@ +import math +import logging +from werkzeug.utils import cached_property +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): + self._parent_page = page + self._source = source + self._uri = uri + self._page_num = page_num + self._iterator = None + self._pgn_filter = pgn_filter + 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): + return (self._parent_page.config.get('items_per_page') or + self._source.items_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 range(1, total_page_count) + + 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 range(first_num, last_num) + + 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) + + conf = (self._parent_page.config.get('items_filters') or + self._parent_page.app.config.get('site/items_filters')) + if conf == 'none' or conf == 'nil' or conf == '': + conf = None + if conf is not None: + f.addClausesFromConfig(conf) + + return f + + def _getPageUri(self, index): + uri = self._uri + if index > 1: + if uri != '': + uri += '/' + uri += str(index) + return uri + + def _onIteration(self): + if 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 +