view piecrust/data/linker.py @ 1088:c5f1936e9e89

data: Fix recently introduced bug with blog data provider.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 15 Feb 2018 23:46:37 -0800
parents 00a0a65d08e6
children 1a214de1e1f7
line wrap: on
line source

import logging
from piecrust.data.paginationdata import PaginationData
from piecrust.sources.base import (
    REL_PARENT_GROUP, REL_LOGICAL_PARENT_ITEM, REL_LOGICAL_CHILD_GROUP)


logger = logging.getLogger(__name__)


_unloaded = object()


class Linker:
    """ A template-exposed data class that lets the user navigate the
        logical hierarchy of pages in a page source.
    """
    debug_render = ['parent', 'ancestors', 'siblings', 'children', 'root',
                    'forpath']
    debug_render_invoke = ['ancestors', 'siblings', 'children']
    debug_render_redirect = {
        'ancestors': '_debugRenderAncestors',
        'siblings': '_debugRenderSiblings',
        'children': '_debugRenderChildren'}

    def __init__(self, source, content_item):
        self._source = source
        self._content_item = content_item

        self._parent_group = _unloaded
        self._ancestors = None
        self._siblings = None
        self._children = None

    @property
    def parent(self):
        a = self.ancestors
        if a:
            return a[0]
        return None

    @property
    def root(self):
        a = self.ancestors
        if a:
            return a[-1]
        return self.myself

    @property
    def myself(self):
        page = self._source.app.getPage(self._source, self._content_item)
        return self._makePageData(page)

    @property
    def ancestors(self):
        if self._ancestors is None:
            src = self._source
            app = src.app

            self._ancestors = []
            cur_group = self._getParentGroup()
            while cur_group:
                pi = src.getRelatedContents(cur_group,
                                            REL_LOGICAL_PARENT_ITEM)
                if pi is not None:
                    pipage = app.getPage(src, pi)
                    self._ancestors.append(self._makePageData(pipage))
                    cur_group = src.getRelatedContents(
                        pi, REL_PARENT_GROUP)
                else:
                    break
        return self._ancestors

    @property
    def siblings(self):
        src = self._source
        app = src.app
        sibs = []
        for i in self._getAllSiblings():
            if not i.is_group:
                ipage = app.getPage(src, i)
                ipage_data = self._makePageData(ipage)
                sibs.append(ipage_data)
        return sibs

    @property
    def siblings_all(self):
        src = self._source
        app = src.app
        sibs = []
        for i in self._getAllSiblings():
            if not i.is_group:
                ipage = app.getPage(src, i)
                ipage_data = self._makePageData(ipage)
                sibs.append(ipage_data)
            else:
                sibs.append(self._makeGroupData(i))
        return sibs

    @property
    def has_children(self):
        return bool(self.children)

    @property
    def children(self):
        src = self._source
        app = src.app
        childs = []
        for i in self._getAllChildren():
            if not i.is_group:
                ipage = app.getPage(src, i)
                childs.append(self._makePageData(ipage))
        return childs

    @property
    def children_all(self):
        src = self._source
        app = src.app
        childs = []
        for i in self._getAllChildren():
            if not i.is_group:
                ipage = app.getPage(src, i)
                childs.append(self._makePageData(ipage))
            else:
                childs.append(self._makeGroupData(i))
        return childs

    def forpath(self, path):
        # TODO: generalize this for sources that aren't file-system based.
        item = self._source.findContentFromRoute({'slug': path})
        return Linker(self._source, item)

    def childrenof(self, path, with_groups=False):
        # TODO: generalize this for sources that aren't file-system based.
        src = self._source
        app = src.app
        item = src.findContentFromRoute({'slug': path})
        if item is None:
            raise ValueError("No such content: %s" % path)

        group = self._source.getRelatedContents(item,
                                                REL_LOGICAL_CHILD_GROUP)
        if group is not None:
            childs = []
            for i in src.getContents(group):
                if not i.is_group:
                    ipage = app.getPage(src, i)
                    childs.append(self._makePageData(ipage))
                elif with_groups:
                    childs.append(self._makeGroupData(i))
            return childs
        return None

    def _getAllSiblings(self):
        if self._siblings is None:
            self._siblings = list(self._source.getContents(
                self._getParentGroup()))
        return self._siblings

    def _getAllChildren(self):
        if self._children is None:
            child_group = self._source.getRelatedContents(
                self._content_item, REL_LOGICAL_CHILD_GROUP)
            if child_group is not None:
                self._children = list(
                    self._source.getContents(child_group))
            else:
                self._children = []
        return self._children

    def _getParentGroup(self):
        if self._parent_group is _unloaded:
            self._parent_group = self._source.getRelatedContents(
                self._content_item, REL_PARENT_GROUP)
        return self._parent_group

    def _makePageData(self, page):
        return _PageData(self, page)

    def _makeGroupData(self, group):
        return _GroupData(self._source, group)

    def _debugRenderAncestors(self):
        return [i.title for i in self.ancestors]

    def _debugRenderSiblings(self):
        return [i.title for i in self.siblings]

    def _debugRenderChildren(self):
        return [i.title for i in self.children]


class _PageData(PaginationData):
    def __init__(self, linker, page):
        super().__init__(page)
        self._linker = linker

    def _load(self):
        super()._load()
        self._mapValue('is_page', True)
        self._mapValue(
            'is_self',
            self._page.content_spec == self._linker._content_item.spec)

        self._mapLoader('is_dir', lambda d, n: self._linker.has_children)
        self._mapLoader('is_group', lambda d, n: self._linker.has_children)


class _GroupData:
    def __init__(self, source, group_item):
        self._source = source
        self._group_item = group_item
        self.is_page = False
        self.is_dir = True
        self.is_group = True
        self.family = Linker(source, group_item)