Commit f1d6221c authored by Sylvain Thénault's avatar Sylvain Thénault
Browse files

[web] move itree adapter to entities, it may be used outside the web interface

parent 2c3e83817a8e
......@@ -21,11 +21,14 @@ framework itself.
__docformat__ = "restructuredtext en"
from itertools import chain
from logilab.mtconverter import TransformError
from logilab.common.decorators import cached
from cubicweb.view import EntityAdapter, implements_adapter_compat
from cubicweb.selectors import implements, relation_possible
from cubicweb.interfaces import IDownloadable
from cubicweb.interfaces import IDownloadable, ITree
class IEmailableAdapter(EntityAdapter):
......@@ -166,3 +169,154 @@ class IDownloadableAdapter(EntityAdapter):
def download_data(self):
"""return actual data of the downloadable content"""
raise NotImplementedError
class ITreeAdapter(EntityAdapter):
"""This adapter has to be overriden to be configured using the
tree_relation, child_role and parent_role class attributes to
benefit from this default implementation
"""
__regid__ = 'ITree'
__select__ = implements(ITree) # XXX for bw compat, else should be abstract
tree_relation = None
child_role = 'subject'
parent_role = 'object'
@implements_adapter_compat('ITree')
def children_rql(self):
"""returns RQL to get children
XXX should be removed from the public interface
"""
return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
@implements_adapter_compat('ITree')
def different_type_children(self, entities=True):
"""return children entities of different type as this entity.
according to the `entities` parameter, return entity objects or the
equivalent result set
"""
res = self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
eschema = self.entity.e_schema
if entities:
return [e for e in res if e.e_schema != eschema]
return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
@implements_adapter_compat('ITree')
def same_type_children(self, entities=True):
"""return children entities of the same type as this entity.
according to the `entities` parameter, return entity objects or the
equivalent result set
"""
res = self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
eschema = self.entity.e_schema
if entities:
return [e for e in res if e.e_schema == eschema]
return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
@implements_adapter_compat('ITree')
def is_leaf(self):
"""returns true if this node as no child"""
return len(self.children()) == 0
@implements_adapter_compat('ITree')
def is_root(self):
"""returns true if this node has no parent"""
return self.parent() is None
@implements_adapter_compat('ITree')
def root(self):
"""return the root object"""
return self._cw.entity_from_eid(self.path()[0])
@implements_adapter_compat('ITree')
def parent(self):
"""return the parent entity if any, else None (e.g. if we are on the
root)
"""
try:
return self.entity.related(self.tree_relation, self.child_role,
entities=True)[0]
except (KeyError, IndexError):
return None
@implements_adapter_compat('ITree')
def children(self, entities=True, sametype=False):
"""return children entities
according to the `entities` parameter, return entity objects or the
equivalent result set
"""
if sametype:
return self.same_type_children(entities)
else:
return self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
@implements_adapter_compat('ITree')
def iterparents(self, strict=True):
def _uptoroot(self):
curr = self
while True:
curr = curr.parent()
if curr is None:
break
yield curr
curr = curr.cw_adapt_to('ITree')
if not strict:
return chain([self.entity], _uptoroot(self))
return _uptoroot(self)
@implements_adapter_compat('ITree')
def iterchildren(self, _done=None):
"""iterates over the item's children"""
if _done is None:
_done = set()
for child in self.children():
if child.eid in _done:
self.error('loop in %s tree', child.__regid__.lower())
continue
yield child
_done.add(child.eid)
@implements_adapter_compat('ITree')
def prefixiter(self, _done=None):
if _done is None:
_done = set()
if self.entity.eid in _done:
return
_done.add(self.entity.eid)
yield self.entity
for child in self.same_type_children():
for entity in child.cw_adapt_to('ITree').prefixiter(_done):
yield entity
@cached
@implements_adapter_compat('ITree')
def path(self):
"""returns the list of eids from the root object to this object"""
path = []
adapter = self
entity = adapter.entity
while entity is not None:
if entity.eid in path:
self.error('loop in %s tree', entity.__regid__.lower())
break
path.append(entity.eid)
try:
# check we are not jumping to another tree
if (adapter.tree_relation != self.tree_relation or
adapter.child_role != self.child_role):
break
entity = adapter.parent()
adapter = entity.cw_adapt_to('ITree')
except AttributeError:
break
path.reverse()
return path
......@@ -22,173 +22,20 @@ plugin.
__docformat__ = "restructuredtext en"
from warnings import warn
from itertools import chain
from logilab.mtconverter import xml_escape
from logilab.common.decorators import cached
from cubicweb.utils import make_uid
from cubicweb.selectors import implements, adaptable
from cubicweb.view import EntityView, EntityAdapter, implements_adapter_compat
from cubicweb.selectors import adaptable
from cubicweb.view import EntityView
from cubicweb.mixins import _done_init
from cubicweb.web import json
from cubicweb.interfaces import ITree
from cubicweb.web.views import baseviews
def treecookiename(treeid):
return str('%s-treestate' % treeid)
class ITreeAdapter(EntityAdapter):
"""This adapter has to be overriden to be configured using the
tree_relation, child_role and parent_role class attributes to
benefit from this default implementation
"""
__regid__ = 'ITree'
__select__ = implements(ITree) # XXX for bw compat, else should be abstract
tree_relation = None
child_role = 'subject'
parent_role = 'object'
@implements_adapter_compat('ITree')
def children_rql(self):
"""returns RQL to get children
XXX should be removed from the public interface
"""
return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
@implements_adapter_compat('ITree')
def different_type_children(self, entities=True):
"""return children entities of different type as this entity.
according to the `entities` parameter, return entity objects or the
equivalent result set
"""
res = self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
eschema = self.entity.e_schema
if entities:
return [e for e in res if e.e_schema != eschema]
return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
@implements_adapter_compat('ITree')
def same_type_children(self, entities=True):
"""return children entities of the same type as this entity.
according to the `entities` parameter, return entity objects or the
equivalent result set
"""
res = self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
eschema = self.entity.e_schema
if entities:
return [e for e in res if e.e_schema == eschema]
return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
@implements_adapter_compat('ITree')
def is_leaf(self):
"""returns true if this node as no child"""
return len(self.children()) == 0
@implements_adapter_compat('ITree')
def is_root(self):
"""returns true if this node has no parent"""
return self.parent() is None
@implements_adapter_compat('ITree')
def root(self):
"""return the root object"""
return self._cw.entity_from_eid(self.path()[0])
@implements_adapter_compat('ITree')
def parent(self):
"""return the parent entity if any, else None (e.g. if we are on the
root)
"""
try:
return self.entity.related(self.tree_relation, self.child_role,
entities=True)[0]
except (KeyError, IndexError):
return None
@implements_adapter_compat('ITree')
def children(self, entities=True, sametype=False):
"""return children entities
according to the `entities` parameter, return entity objects or the
equivalent result set
"""
if sametype:
return self.same_type_children(entities)
else:
return self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
@implements_adapter_compat('ITree')
def iterparents(self, strict=True):
def _uptoroot(self):
curr = self
while True:
curr = curr.parent()
if curr is None:
break
yield curr
curr = curr.cw_adapt_to('ITree')
if not strict:
return chain([self.entity], _uptoroot(self))
return _uptoroot(self)
@implements_adapter_compat('ITree')
def iterchildren(self, _done=None):
"""iterates over the item's children"""
if _done is None:
_done = set()
for child in self.children():
if child.eid in _done:
self.error('loop in %s tree', child.__regid__.lower())
continue
yield child
_done.add(child.eid)
@implements_adapter_compat('ITree')
def prefixiter(self, _done=None):
if _done is None:
_done = set()
if self.entity.eid in _done:
return
_done.add(self.entity.eid)
yield self.entity
for child in self.same_type_children():
for entity in child.cw_adapt_to('ITree').prefixiter(_done):
yield entity
@cached
@implements_adapter_compat('ITree')
def path(self):
"""returns the list of eids from the root object to this object"""
path = []
adapter = self
entity = adapter.entity
while entity is not None:
if entity.eid in path:
self.error('loop in %s tree', entity.__regid__.lower())
break
path.append(entity.eid)
try:
# check we are not jumping to another tree
if (adapter.tree_relation != self.tree_relation or
adapter.child_role != self.child_role):
break
entity = adapter.parent()
adapter = entity.cw_adapt_to('ITree')
except AttributeError:
break
path.reverse()
return path
class BaseTreeView(baseviews.ListView):
"""base tree view"""
__regid__ = 'tree'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment