cubicweb/entities/__init__.py
changeset 11057 0b59724cb3f2
parent 10847 ce5403611cbe
child 11767 432f87a63057
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """base application's entities class implementation: `AnyEntity`"""
       
    19 
       
    20 __docformat__ = "restructuredtext en"
       
    21 
       
    22 from warnings import warn
       
    23 
       
    24 from six import text_type, string_types
       
    25 
       
    26 from logilab.common.decorators import classproperty
       
    27 from logilab.common.deprecation import deprecated
       
    28 
       
    29 from cubicweb import Unauthorized
       
    30 from cubicweb.entity import Entity
       
    31 
       
    32 
       
    33 class AnyEntity(Entity):
       
    34     """an entity instance has e_schema automagically set on the class and
       
    35     instances have access to their issuing cursor
       
    36     """
       
    37     __regid__ = 'Any'
       
    38 
       
    39     @classproperty
       
    40     def cw_etype(cls):
       
    41         """entity type as a unicode string"""
       
    42         return text_type(cls.__regid__)
       
    43 
       
    44     @classmethod
       
    45     def cw_create_url(cls, req, **kwargs):
       
    46         """ return the url of the entity creation form for this entity type"""
       
    47         return req.build_url('add/%s' % cls.__regid__, **kwargs)
       
    48 
       
    49     @classmethod
       
    50     @deprecated('[3.22] use cw_fti_index_rql_limit instead')
       
    51     def cw_fti_index_rql_queries(cls, req):
       
    52         """return the list of rql queries to fetch entities to FT-index
       
    53 
       
    54         The default is to fetch all entities at once and to prefetch
       
    55         indexable attributes but one could imagine iterating over
       
    56         "smaller" resultsets if the table is very big or returning
       
    57         a subset of entities that match some business-logic condition.
       
    58         """
       
    59         restrictions = ['X is %s' % cls.__regid__]
       
    60         selected = ['X']
       
    61         for attrschema in sorted(cls.e_schema.indexable_attributes()):
       
    62             varname = attrschema.type.upper()
       
    63             restrictions.append('X %s %s' % (attrschema, varname))
       
    64             selected.append(varname)
       
    65         return ['Any %s WHERE %s' % (', '.join(selected),
       
    66                                      ', '.join(restrictions))]
       
    67 
       
    68     @classmethod
       
    69     def cw_fti_index_rql_limit(cls, req, limit=1000):
       
    70         """generate rsets of entities to FT-index
       
    71 
       
    72         By default, each successive result set is limited to 1000 entities
       
    73         """
       
    74         if cls.cw_fti_index_rql_queries.__func__ != AnyEntity.cw_fti_index_rql_queries.__func__:
       
    75             warn("[3.22] cw_fti_index_rql_queries is replaced by cw_fti_index_rql_limit",
       
    76                  DeprecationWarning)
       
    77             for rql in cls.cw_fti_index_rql_queries(req):
       
    78                 yield req.execute(rql)
       
    79             return
       
    80         restrictions = ['X is %s' % cls.__regid__]
       
    81         selected = ['X']
       
    82         start = 0
       
    83         for attrschema in sorted(cls.e_schema.indexable_attributes()):
       
    84             varname = attrschema.type.upper()
       
    85             restrictions.append('X %s %s' % (attrschema, varname))
       
    86             selected.append(varname)
       
    87         while True:
       
    88             q_restrictions = restrictions + ['X eid > %s' % start]
       
    89             rset = req.execute('Any %s ORDERBY X LIMIT %s WHERE %s' %
       
    90                                (', '.join(selected),
       
    91                                 limit,
       
    92                                 ', '.join(q_restrictions)))
       
    93             if rset:
       
    94                 start = rset[-1][0]
       
    95                 yield rset
       
    96             else:
       
    97                 break
       
    98 
       
    99     # meta data api ###########################################################
       
   100 
       
   101     def dc_title(self):
       
   102         """return a suitable *unicode* title for this entity"""
       
   103         for rschema, attrschema in self.e_schema.attribute_definitions():
       
   104             if rschema.meta:
       
   105                 continue
       
   106             value = self.cw_attr_value(rschema.type)
       
   107             if value is not None:
       
   108                 # make the value printable (dates, floats, bytes, etc.)
       
   109                 return self.printable_value(rschema.type, value, attrschema.type,
       
   110                                             format='text/plain')
       
   111         return u'%s #%s' % (self.dc_type(), self.eid)
       
   112 
       
   113     def dc_long_title(self):
       
   114         """return a more detailled title for this entity"""
       
   115         return self.dc_title()
       
   116 
       
   117     def dc_description(self, format='text/plain'):
       
   118         """return a suitable description for this entity"""
       
   119         if 'description' in self.e_schema.subjrels:
       
   120             return self.printable_value('description', format=format)
       
   121         return u''
       
   122 
       
   123     def dc_authors(self):
       
   124         """return a suitable description for the author(s) of the entity"""
       
   125         try:
       
   126             return ', '.join(u.name() for u in self.owned_by)
       
   127         except Unauthorized:
       
   128             return u''
       
   129 
       
   130     def dc_creator(self):
       
   131         """return a suitable description for the creator of the entity"""
       
   132         if self.creator:
       
   133             return self.creator.name()
       
   134         return u''
       
   135 
       
   136     def dc_date(self, date_format=None):# XXX default to ISO 8601 ?
       
   137         """return latest modification date of this entity"""
       
   138         return self._cw.format_date(self.modification_date, date_format=date_format)
       
   139 
       
   140     def dc_type(self, form=''):
       
   141         """return the display name for the type of this entity (translated)"""
       
   142         return self.e_schema.display_name(self._cw, form)
       
   143 
       
   144     def dc_language(self):
       
   145         """return language used by this entity (translated)"""
       
   146         # check if entities has internationalizable attributes
       
   147         # XXX one is enough or check if all String attributes are internationalizable?
       
   148         for rschema, attrschema in self.e_schema.attribute_definitions():
       
   149             if rschema.rdef(self.e_schema, attrschema).internationalizable:
       
   150                 return self._cw._(self._cw.user.property_value('ui.language'))
       
   151         return self._cw._(self._cw.vreg.property_value('ui.language'))
       
   152 
       
   153     @property
       
   154     def creator(self):
       
   155         """return the CWUser entity which has created this entity, or None if
       
   156         unknown or if the curent user doesn't has access to this euser
       
   157         """
       
   158         try:
       
   159             return self.created_by[0]
       
   160         except (Unauthorized, IndexError):
       
   161             return None
       
   162 
       
   163     # abstractions making the whole things (well, some at least) working ######
       
   164 
       
   165     def sortvalue(self, rtype=None):
       
   166         """return a value which can be used to sort this entity or given
       
   167         entity's attribute
       
   168         """
       
   169         if rtype is None:
       
   170             return self.dc_title().lower()
       
   171         value = self.cw_attr_value(rtype)
       
   172         # do not restrict to `unicode` because Bytes will return a `str` value
       
   173         if isinstance(value, string_types):
       
   174             return self.printable_value(rtype, format='text/plain').lower()
       
   175         return value
       
   176 
       
   177 
       
   178 def fetch_config(fetchattrs, mainattr=None, pclass=AnyEntity, order='ASC'):
       
   179     """function to ease basic configuration of an entity class ORM. Basic usage
       
   180     is:
       
   181 
       
   182     .. sourcecode:: python
       
   183 
       
   184       class MyEntity(AnyEntity):
       
   185 
       
   186           fetch_attrs, cw_fetch_order = fetch_config(['attr1', 'attr2'])
       
   187           # uncomment line below if you want the same sorting for 'unrelated' entities
       
   188           # cw_fetch_unrelated_order = cw_fetch_order
       
   189 
       
   190     Using this, when using ORM methods retrieving this type of entity, 'attr1'
       
   191     and 'attr2' will be automatically prefetched and results will be sorted on
       
   192     'attr1' ascending (ie the first attribute in the list).
       
   193 
       
   194     This function will automatically add to fetched attributes those defined in
       
   195     parent class given using the `pclass` argument.
       
   196 
       
   197     Also, You can use `mainattr` and `order` argument to have a different
       
   198     sorting.
       
   199     """
       
   200     if pclass is not None:
       
   201         fetchattrs += pclass.fetch_attrs
       
   202     if mainattr is None:
       
   203         mainattr = fetchattrs[0]
       
   204     @classmethod
       
   205     def fetch_order(cls, select, attr, var):
       
   206         if attr == mainattr:
       
   207             select.add_sort_var(var, order=='ASC')
       
   208     return fetchattrs, fetch_order