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
+