entities/adapters.py
changeset 9256 697a8181ba30
parent 9130 0f1504a9fb51
child 9375 8e88576787c3
equal deleted inserted replaced
9255:46f41c3e1443 9256:697a8181ba30
    26 
    26 
    27 from logilab.mtconverter import TransformError
    27 from logilab.mtconverter import TransformError
    28 from logilab.common.decorators import cached
    28 from logilab.common.decorators import cached
    29 
    29 
    30 from cubicweb import ValidationError, view
    30 from cubicweb import ValidationError, view
    31 from cubicweb.predicates import (implements, is_instance, relation_possible,
    31 from cubicweb.predicates import is_instance, relation_possible, match_exception
    32                                 match_exception)
       
    33 from cubicweb.interfaces import IDownloadable, ITree
       
    34 
    32 
    35 
    33 
    36 class IEmailableAdapter(view.EntityAdapter):
    34 class IEmailableAdapter(view.EntityAdapter):
    37     __regid__ = 'IEmailable'
    35     __regid__ = 'IEmailable'
    38     __select__ = relation_possible('primary_email') | relation_possible('use_email')
    36     __select__ = relation_possible('primary_email') | relation_possible('use_email')
    65         return dict( (attr, getattr(self.entity, attr))
    63         return dict( (attr, getattr(self.entity, attr))
    66                      for attr in self.allowed_massmail_keys() )
    64                      for attr in self.allowed_massmail_keys() )
    67 
    65 
    68 
    66 
    69 class INotifiableAdapter(view.EntityAdapter):
    67 class INotifiableAdapter(view.EntityAdapter):
    70     __needs_bw_compat__ = True
       
    71     __regid__ = 'INotifiable'
    68     __regid__ = 'INotifiable'
    72     __select__ = is_instance('Any')
    69     __select__ = is_instance('Any')
    73 
    70 
    74     @view.implements_adapter_compat('INotifiableAdapter')
       
    75     def notification_references(self, view):
    71     def notification_references(self, view):
    76         """used to control References field of email send on notification
    72         """used to control References field of email send on notification
    77         for this entity. `view` is the notification view.
    73         for this entity. `view` is the notification view.
    78 
    74 
    79         Should return a list of eids which can be used to generate message
    75         Should return a list of eids which can be used to generate message
   165     for weight, words in newdict.iteritems():
   161     for weight, words in newdict.iteritems():
   166         maindict.setdefault(weight, []).extend(words)
   162         maindict.setdefault(weight, []).extend(words)
   167 
   163 
   168 class IDownloadableAdapter(view.EntityAdapter):
   164 class IDownloadableAdapter(view.EntityAdapter):
   169     """interface for downloadable entities"""
   165     """interface for downloadable entities"""
   170     __needs_bw_compat__ = True
       
   171     __regid__ = 'IDownloadable'
   166     __regid__ = 'IDownloadable'
   172     __select__ = implements(IDownloadable, warn=False) # XXX for bw compat, else should be abstract
   167     __abstract__ = True
   173 
   168 
   174     @view.implements_adapter_compat('IDownloadable')
       
   175     def download_url(self, **kwargs): # XXX not really part of this interface
   169     def download_url(self, **kwargs): # XXX not really part of this interface
   176         """return an url to download entity's content"""
   170         """return an url to download entity's content"""
   177         raise NotImplementedError
   171         raise NotImplementedError
   178     @view.implements_adapter_compat('IDownloadable')
   172 
   179     def download_content_type(self):
   173     def download_content_type(self):
   180         """return MIME type of the downloadable content"""
   174         """return MIME type of the downloadable content"""
   181         raise NotImplementedError
   175         raise NotImplementedError
   182     @view.implements_adapter_compat('IDownloadable')
   176 
   183     def download_encoding(self):
   177     def download_encoding(self):
   184         """return encoding of the downloadable content"""
   178         """return encoding of the downloadable content"""
   185         raise NotImplementedError
   179         raise NotImplementedError
   186     @view.implements_adapter_compat('IDownloadable')
   180 
   187     def download_file_name(self):
   181     def download_file_name(self):
   188         """return file name of the downloadable content"""
   182         """return file name of the downloadable content"""
   189         raise NotImplementedError
   183         raise NotImplementedError
   190     @view.implements_adapter_compat('IDownloadable')
   184 
   191     def download_data(self):
   185     def download_data(self):
   192         """return actual data of the downloadable content"""
   186         """return actual data of the downloadable content"""
   193         raise NotImplementedError
   187         raise NotImplementedError
   194 
   188 
   195 # XXX should propose to use two different relations for children/parent
   189 # XXX should propose to use two different relations for children/parent
   217     .. automethod: different_type_children
   211     .. automethod: different_type_children
   218     .. automethod: same_type_children
   212     .. automethod: same_type_children
   219     .. automethod: children_rql
   213     .. automethod: children_rql
   220     .. automethod: path
   214     .. automethod: path
   221     """
   215     """
   222     __needs_bw_compat__ = True
       
   223     __regid__ = 'ITree'
   216     __regid__ = 'ITree'
   224     __select__ = implements(ITree, warn=False) # XXX for bw compat, else should be abstract
   217     __abstract__ = True
   225 
   218 
   226     child_role = 'subject'
   219     child_role = 'subject'
   227     parent_role = 'object'
   220     parent_role = 'object'
   228 
   221 
   229     @property
       
   230     def tree_relation(self):
       
   231         warn('[3.9] tree_attribute is deprecated, define tree_relation on a custom '
       
   232              'ITree for %s instead' % (self.entity.__class__),
       
   233              DeprecationWarning)
       
   234         return self.entity.tree_attribute
       
   235 
       
   236     # XXX should be removed from the public interface
       
   237     @view.implements_adapter_compat('ITree')
       
   238     def children_rql(self):
   222     def children_rql(self):
   239         """Returns RQL to get the children of the entity."""
   223         """Returns RQL to get the children of the entity."""
   240         return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
   224         return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
   241 
   225 
   242     @view.implements_adapter_compat('ITree')
       
   243     def different_type_children(self, entities=True):
   226     def different_type_children(self, entities=True):
   244         """Return children entities of different type as this entity.
   227         """Return children entities of different type as this entity.
   245 
   228 
   246         According to the `entities` parameter, return entity objects or the
   229         According to the `entities` parameter, return entity objects or the
   247         equivalent result set.
   230         equivalent result set.
   251         eschema = self.entity.e_schema
   234         eschema = self.entity.e_schema
   252         if entities:
   235         if entities:
   253             return [e for e in res if e.e_schema != eschema]
   236             return [e for e in res if e.e_schema != eschema]
   254         return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
   237         return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
   255 
   238 
   256     @view.implements_adapter_compat('ITree')
       
   257     def same_type_children(self, entities=True):
   239     def same_type_children(self, entities=True):
   258         """Return children entities of the same type as this entity.
   240         """Return children entities of the same type as this entity.
   259 
   241 
   260         According to the `entities` parameter, return entity objects or the
   242         According to the `entities` parameter, return entity objects or the
   261         equivalent result set.
   243         equivalent result set.
   265         eschema = self.entity.e_schema
   247         eschema = self.entity.e_schema
   266         if entities:
   248         if entities:
   267             return [e for e in res if e.e_schema == eschema]
   249             return [e for e in res if e.e_schema == eschema]
   268         return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
   250         return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
   269 
   251 
   270     @view.implements_adapter_compat('ITree')
       
   271     def is_leaf(self):
   252     def is_leaf(self):
   272         """Returns True if the entity does not have any children."""
   253         """Returns True if the entity does not have any children."""
   273         return len(self.children()) == 0
   254         return len(self.children()) == 0
   274 
   255 
   275     @view.implements_adapter_compat('ITree')
       
   276     def is_root(self):
   256     def is_root(self):
   277         """Returns true if the entity is root of the tree (e.g. has no parent).
   257         """Returns true if the entity is root of the tree (e.g. has no parent).
   278         """
   258         """
   279         return self.parent() is None
   259         return self.parent() is None
   280 
   260 
   281     @view.implements_adapter_compat('ITree')
       
   282     def root(self):
   261     def root(self):
   283         """Return the root entity of the tree."""
   262         """Return the root entity of the tree."""
   284         return self._cw.entity_from_eid(self.path()[0])
   263         return self._cw.entity_from_eid(self.path()[0])
   285 
   264 
   286     @view.implements_adapter_compat('ITree')
       
   287     def parent(self):
   265     def parent(self):
   288         """Returns the parent entity if any, else None (e.g. if we are on the
   266         """Returns the parent entity if any, else None (e.g. if we are on the
   289         root).
   267         root).
   290         """
   268         """
   291         try:
   269         try:
   292             return self.entity.related(self.tree_relation, self.child_role,
   270             return self.entity.related(self.tree_relation, self.child_role,
   293                                        entities=True)[0]
   271                                        entities=True)[0]
   294         except (KeyError, IndexError):
   272         except (KeyError, IndexError):
   295             return None
   273             return None
   296 
   274 
   297     @view.implements_adapter_compat('ITree')
       
   298     def children(self, entities=True, sametype=False):
   275     def children(self, entities=True, sametype=False):
   299         """Return children entities.
   276         """Return children entities.
   300 
   277 
   301         According to the `entities` parameter, return entity objects or the
   278         According to the `entities` parameter, return entity objects or the
   302         equivalent result set.
   279         equivalent result set.
   305             return self.same_type_children(entities)
   282             return self.same_type_children(entities)
   306         else:
   283         else:
   307             return self.entity.related(self.tree_relation, self.parent_role,
   284             return self.entity.related(self.tree_relation, self.parent_role,
   308                                        entities=entities)
   285                                        entities=entities)
   309 
   286 
   310     @view.implements_adapter_compat('ITree')
       
   311     def iterparents(self, strict=True):
   287     def iterparents(self, strict=True):
   312         """Return an iterator on the parents of the entity."""
   288         """Return an iterator on the parents of the entity."""
   313         def _uptoroot(self):
   289         def _uptoroot(self):
   314             curr = self
   290             curr = self
   315             while True:
   291             while True:
   320                 curr = curr.cw_adapt_to('ITree')
   296                 curr = curr.cw_adapt_to('ITree')
   321         if not strict:
   297         if not strict:
   322             return chain([self.entity], _uptoroot(self))
   298             return chain([self.entity], _uptoroot(self))
   323         return _uptoroot(self)
   299         return _uptoroot(self)
   324 
   300 
   325     @view.implements_adapter_compat('ITree')
       
   326     def iterchildren(self, _done=None):
   301     def iterchildren(self, _done=None):
   327         """Return an iterator over the item's children."""
   302         """Return an iterator over the item's children."""
   328         if _done is None:
   303         if _done is None:
   329             _done = set()
   304             _done = set()
   330         for child in self.children():
   305         for child in self.children():
   332                 self.error('loop in %s tree: %s', child.cw_etype.lower(), child)
   307                 self.error('loop in %s tree: %s', child.cw_etype.lower(), child)
   333                 continue
   308                 continue
   334             yield child
   309             yield child
   335             _done.add(child.eid)
   310             _done.add(child.eid)
   336 
   311 
   337     @view.implements_adapter_compat('ITree')
       
   338     def prefixiter(self, _done=None):
   312     def prefixiter(self, _done=None):
   339         """Return an iterator over the item's descendants in a prefixed order."""
   313         """Return an iterator over the item's descendants in a prefixed order."""
   340         if _done is None:
   314         if _done is None:
   341             _done = set()
   315             _done = set()
   342         if self.entity.eid in _done:
   316         if self.entity.eid in _done:
   345         yield self.entity
   319         yield self.entity
   346         for child in self.same_type_children():
   320         for child in self.same_type_children():
   347             for entity in child.cw_adapt_to('ITree').prefixiter(_done):
   321             for entity in child.cw_adapt_to('ITree').prefixiter(_done):
   348                 yield entity
   322                 yield entity
   349 
   323 
   350     @view.implements_adapter_compat('ITree')
       
   351     @cached
   324     @cached
   352     def path(self):
   325     def path(self):
   353         """Returns the list of eids from the root object to this object."""
   326         """Returns the list of eids from the root object to this object."""
   354         path = []
   327         path = []
   355         adapter = self
   328         adapter = self