entities/adapters.py
changeset 5716 0e2af244dea5
parent 5694 ce2c108a9595
child 5718 8d246203730a
equal deleted inserted replaced
5715:2c3e83817a8e 5716:0e2af244dea5
    19 framework itself.
    19 framework itself.
    20 """
    20 """
    21 
    21 
    22 __docformat__ = "restructuredtext en"
    22 __docformat__ = "restructuredtext en"
    23 
    23 
       
    24 from itertools import chain
       
    25 
    24 from logilab.mtconverter import TransformError
    26 from logilab.mtconverter import TransformError
       
    27 from logilab.common.decorators import cached
    25 
    28 
    26 from cubicweb.view import EntityAdapter, implements_adapter_compat
    29 from cubicweb.view import EntityAdapter, implements_adapter_compat
    27 from cubicweb.selectors import implements, relation_possible
    30 from cubicweb.selectors import implements, relation_possible
    28 from cubicweb.interfaces import IDownloadable
    31 from cubicweb.interfaces import IDownloadable, ITree
    29 
    32 
    30 
    33 
    31 class IEmailableAdapter(EntityAdapter):
    34 class IEmailableAdapter(EntityAdapter):
    32     __regid__ = 'IEmailable'
    35     __regid__ = 'IEmailable'
    33     __select__ = relation_possible('primary_email') | relation_possible('use_email')
    36     __select__ = relation_possible('primary_email') | relation_possible('use_email')
   164         raise NotImplementedError
   167         raise NotImplementedError
   165     @implements_adapter_compat('IDownloadable')
   168     @implements_adapter_compat('IDownloadable')
   166     def download_data(self):
   169     def download_data(self):
   167         """return actual data of the downloadable content"""
   170         """return actual data of the downloadable content"""
   168         raise NotImplementedError
   171         raise NotImplementedError
       
   172 
       
   173 
       
   174 class ITreeAdapter(EntityAdapter):
       
   175     """This adapter has to be overriden to be configured using the
       
   176     tree_relation, child_role and parent_role class attributes to
       
   177     benefit from this default implementation
       
   178     """
       
   179     __regid__ = 'ITree'
       
   180     __select__ = implements(ITree) # XXX for bw compat, else should be abstract
       
   181 
       
   182     tree_relation = None
       
   183     child_role = 'subject'
       
   184     parent_role = 'object'
       
   185 
       
   186     @implements_adapter_compat('ITree')
       
   187     def children_rql(self):
       
   188         """returns RQL to get children
       
   189 
       
   190         XXX should be removed from the public interface
       
   191         """
       
   192         return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
       
   193 
       
   194     @implements_adapter_compat('ITree')
       
   195     def different_type_children(self, entities=True):
       
   196         """return children entities of different type as this entity.
       
   197 
       
   198         according to the `entities` parameter, return entity objects or the
       
   199         equivalent result set
       
   200         """
       
   201         res = self.entity.related(self.tree_relation, self.parent_role,
       
   202                                   entities=entities)
       
   203         eschema = self.entity.e_schema
       
   204         if entities:
       
   205             return [e for e in res if e.e_schema != eschema]
       
   206         return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
       
   207 
       
   208     @implements_adapter_compat('ITree')
       
   209     def same_type_children(self, entities=True):
       
   210         """return children entities of the same type as this entity.
       
   211 
       
   212         according to the `entities` parameter, return entity objects or the
       
   213         equivalent result set
       
   214         """
       
   215         res = self.entity.related(self.tree_relation, self.parent_role,
       
   216                                   entities=entities)
       
   217         eschema = self.entity.e_schema
       
   218         if entities:
       
   219             return [e for e in res if e.e_schema == eschema]
       
   220         return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
       
   221 
       
   222     @implements_adapter_compat('ITree')
       
   223     def is_leaf(self):
       
   224         """returns true if this node as no child"""
       
   225         return len(self.children()) == 0
       
   226 
       
   227     @implements_adapter_compat('ITree')
       
   228     def is_root(self):
       
   229         """returns true if this node has no parent"""
       
   230         return self.parent() is None
       
   231 
       
   232     @implements_adapter_compat('ITree')
       
   233     def root(self):
       
   234         """return the root object"""
       
   235         return self._cw.entity_from_eid(self.path()[0])
       
   236 
       
   237     @implements_adapter_compat('ITree')
       
   238     def parent(self):
       
   239         """return the parent entity if any, else None (e.g. if we are on the
       
   240         root)
       
   241         """
       
   242         try:
       
   243             return self.entity.related(self.tree_relation, self.child_role,
       
   244                                        entities=True)[0]
       
   245         except (KeyError, IndexError):
       
   246             return None
       
   247 
       
   248     @implements_adapter_compat('ITree')
       
   249     def children(self, entities=True, sametype=False):
       
   250         """return children entities
       
   251 
       
   252         according to the `entities` parameter, return entity objects or the
       
   253         equivalent result set
       
   254         """
       
   255         if sametype:
       
   256             return self.same_type_children(entities)
       
   257         else:
       
   258             return self.entity.related(self.tree_relation, self.parent_role,
       
   259                                        entities=entities)
       
   260 
       
   261     @implements_adapter_compat('ITree')
       
   262     def iterparents(self, strict=True):
       
   263         def _uptoroot(self):
       
   264             curr = self
       
   265             while True:
       
   266                 curr = curr.parent()
       
   267                 if curr is None:
       
   268                     break
       
   269                 yield curr
       
   270                 curr = curr.cw_adapt_to('ITree')
       
   271         if not strict:
       
   272             return chain([self.entity], _uptoroot(self))
       
   273         return _uptoroot(self)
       
   274 
       
   275     @implements_adapter_compat('ITree')
       
   276     def iterchildren(self, _done=None):
       
   277         """iterates over the item's children"""
       
   278         if _done is None:
       
   279             _done = set()
       
   280         for child in self.children():
       
   281             if child.eid in _done:
       
   282                 self.error('loop in %s tree', child.__regid__.lower())
       
   283                 continue
       
   284             yield child
       
   285             _done.add(child.eid)
       
   286 
       
   287     @implements_adapter_compat('ITree')
       
   288     def prefixiter(self, _done=None):
       
   289         if _done is None:
       
   290             _done = set()
       
   291         if self.entity.eid in _done:
       
   292             return
       
   293         _done.add(self.entity.eid)
       
   294         yield self.entity
       
   295         for child in self.same_type_children():
       
   296             for entity in child.cw_adapt_to('ITree').prefixiter(_done):
       
   297                 yield entity
       
   298 
       
   299     @cached
       
   300     @implements_adapter_compat('ITree')
       
   301     def path(self):
       
   302         """returns the list of eids from the root object to this object"""
       
   303         path = []
       
   304         adapter = self
       
   305         entity = adapter.entity
       
   306         while entity is not None:
       
   307             if entity.eid in path:
       
   308                 self.error('loop in %s tree', entity.__regid__.lower())
       
   309                 break
       
   310             path.append(entity.eid)
       
   311             try:
       
   312                 # check we are not jumping to another tree
       
   313                 if (adapter.tree_relation != self.tree_relation or
       
   314                     adapter.child_role != self.child_role):
       
   315                     break
       
   316                 entity = adapter.parent()
       
   317                 adapter = entity.cw_adapt_to('ITree')
       
   318             except AttributeError:
       
   319                 break
       
   320         path.reverse()
       
   321         return path
       
   322