fix keywords comparison in SetDescendantOfKeywordOp stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 15 Apr 2010 18:30:19 +0200
branchstable
changeset 107 d7b05aec0868
parent 106 d9746abcdd02
child 108 6adea1a62e97
fix keywords comparison in SetDescendantOfKeywordOp * * * use yams.schema.role_name for fields name in ValidationError
hooks.py
--- a/hooks.py	Tue Mar 23 11:34:38 2010 +0100
+++ b/hooks.py	Thu Apr 15 18:30:19 2010 +0200
@@ -4,10 +4,105 @@
 :copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
+
+from itertools import chain
+
 from cubicweb import ValidationError
-from cubicweb.server.hook import Hook, Operation
-from itertools import chain
-from cubicweb.server import hook
+from cubicweb.server.hook import Hook, Operation, match_rtype
+from yams.schema import role_name
+
+class BeforeAddDescendantOf(Hook):
+    """check indirect cycle for ``descendant_of`` relation
+    """
+    __regid__ = 'beforeadddescendant'
+    events = ('before_add_relation', )
+    __select__ = Hook.__select__ & match_rtype('descendant_of')
+
+    def __call__(self):
+        entity = self._cw.entity_from_eid(self.eidfrom)
+        parent = self._cw.entity_from_eid(self.eidto)
+        parents = set([x.eid for x in chain([parent,], parent.iterparents())])
+        children = set([x.eid for x in chain([entity], entity.iterchildren())])
+        if children & parents:
+            msg = _('detected descendant_of cycle')
+            raise ValidationError(self.eidfrom, {role_name(self.rtype, 'subject'): msg})
+
+
+class AfterAddSubKeywordOf(Hook):
+    """sets ``descendant_of`` relation
+    """
+    __regid__ = 'afteradddescendant'
+    events = ('after_add_relation', )
+    __select__ = Hook.__select__ & match_rtype('subkeyword_of')
+
+    def __call__(self):
+        entity = self._cw.entity_from_eid(self.eidfrom)
+        parent = self._cw.entity_from_eid(self.eidto)
+        SetDescendantOfKeywordOp(self._cw, parent=parent, entity=entity)
+
+class SetIncludedInRelationHook(Hook):
+    """sets the included_in relation on a subkeyword if not already set
+    """
+    __regid__ = 'setincludedinrelationhook'
+    events = ('before_add_relation',)
+    __select__ = Hook.__select__ & match_rtype('subkeyword_of')
+
+    def __call__(self):
+        # immediate test direct cycles
+        if self.eidfrom == self.eidto:
+            msg = self._cw._('keyword cannot be subkeyword of himself')
+            raise ValidationError(self.eidfrom, {role_name(self.rtype, 'subject') : msg})
+        SetIncludedInRelationOp(self._cw, vreg=self._cw.vreg,
+                                eidfrom=self.eidfrom, eidto=self.eidto)
+
+
+
+class RemoveDescendantOfRelation(Hook):
+    """removes ``descendant_of`` relation
+
+    we delete the relation for entity's parents recursively
+    """
+    __regid__ = 'removedescendantofrelation'
+    events = ('after_delete_relation',)
+    __select__ = Hook.__select__ & match_rtype('subkeyword_of')
+
+    def __call__(self):
+        parent = self._cw.entity_from_eid(self.eidto)
+        for parent in chain([parent], parent.iterparents()):
+            self._cw.execute('DELETE K descendant_of P WHERE K eid %(k)s, '
+                            'P eid %(p)s', {'p':parent.eid, 'k': self.eidfrom})
+
+
+## operations #################################################################
+class SetIncludedInRelationOp(Operation):
+    """delay this operation to commit to avoid conflict with a late rql query
+    already setting the relation
+    """
+    def precommit_event(self):
+        session = self.session
+        # test for indirect cycles
+        self.check_cycle()
+        subkw = session.entity_from_eid(self.eidfrom)
+        if subkw.included_in:
+            kw = session.entity_from_eid(self.eidto)
+            if subkw.included_in[0].eid != kw.included_in[0].eid:
+                msgid = "keywords %(subkw)s and %(kw)s belong to different classifications"
+                raise ValidationError(subkw.eid, {role_name('subkeyword_of', 'subject'): session._(msgid) %
+                                                  {'subkw' : subkw.eid, 'kw' : kw.eid}})
+        else:
+            session.execute('SET SK included_in C WHERE SK eid %(x)s, '
+                            'SK subkeyword_of K, K included_in C',
+                            {'x': subkw.eid}, 'x')
+
+    def check_cycle(self):
+        parents = set([self.eidto])
+        parent = self.session.entity_from_eid(self.eidto)
+        while parent.subkeyword_of:
+            parent = parent.subkeyword_of[0]
+            if parent.eid in parents:
+                msg = self.session._('detected subkeyword cycle')
+                raise ValidationError(self.eidfrom, {role_name('subkeyword_of', 'subject'): msg})
+            parents.add(parent.eid)
 
 
 class SetDescendantOfKeywordOp(Operation):
