cubicweb/hooks/synccomputed.py
changeset 12227 dc333e9104c9
parent 11767 432f87a63057
equal deleted inserted replaced
12226:2e2425d2d54d 12227:dc333e9104c9
    16 # You should have received a copy of the GNU Lesser General Public License along
    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/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """Hooks for synchronizing computed attributes"""
    18 """Hooks for synchronizing computed attributes"""
    19 
    19 
    20 
    20 
    21 from cubicweb import _
       
    22 
       
    23 from collections import defaultdict
    21 from collections import defaultdict
    24 
    22 
    25 from rql import nodes
    23 from rql import nodes
    26 
    24 
    27 from cubicweb.server import hook
    25 from cubicweb.server import hook
    31     """Operation to recompute caches of computed attribute at commit time,
    29     """Operation to recompute caches of computed attribute at commit time,
    32     depending on what's have been modified in the transaction and avoiding to
    30     depending on what's have been modified in the transaction and avoiding to
    33     recompute twice the same attribute
    31     recompute twice the same attribute
    34     """
    32     """
    35     containercls = dict
    33     containercls = dict
       
    34 
    36     def add_data(self, computed_attribute, eid=None):
    35     def add_data(self, computed_attribute, eid=None):
    37         try:
    36         try:
    38             self._container[computed_attribute].add(eid)
    37             self._container[computed_attribute].add(eid)
    39         except KeyError:
    38         except KeyError:
    40             self._container[computed_attribute] = set((eid,))
    39             self._container[computed_attribute] = set((eid,))
    41 
    40 
    42     def precommit_event(self):
    41     def precommit_event(self):
    43         for computed_attribute_rdef, eids in self.get_data().items():
    42         for computed_attribute_rdef, eids in self.get_data().items():
    44             attr = computed_attribute_rdef.rtype
    43             attr = computed_attribute_rdef.rtype
    45             formula  = computed_attribute_rdef.formula
    44             formula = computed_attribute_rdef.formula
    46             select = self.cnx.repo.vreg.rqlhelper.parse(formula).children[0]
    45             select = self.cnx.repo.vreg.rqlhelper.parse(formula).children[0]
    47             xvar = select.get_variable('X')
    46             xvar = select.get_variable('X')
    48             select.add_selected(xvar, index=0)
    47             select.add_selected(xvar, index=0)
    49             select.add_group_var(xvar, index=0)
    48             select.add_group_var(xvar, index=0)
    50             if None in eids:
    49             if None in eids:
   149         # entity types holding some computed attribute {etype: [computed rdefs]}
   148         # entity types holding some computed attribute {etype: [computed rdefs]}
   150         self.computed_attribute_by_etype = defaultdict(list)
   149         self.computed_attribute_by_etype = defaultdict(list)
   151         # depending entity types {dep. etype: {computed rdef: dep. etype attributes}}
   150         # depending entity types {dep. etype: {computed rdef: dep. etype attributes}}
   152         self.computed_attribute_by_etype_attrs = defaultdict(lambda: defaultdict(set))
   151         self.computed_attribute_by_etype_attrs = defaultdict(lambda: defaultdict(set))
   153         # depending relations def {dep. rdef: [computed rdefs]
   152         # depending relations def {dep. rdef: [computed rdefs]
   154         self.computed_attribute_by_relation = defaultdict(list) # by rdef
   153         self.computed_attribute_by_relation = defaultdict(list)  # by rdef
   155         # Walk through all attributes definitions
   154         # Walk through all attributes definitions
   156         for rdef in schema.iter_computed_attributes():
   155         for rdef in schema.iter_computed_attributes():
   157             self.computed_attribute_by_etype[rdef.subject.type].append(rdef)
   156             self.computed_attribute_by_etype[rdef.subject.type].append(rdef)
   158             # extract the relations it depends upon - `rdef.formula_select` is
   157             # extract the relations it depends upon - `rdef.formula_select` is
   159             # expected to have been set by finalize_computed_attributes
   158             # expected to have been set by finalize_computed_attributes
   169                         object_etypes = set(sol[rhs.name] for sol in select.solutions)
   168                         object_etypes = set(sol[rhs.name] for sol in select.solutions)
   170                     else:
   169                     else:
   171                         object_etypes = rschema.objects(subject_etype)
   170                         object_etypes = rschema.objects(subject_etype)
   172                     for object_etype in object_etypes:
   171                     for object_etype in object_etypes:
   173                         if rschema.final:
   172                         if rschema.final:
   174                             attr_for_computations = self.computed_attribute_by_etype_attrs[subject_etype]
   173                             attr_for_computations = self.computed_attribute_by_etype_attrs[
       
   174                                 subject_etype]
   175                             attr_for_computations[rdef].add(rschema.type)
   175                             attr_for_computations[rdef].add(rschema.type)
   176                         else:
   176                         else:
   177                             depend_on_rdef = rschema.rdefs[subject_etype, object_etype]
   177                             depend_on_rdef = rschema.rdefs[subject_etype, object_etype]
   178                             self.computed_attribute_by_relation[depend_on_rdef].append(rdef)
   178                             self.computed_attribute_by_relation[depend_on_rdef].append(rdef)
   179 
   179 
   182             regid = 'computed_attribute.%s_created' % etype
   182             regid = 'computed_attribute.%s_created' % etype
   183             selector = hook.is_instance(etype)
   183             selector = hook.is_instance(etype)
   184             yield type('%sCreatedHook' % etype,
   184             yield type('%sCreatedHook' % etype,
   185                        (EntityWithCACreatedHook,),
   185                        (EntityWithCACreatedHook,),
   186                        {'__regid__': regid,
   186                        {'__regid__': regid,
   187                         '__select__':  hook.Hook.__select__ & selector,
   187                         '__select__': hook.Hook.__select__ & selector,
   188                         'computed_attributes': computed_attributes})
   188                         'computed_attributes': computed_attributes})
   189 
   189 
   190     def generate_relation_change_hooks(self):
   190     def generate_relation_change_hooks(self):
   191         for rdef, computed_attributes in self.computed_attribute_by_relation.items():
   191         for rdef, computed_attributes in self.computed_attribute_by_relation.items():
   192             regid = 'computed_attribute.%s_modified' % rdef.rtype
   192             regid = 'computed_attribute.%s_modified' % rdef.rtype
   196             optimized_computed_attributes = []
   196             optimized_computed_attributes = []
   197             for computed_rdef in computed_attributes:
   197             for computed_rdef in computed_attributes:
   198                 optimized_computed_attributes.append(
   198                 optimized_computed_attributes.append(
   199                     (computed_rdef,
   199                     (computed_rdef,
   200                      _optimize_on(computed_rdef.formula_select, rdef.rtype))
   200                      _optimize_on(computed_rdef.formula_select, rdef.rtype))
   201                      )
   201                 )
   202             yield type('%sModifiedHook' % rdef.rtype,
   202             yield type('%sModifiedHook' % rdef.rtype,
   203                        (RelationInvolvedInCAModifiedHook,),
   203                        (RelationInvolvedInCAModifiedHook,),
   204                        {'__regid__': regid,
   204                        {'__regid__': regid,
   205                         '__select__':  hook.Hook.__select__ & selector,
   205                         '__select__': hook.Hook.__select__ & selector,
   206                         'optimized_computed_attributes': optimized_computed_attributes})
   206                         'optimized_computed_attributes': optimized_computed_attributes})
   207 
   207 
   208     def generate_entity_update_hooks(self):
   208     def generate_entity_update_hooks(self):
   209         for etype, attributes_computed_attributes in self.computed_attribute_by_etype_attrs.items():
   209         for etype, attributes_computed_attributes in self.computed_attribute_by_etype_attrs.items():
   210             regid = 'computed_attribute.%s_updated' % etype
   210             regid = 'computed_attribute.%s_updated' % etype
   211             selector = hook.is_instance(etype)
   211             selector = hook.is_instance(etype)
   212             yield type('%sModifiedHook' % etype,
   212             yield type('%sModifiedHook' % etype,
   213                        (AttributeInvolvedInCAModifiedHook,),
   213                        (AttributeInvolvedInCAModifiedHook,),
   214                        {'__regid__': regid,
   214                        {'__regid__': regid,
   215                         '__select__':  hook.Hook.__select__ & selector,
   215                         '__select__': hook.Hook.__select__ & selector,
   216                         'attributes_computed_attributes': attributes_computed_attributes})
   216                         'attributes_computed_attributes': attributes_computed_attributes})
   217 
   217 
   218 
   218 
   219 def registration_callback(vreg):
   219 def registration_callback(vreg):
   220     vreg.register_all(globals().values(), __name__)
   220     vreg.register_all(globals().values(), __name__)