@@ -21,101 +116,6 @@
                 if child.eid != parent.eid:
                     closure.add((child, parent))
         for child, parent in closure:
-            if not parent in child.descendant_of:
-                self.session.execute('SET C descendant_of P WHERE C eid %(c)s, P eid %(p)s',
-                                     {'c': child.eid, 'p':parent.eid})
-
-
-class BeforeAddDescendantOf(Hook):
-    """check indirect cycle for ``descendant_of`` relation
-    """
-    __regid__ = 'beforeadddescendant'
-    events = ('before_add_relation', )
-    __select__ = Hook.__select__ & hook.match_rtype('descendant_of')
-
-    def __call__(self):
-        entity = self._cw.entity_from_eid(self.eidfrom)
-        parent = self._cw.entity_from_eid(self.eidto)
-        parents = set([x.eid for x in chain([parent,], parent.iterparents())])
-        children = set([x.eid for x in chain([entity], entity.iterchildren())])
-        if children & parents:
-            msg = _('detected descendant_of cycle')
-            raise ValidationError(self.eidfrom, {'descendant_of': msg})
-
-
-class AfterAddSubKeywordOf(Hook):
-    """sets ``descendant_of`` relation
-    """
-    __regid__ = 'afteradddescendant'
-    events = ('after_add_relation', )
-    __select__ = Hook.__select__ & hook.match_rtype('subkeyword_of')
-
-    def __call__(self):
-        entity = self._cw.entity_from_eid(self.eidfrom)
-        parent = self._cw.entity_from_eid(self.eidto)
-        SetDescendantOfKeywordOp(self._cw, parent=parent, entity=entity)
-
-
-class SetIncludedInRelationOp(Operation):
-    """delay this operation to commit to avoid conflict with a late rql query
-    already setting the relation
-    """
-    def precommit_event(self):
-        session = self.session
-        # test for indirect cycles
-        self.check_cycle()
-        subkw = session.entity_from_eid(self.eidfrom)
-        if subkw.included_in:
-            kw = session.entity_from_eid(self.eidto)
-            if subkw.included_in[0].eid != kw.included_in[0].eid:
-                msgid = "keywords %(subkw)s and %(kw)s belong to different classifications"
-                raise ValidationError(subkw.eid, {'subkeyword_of': session._(msgid) %
-                                                  {'subkw' : subkw.eid, 'kw' : kw.eid}})
-        else:
-            session.execute('SET SK included_in C WHERE SK eid %(x)s, '
-                            'SK subkeyword_of K, K included_in C',
-                            {'x': subkw.eid}, 'x')
-
-    def check_cycle(self):
-        parents = set([self.eidto])
-        parent = self.session.entity_from_eid(self.eidto)
-        while parent.subkeyword_of:
-            parent = parent.subkeyword_of[0]
-            if parent.eid in parents:
-                msg = self.session._('detected subkeyword cycle')
-                raise ValidationError(self.eidfrom, {'subkeyword_of': msg})
-            parents.add(parent.eid)
-
-
-class SetIncludedInRelationHook(Hook):
-    """sets the included_in relation on a subkeyword if not already set
-    """
-    __regid__ = 'setincludedinrelationhook'
-    events = ('before_add_relation',)
-    __select__ = Hook.__select__ & hook.match_rtype('subkeyword_of')
-
-    def __call__(self):
-        # immediate test direct cycles
-        if self.eidfrom == self.eidto:
-            msg = self._cw._('keyword cannot be subkeyword of himself')
-            raise ValidationError(self.eidfrom, {self.rtype : msg})
-        SetIncludedInRelationOp(self._cw, vreg=self._cw.vreg,
-                                eidfrom=self.eidfrom, eidto=self.eidto)
-
-
-class RemoveDescendantOfRelation(Hook):
-    """removes ``descendant_of`` relation
-
-    we delete the relation for entity's parents recursively
-    """
-    __regid__ = 'removedescendantofrelation'
-    events = ('after_delete_relation',)
-    __select__ = Hook.__select__ & hook.match_rtype('subkeyword_of')
-
-    def __call__(self):
-        parent = self._cw.entity_from_eid(self.eidto)
-        for parent in chain([parent], parent.iterparents()):
-            self._cw.execute('DELETE K descendant_of P WHERE K eid %(k)s, '
-                            'P eid %(p)s', {'p':parent.eid, 'k': self.eidfrom})
-
-
+            descendants = [p.eid for p in child.descendant_of]
+            if not parent.eid in descendants:
+                child.set_relations(descendant_of=parent)