backport stable branch
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
Mon, 05 Jul 2010 13:33:55 +0200
changeset 112 a24428726e54
parent 103 b27eb54052f6 (current diff)
parent 111 4b1b08d844a3 (diff)
child 113 45a0bff67d7e
backport stable branch
--- a/.hgtags	Wed Feb 10 16:22:04 2010 +0100
+++ b/.hgtags	Mon Jul 05 13:33:55 2010 +0200
@@ -8,3 +8,5 @@
 39b0f2292d0fa4a80c861c5db82a4cd3ce312f64 cubicweb-keyword-debian-version-1.5.0-1
 b9c77c4c40b8f37dd0f27c3397ec2fdc8fc032f6 cubicweb-keyword-version-1.6.0
 294a50fe97722d9285bbb30762c9040a4bc23214 cubicweb-keyword-debian-version-1.6.0-1
+cbda8e6981069dc2fbb653a545c40950fad101dc oldstable
+36d3106df60c89d8d79fde1b56a73806a19567f9 1.6.1
--- a/MANIFEST.in	Wed Feb 10 16:22:04 2010 +0100
+++ b/MANIFEST.in	Mon Jul 05 13:33:55 2010 +0200
@@ -2,3 +2,5 @@
 recursive-include data external_resources *.gif *.js
 recursive-include i18n *.pot *.po
 recursive-include migration *.py
+recursive-include test *.py
+recursive-include test/data bootstrap_cubes  *.py
--- a/__pkginfo__.py	Wed Feb 10 16:22:04 2010 +0100
+++ b/__pkginfo__.py	Mon Jul 05 13:33:55 2010 +0200
@@ -4,7 +4,7 @@
 modname = 'keyword'
 distname = "cubicweb-keyword"
 
-numversion = (1, 6, 0)
+numversion = (1, 6, 1)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'LGPL'
@@ -15,29 +15,34 @@
 author_email = "contact@logilab.fr"
 web = 'http://www.cubicweb.org/project/%s' % distname
 
-short_desc = "keyword component for the Cubicweb framework"
+short_desc = "classification schemes system for the Cubicweb framework"
 long_desc = """Summary
 -------
 The `keyword` cube provides classification by using hierarchies of keywords to
 classify content.
 
+Each classification is represented using a `Classification` entity, which will
+hold a keywords tree.
+
 There is two types of keywords:
 
 - `Keyword` which contains a description,
-- `CodeKeyword` which contains the keyword description and the
-  associated code.
+
+- `CodeKeyword` which contains the keyword description and the associated code.
 
-In order to link an entity to a keyword, you have to add a relation
-`applied_to` in the schema.
+In order to link an entity to a keyword, you have to add a relation `applied_to`
+in the schema.
 
-Each keyword has the `subkeyword_of` relation definition. This allows
-to navigate in the classification without a Modified Preorder Tree
-Traversal representation of the data.
+Each keyword has the `subkeyword_of` relation definition. This allows to
+navigate in the classification without a Modified Preorder Tree Traversal
+representation of the data.
 
-Some methods are defined in order to get parents and children or get
-the status of a keyword (leaf or root).
+Some methods are defined in order to get parents and children or get the status
+of a keyword (leaf or root).
 
-See also cubicweb-tag as another (simpler) way to classify content.
+See also `cubicweb-tag`_ as another (simpler) way to classify content.
+
+.. _`cubicweb-tag`: http://www.cubicweb.org/project/cubicweb-tag
 """
 
 classifiers = [
@@ -51,22 +56,33 @@
 __depends__ = {'cubicweb': '>= 3.6.0'}
 __use__ = tuple(__depends_cubes__)
 
-from os import listdir
-from os.path import join
+from os import listdir as _listdir
+from os.path import join, isdir
+from glob import glob
+
+THIS_CUBE_DIR = join('share', 'cubicweb', 'cubes', modname)
 
-CUBES_DIR = join('share', 'cubicweb', 'cubes')
+def listdir(dirpath):
+    return [join(dirpath, fname) for fname in _listdir(dirpath)
+            if fname[0] != '.' and not fname.endswith('.pyc')
+            and not fname.endswith('~')
+            and not isdir(join(dirpath, fname))]
 try:
     data_files = [
-        [join(CUBES_DIR, 'keyword'),
-         [fname for fname in listdir('.')
-          if fname.endswith('.py') and fname != 'setup.py']],
-        [join(CUBES_DIR, 'keyword', 'data'),
-         [join('data', fname) for fname in listdir('data')]],
-        [join(CUBES_DIR, 'keyword', 'i18n'),
-         [join('i18n', fname) for fname in listdir('i18n')]],
-        [join(CUBES_DIR, 'keyword', 'migration'),
-         [join('migration', fname) for fname in listdir('migration')]],
-        ]
+        # common files
+        [THIS_CUBE_DIR, [fname for fname in glob('*.py') if fname != 'setup.py']],
+        [join(THIS_CUBE_DIR, 'test'), [fname for fname in glob('test/*.py')]],
+
+    ]
+    for dname in ('data', 'i18n', 'migration', ):
+        if isdir(dname):
+            data_files.append([join(THIS_CUBE_DIR, dname), listdir(dname)])
+
+    # Note: here, you'll need to add subdirectories if you want
+    # them to be included in the debian package
+    for dname in ('data', ):
+        data_files.append([join(THIS_CUBE_DIR, 'test', dname), listdir(join('test', dname))])
+
 except OSError:
     # we are in an installed directory
     pass
--- a/debian/changelog	Wed Feb 10 16:22:04 2010 +0100
+++ b/debian/changelog	Mon Jul 05 13:33:55 2010 +0200
@@ -1,3 +1,9 @@
+cubicweb-keyword (1.6.1-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Katia Saurfelt <katia.saurfelt@logilab.fr>  Fri, 16 Apr 2010 11:13:12 +0100
+
 cubicweb-keyword (1.6.0-1) unstable; urgency=low
 
   * new upstream release
--- a/entities.py	Wed Feb 10 16:22:04 2010 +0100
+++ b/entities.py	Mon Jul 05 13:33:55 2010 +0200
@@ -6,6 +6,8 @@
 """
 __docformat__ = "restructuredtext en"
 
+from logilab.common.deprecation import  deprecated
+
 from cubicweb.mixins import MI_REL_TRIGGERS, TreeMixIn
 from cubicweb.entities import AnyEntity, fetch_config
 from cubicweb.interfaces import ITree
@@ -44,6 +46,7 @@
         """returns true if this node has no parent"""
         return True
 
+    @deprecated('[3.6] was specific to external project')
     def first_level_keywords(self):
         return self.req.execute('Any K,N ORDERBY N WHERE K included_in C, '
                                 'NOT K subkeyword_of KK, K name N, '
@@ -54,7 +57,6 @@
     __regid__ = 'Keyword'
     fetch_attrs, fetch_order = fetch_config(['name'])
     __implements__ = AnyEntity.__implements__ + (ITree,)
-
     tree_attribute = 'subkeyword_of'
 
     @property
@@ -64,13 +66,16 @@
         return None
 
     def parent(self):
-        """IBreadcrumbs implementation"""
-        if self.subkeyword_of:
-            return self.subkeyword_of[0]
-        return self.classification
+        """ITree + IBreadcrumbs implementation"""
+        try:
+            return self.related(self.tree_attribute, self.parent_target,
+                                entities=True)[0]
+        except (KeyError, IndexError):
+            return self.classification
 
     def iterparents(self):
-        """returns parent keyword entities
+        """returns parent keyword entities,
+           without the root classification
         """
         if self.subkeyword_of:
             parent = self.subkeyword_of[0]
@@ -81,13 +86,6 @@
                 else:
                     parent = None
 
-    def children(self, entities=True):
-        """returns the item's children
-
-        we have only one direct child by ``subkeyword_of`` relation"""
-        assert 1 == len(self.reverse_subkeyword_of)
-        return iter(self.reverse_subkeyword_of)
-
     def iterchildren(self):
         """returns children entities"""
         if self.reverse_subkeyword_of:
@@ -99,24 +97,18 @@
                 else:
                     child = None
 
-    def is_leaf(self):
-        if self.reverse_subkeyword_of:
-            return False
-        return True
-
-    def children_rql(self):
-        return 'Any K WHERE  K subkeyword_of X, X eid %(x)s'
-
-    """
-    # FIXME unittest
-    def subkeywords(self, recursive=True):
-        rset = self.req.execute(self.children_rql(), {'x': self.eid})
-        subentities = list(rset.entities())
-        if recursive:
-            for entity in subentities[:]:
-                subentities.extend(entity.subkeywords(recursive=True))
-        return subentities
-    """
+    def recurse_children(self, _done=None):
+        """returns strict descendents"""
+        if _done is not None and self.eid in _done:
+            return
+        if _done is not None:
+            _done.add(self.eid)
+            yield self
+        else:
+            _done = set()
+        for child in self.children():
+            for entity in child.recurse_children(_done):
+                yield entity
 
 class CodeKeyword(Keyword):
     __regid__ = 'CodeKeyword'
--- a/hooks.py	Wed Feb 10 16:22:04 2010 +0100
+++ b/hooks.py	Mon Jul 05 13:33:55 2010 +0200
@@ -4,43 +4,28 @@
 :copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
-from cubicweb import ValidationError
-from cubicweb.server.hook import Hook, Operation
+
 from itertools import chain
-from cubicweb.server import hook
-
 
-class SetDescendantOfKeywordOp(Operation):
-    def precommit_event(self):
-        """transitive closure of ``descendant_of`` relations to current entity"""
-        closure = set()
-        entity = self.entity
-        parent = self.parent
-        for parent in chain([parent, entity], parent.iterparents()):
-            for child in chain([entity], entity.iterchildren()):
-                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})
-
+from cubicweb import ValidationError
+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__ & hook.match_rtype('descendant_of')
+    __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())])
+        children = set([x.eid for x in chain([entity], entity.recurse_children())])
         if children & parents:
             msg = _('detected descendant_of cycle')
-            raise ValidationError(self.eidfrom, {'descendant_of': msg})
+            raise ValidationError(self.eidfrom, {role_name(self.rtype, 'subject'): msg})
 
 
 class AfterAddSubKeywordOf(Hook):
@@ -48,14 +33,47 @@
     """
     __regid__ = 'afteradddescendant'
     events = ('after_add_relation', )
-    __select__ = Hook.__select__ & hook.match_rtype('subkeyword_of')
+    __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
@@ -69,7 +87,7 @@
             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) %
+                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, '
@@ -83,39 +101,21 @@
             parent = parent.subkeyword_of[0]
             if parent.eid in parents:
                 msg = self.session._('detected subkeyword cycle')
-                raise ValidationError(self.eidfrom, {'subkeyword_of': msg})
+                raise ValidationError(self.eidfrom, {role_name('subkeyword_of', 'subject'): 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})
-
-
+class SetDescendantOfKeywordOp(Operation):
+    def precommit_event(self):
+        """transitive closure of ``descendant_of`` relations to current entity"""
+        closure = set()
+        entity = self.entity
+        parent = self.parent
+        for parent in chain([parent, entity], parent.iterparents()):
+            for child in chain([entity], entity.recurse_children()):
+                if child.eid != parent.eid:
+                    closure.add((child, parent))
+        for child, parent in closure:
+            descendants = [p.eid for p in child.descendant_of]
+            if not parent.eid in descendants:
+                child.set_relations(descendant_of=parent)
--- a/test/data/schema.py	Wed Feb 10 16:22:04 2010 +0100
+++ b/test/data/schema.py	Mon Jul 05 13:33:55 2010 +0200
@@ -2,4 +2,4 @@
 
 class applied_to(RelationDefinition):
     subject = 'Keyword'
-    object = ('CWGroup', 'CWUser')
+    object = ('CWUser', 'CWGroup')
--- a/test/unittest_classification.py	Wed Feb 10 16:22:04 2010 +0100
+++ b/test/unittest_classification.py	Mon Jul 05 13:33:55 2010 +0200
@@ -6,39 +6,46 @@
 class ClassificationHooksTC(CubicWebTC):
 
     def setup_database(self):
-        self.execute('INSERT Classification C: C name "classif1", C classifies ET WHERE ET name "CWGroup"')
-        self.execute('INSERT Classification C: C name "classif2", C classifies ET WHERE ET name "CWUser"')
-        self.kw1 = self.execute('INSERT Keyword K: K name "kwgroup", K included_in C WHERE C name "classif1"')[0][0]
-        self.kw2 = self.execute('INSERT Keyword K: K name "kwuser", K included_in C WHERE C name "classif2"')[0][0]
-        #self.zone = self.add_entity('Zone', name=u"Paris")
+        req = self.request()
+        # classification for CWGroup
+        cwgroup = self.execute('Any U WHERE U is ET, U name "CWGroup"').get_entity(0,0)
+        self.classif1 = self.request().create_entity('Classification', name=u"classif1", classifies=cwgroup)
+        self.kwgroup = req.create_entity('Keyword', name=u'kwgroup', included_in=self.classif1)
+        # classification for CWUser
+        cwuser = self.execute('Any U WHERE U is ET, U name "CWUser"').get_entity(0,0)
+        self.classif2 = self.request().create_entity('Classification', name=u"classif2", classifies=cwuser)
+        self.kwuser = req.create_entity('Keyword', name=u'kwuser', included_in=self.classif2)
 
     def test_application_of_bad_keyword_fails(self):
-        self.execute('SET K applied_to G WHERE G is CWGroup, K name "kwuser"')
+        self.execute('SET K applied_to G WHERE G is CWGroup, K eid %(k)s',
+                     {'k':self.kwuser.eid})
         self.assertRaises(ValidationError, self.commit)
 
-
     def test_creating_a_new_subkeyword_sets_included_in(self):
-        self.execute('INSERT Keyword SK: SK name "kwgroup2", SK subkeyword_of K '
-                     'WHERE K name "kwgroup"')
+        req = self.request()
+        kwgroup2 = req.create_entity('Keyword', name=u'kwgroup2', subkeyword_of=self.kwgroup)
         self.commit()
-        rset = self.execute('Any N WHERE C name N, K included_in C, K name "kwgroup2"')
+        rset = self.execute('Any N WHERE C name N, K included_in C, K eid %(k)s', {'k':kwgroup2.eid})
         self.assertEquals(len(rset), 1)
         self.assertEqual(rset[0][0], 'classif1')
 
 
     def test_cannot_create_subkeyword_from_other_classification(self):
-        self.execute('SET X subkeyword_of Y WHERE X eid %s, Y eid %s' % (self.kw1, self.kw2))
+        self.execute('SET K1 subkeyword_of K2 WHERE K1 eid %(k1)s, K2 eid %(k2)s',
+                     {'k1':self.kwgroup.eid, 'k2':self.kwuser.eid})
         self.assertRaises(ValidationError, self.commit)
 
 
     def test_cannot_create_cycles(self):
+        req = self.request()
         # direct obvious cycle
-        self.assertRaises(ValidationError, self.execute,
-                          'SET X subkeyword_of Y WHERE X eid %s, Y eid %s' % (self.kw1, self.kw1))
+        self.execute('SET K1 subkeyword_of K2 WHERE K1 eid %(k1)s, K2 eid %(k2)s',
+                          {'k1':self.kwgroup.eid, 'k2':self.kwuser.eid})
+        self.assertRaises(ValidationError, self.commit)
         # testing indirect cycles
-        kw3 = self.execute('INSERT Keyword SK: SK name "kwgroup2", SK included_in C, '
-                           'SK subkeyword_of K WHERE C name "classif1", K eid %s' % self.kw1)[0][0]
-        self.execute('SET X subkeyword_of Y WHERE X eid %s, Y eid %s' % (self.kw1, kw3))
+        kwgroup2 =  req.create_entity('Keyword', name=u'kwgroup2', included_in=self.classif1, subkeyword_of=self.kwgroup)
+        self.execute('SET K subkeyword_of K2 WHERE K eid %(k)s, K2 eid %(k2)s',
+                     {'k':self.kwgroup.eid, 'k2':kwgroup2.eid})
         self.assertRaises(ValidationError, self.commit)
 
 
--- a/test/unittest_descendant_of.py	Wed Feb 10 16:22:04 2010 +0100
+++ b/test/unittest_descendant_of.py	Mon Jul 05 13:33:55 2010 +0200
@@ -6,144 +6,169 @@
 class KeywordHooksTC(CubicWebTC):
 
     def setup_database(self):
-        self.execute('INSERT Classification C: C name "classif1", C classifies ET WHERE ET name "CWGroup"')
-        self.execute('INSERT Classification C: C name "classif2", C classifies ET WHERE ET name "CWUser"')
-        self.kw1 = self.execute('INSERT Keyword K: K name "kwgroup", K included_in C WHERE C name "classif1"')[0][0]
-        self.kw2 = self.execute('INSERT Keyword K: K name "kwuser", K included_in C WHERE C name "classif2"')[0][0]
+        cwgroup = self.execute('Any U WHERE U is ET, U name "CWGroup"').get_entity(0,0)
+        self.classif1 = self.request().create_entity('Classification', name=u"classif1", classifies=cwgroup)
 
     def test_keyword_add1(self):
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw2"')
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw3"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw4"')
+        req = self.request()
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', subkeyword_of=kw2, included_in=self.classif1)
+        kw4 = req.create_entity('Keyword', name=u'kw4', subkeyword_of=kw3, included_in=self.classif1)
+        kw5 = req.create_entity('Keyword', name=u'kw5', subkeyword_of=kw4, included_in=self.classif1)
         self.commit()
-        parent = self.execute('Any K WHERE K is Keyword, K name "kw1"').get_entity(0, 0)
-        child = self.execute('Any K WHERE K is Keyword, K name "kw5"').get_entity(0, 0)
+        parent = kw1
+        child = kw5
         self.assertUnorderedIterableEquals([kw.name for kw in child.iterparents()], ['kw4', 'kw3', 'kw2', 'kw1'])
         self.assertUnorderedIterableEquals([kw.name for kw in child.descendant_of], ['kw4', 'kw3', 'kw2', 'kw1'])
         self.assertUnorderedIterableEquals([kw.name for kw in parent.reverse_descendant_of], ['kw5', 'kw4', 'kw3', 'kw2'])
-        self.assertUnorderedIterableEquals([kw.name for kw in parent.iterchildren()], ['kw5', 'kw4', 'kw3', 'kw2'])
+        self.assertUnorderedIterableEquals([kw.name for kw in parent.recurse_children()], ['kw5', 'kw4', 'kw3', 'kw2'])
 
     def test_keyword_add2(self):
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, KK subkeyword_of K WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C, KK subkeyword_of K WHERE C name "classif1", KK name "kw2"')
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C, KK subkeyword_of K WHERE C name "classif1", KK name "kw3"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, KK subkeyword_of K WHERE C name "classif1", KK name "kw4"')
+        req = self.request()
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', included_in=self.classif1)
+        kw4 = req.create_entity('Keyword', name=u'kw4', subkeyword_of=kw3, included_in=self.classif1)
+        kw5 = req.create_entity('Keyword', name=u'kw5',  subkeyword_of=kw4, included_in=self.classif1)
         self.commit()
-        child  = self.execute('Any K WHERE K is Keyword, K name "kw1"').get_entity(0, 0)
-        parent = self.execute('Any K WHERE K is Keyword, K name "kw5"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in child.iterparents()], ['kw2', 'kw3', 'kw4', 'kw5'])
-        self.assertUnorderedIterableEquals([kw.name for kw in child.descendant_of], ['kw2', 'kw3', 'kw4', 'kw5'])
-        self.assertUnorderedIterableEquals([kw.name for kw in parent.reverse_descendant_of], ['kw4', 'kw3', 'kw2', 'kw1'])
-        self.assertUnorderedIterableEquals([kw.name for kw in parent.iterchildren()], ['kw4', 'kw3', 'kw2', 'kw1'])
-
-    def test_keyword_add3(self):
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw3"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw4"')
+        self.execute('SET K3 subkeyword_of K2 WHERE K3 eid %(kw3)s, K2 eid %(kw2)s',
+                     {'kw3':kw3.eid, 'kw2':kw2.eid})
         self.commit()
-        self.execute('SET K subkeyword_of KK WHERE K name "kw3", KK name "kw2"')
-        self.commit()
-        parent = self.execute('Any K WHERE K is Keyword, K name "kw1"').get_entity(0, 0)
-        child = self.execute('Any K WHERE K is Keyword, K name "kw5"').get_entity(0, 0)
+        parent = kw1
+        child = kw5
         self.assertUnorderedIterableEquals([kw.name for kw in child.iterparents()], ['kw4', 'kw3', 'kw2', 'kw1'])
         self.assertUnorderedIterableEquals([kw.name for kw in child.descendant_of], ['kw4', 'kw3', 'kw2', 'kw1'])
         self.assertUnorderedIterableEquals([kw.name for kw in parent.reverse_descendant_of], ['kw5', 'kw4', 'kw3', 'kw2'])
-        self.assertUnorderedIterableEquals([kw.name for kw in parent.iterchildren()], ['kw5', 'kw4', 'kw3', 'kw2'])
+        self.assertUnorderedIterableEquals([kw.name for kw in parent.recurse_children()], ['kw5', 'kw4', 'kw3', 'kw2'])
+
+    def test_keyword_add3(self):
+        req = self.request()
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', included_in=self.classif1)
+        kw4 = req.create_entity('Keyword', name=u'kw4', included_in=self.classif1)
+        kw5 = req.create_entity('Keyword', name=u'kw5', subkeyword_of=kw4, included_in=self.classif1)
+        self.commit()
+        self.execute('SET K2 subkeyword_of K3 WHERE K2 eid %(k2)s, K3 eid %(k3)s',
+                     {'k2': kw4.eid, 'k3': kw3.eid})
+        self.execute('SET K3 subkeyword_of K4 WHERE K3 eid %(k3)s, K4 eid %(k4)s',
+                     {'k3': kw3.eid, 'k4': kw2.eid})
+        self.commit()
+        child  = kw5
+        parent = kw1
+        self.assertUnorderedIterableEquals([kw.name for kw in child.descendant_of], ['kw4', 'kw3', 'kw2', 'kw1'])
+        # XXX check the order of iterparents
+        self.assertUnorderedIterableEquals([kw.name for kw in child.iterparents()], ['kw4', 'kw3', 'kw2', 'kw1'])
+        self.assertUnorderedIterableEquals([kw.name for kw in parent.recurse_children()], ['kw2', 'kw3', 'kw4', 'kw5'])
+        self.assertUnorderedIterableEquals([kw.name for kw in parent.reverse_descendant_of], ['kw2', 'kw3', 'kw4', 'kw5'])
 
     def test_keyword_add4(self):
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, KK subkeyword_of K WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, KK subkeyword_of K WHERE C name "classif1", KK name "kw4"')
-        self.commit()
-        self.execute('SET KK subkeyword_of K WHERE K name "kw3", KK name "kw2"')
-        self.execute('SET KK subkeyword_of K WHERE K name "kw4", KK name "kw3"')
+        req = self.request()
+        kw0 = req.create_entity('Keyword', name=u'kw0', included_in=self.classif1)
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', included_in=self.classif1)
+        kw4 = req.create_entity('Keyword', name=u'kw4', included_in=self.classif1)
+        kw5 = req.create_entity('Keyword', name=u'kw5',  subkeyword_of=kw4, included_in=self.classif1)
+        self.execute('SET K3 subkeyword_of K2 WHERE K3 eid %(kw3)s, K2 eid %(kw2)s',
+                     {'kw2': kw2.eid, 'kw3': kw3.eid})
+        self.commit();
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw1', 'kw2'])
+        self.execute('SET K3 descendant_of K0 WHERE K3 eid %(kw3)s, K0 eid %(kw0)s',
+                      {'kw3': kw3.eid, 'kw0': kw0.eid})
         self.commit()
-        child  = self.execute('Any K WHERE K is Keyword, K name "kw1"').get_entity(0, 0)
-        parent = self.execute('Any K WHERE K is Keyword, K name "kw5"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in child.descendant_of], ['kw2', 'kw3', 'kw4', 'kw5'])
-        self.assertUnorderedIterableEquals([kw.name for kw in child.iterparents()], ['kw2', 'kw3', 'kw4', 'kw5'])
-        self.assertUnorderedIterableEquals([kw.name for kw in parent.iterchildren()], ['kw4', 'kw3', 'kw2', 'kw1'])
-        self.assertUnorderedIterableEquals([kw.name for kw in parent.reverse_descendant_of], ['kw4', 'kw3', 'kw2', 'kw1'])
-
-    def test_keyword_add5(self):
-        self.execute('INSERT Keyword K: K name "kw0", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw4"')
-        self.execute('SET K subkeyword_of KK WHERE K name "kw3", KK name "kw2"')
-        self.commit();
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], ['kw1', 'kw2'])
-        self.execute('SET K descendant_of KK WHERE K name "kw3", KK name "kw0"')
+        kw3.clear_all_caches()
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw0', 'kw1', 'kw2'])
+        self.execute('SET K3 descendant_of K4 WHERE K3 eid %(kw3)s, K4 eid %(kw4)s',
+                      {'kw3': kw3.eid, 'kw4': kw4.eid})
         self.commit()
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], ['kw0', 'kw1', 'kw2'])
-        self.execute('SET K descendant_of KK WHERE K name "kw3", KK name "kw4"')
+        kw3.clear_all_caches()
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw0', 'kw1', 'kw2', 'kw4'])
+        self.execute('SET K3 descendant_of K5 WHERE K3 eid %(kw3)s, K5 eid %(kw5)s',
+                       {'kw3': kw3.eid, 'kw5': kw5.eid})
         self.commit()
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], ['kw0', 'kw1', 'kw2', 'kw4'])
-        self.execute('SET K descendant_of KK WHERE K name "kw3", KK name "kw5"')
-        self.commit()
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], ['kw0', 'kw1', 'kw2', 'kw4', 'kw5'])
+        kw3.clear_all_caches()
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw0', 'kw1', 'kw2', 'kw4', 'kw5'])
 
     def test_keyword_update1(self):
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw4"')
-        self.execute('SET K subkeyword_of KK WHERE K name "kw3", KK name "kw2"')
+        req = self.request()
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', included_in=self.classif1)
+        kw4 = req.create_entity('Keyword', name=u'kw4', included_in=self.classif1)
+        kw5 = req.create_entity('Keyword', name=u'kw5',  subkeyword_of=kw4, included_in=self.classif1)
+        self.execute('SET K3 subkeyword_of K2 WHERE K3 eid %(kw3)s, K2 eid %(kw2)s',
+                      {'kw3': kw3.eid, 'kw2': kw2.eid})
         self.commit();
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], ['kw1', 'kw2'])
-        self.execute('SET K subkeyword_of KK WHERE K name "kw3", KK name "kw4"')
+        kw3 =  req.entity_from_eid(kw3.eid)
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw1', 'kw2'])
+        self.execute('SET K3 subkeyword_of K4 WHERE K3 eid %(kw3)s, K4 eid %(kw4)s',
+                      {'kw3': kw3.eid, 'kw4': kw4.eid})
+        self.commit()
+        kw3.clear_all_caches()
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw4'])
+        self.execute('SET K3 subkeyword_of K5 WHERE K3 eid %(kw3)s, K5 eid %(kw5)s',
+                     {'kw3': kw3.eid, 'kw5': kw5.eid})
         self.commit()
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], ['kw4'])
-        self.execute('SET K subkeyword_of KK WHERE K name "kw3", KK name "kw5"')
+        kw3.clear_all_caches()
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw4', 'kw5'])
+
+    def test_keyword_descendant_of(self):
+        req = self.request()
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', subkeyword_of=kw1, included_in=self.classif1)
         self.commit()
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], ['kw4', 'kw5'])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw2.descendant_of], ['kw1', ])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw1', ])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw1.reverse_descendant_of], ['kw3', 'kw2'])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw1.recurse_children()], ['kw2', 'kw3'])
+        kw0 = req.create_entity('Keyword', name=u'kw0', included_in=self.classif1)
+        self.execute('SET K1 subkeyword_of K0 WHERE K1 eid %(kw1)s, K0 eid %(kw0)s',
+                      {'kw1': kw1.eid, 'kw0': kw0.eid})
+        self.commit();
+        kw1.clear_all_caches()
+        kw2.clear_all_caches()
+        kw3.clear_all_caches()
+        self.assertUnorderedIterableEquals([kw.name for kw in kw0.recurse_children()], ['kw1', 'kw2', 'kw3'])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw0.reverse_descendant_of], ['kw3', 'kw2', 'kw1'])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw1.descendant_of], ['kw0',])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw2.descendant_of], ['kw1', 'kw0'])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], ['kw1', 'kw0'])
+
 
     def test_keyword_delete(self):
         """*after_delete_relation* of ``subkeyword_of``
         """
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw2"')
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw3"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw4"')
+        req = self.request()
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', subkeyword_of=kw2, included_in=self.classif1)
+        kw4 = req.create_entity('Keyword', name=u'kw4', subkeyword_of=kw3, included_in=self.classif1)
+        kw5 = req.create_entity('Keyword', name=u'kw5',  subkeyword_of=kw4, included_in=self.classif1)
         self.commit()
-        self.execute('DELETE K subkeyword_of KK WHERE K is Keyword, K name "kw3"')
+        self.execute('DELETE K subkeyword_of K3 WHERE K is Keyword, K eid %(kw3)s',
+                     {'kw3':kw3.eid})
         self.commit()
-        keyword = self.execute('Any K WHERE K is Keyword, K name "kw3"').get_entity(0, 0)
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.iterparents()], [])
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.descendant_of], [])
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.reverse_descendant_of], ['kw5', 'kw4'])
-        self.assertUnorderedIterableEquals([kw.name for kw in keyword.iterchildren()], ['kw5', 'kw4'])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.iterparents()], [])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.descendant_of], [])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.reverse_descendant_of], ['kw5', 'kw4'])
+        self.assertUnorderedIterableEquals([kw.name for kw in kw3.recurse_children()], ['kw5', 'kw4'])
 
     def test_no_add_descendant_cycle(self):
         """no ``descendant_of`` cycle"""
-        self.execute('INSERT Keyword K: K name "kw1", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw2", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw1"')
-        self.execute('INSERT Keyword K: K name "kw3", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw2"')
+        req = self.request()
+        kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
+        kw2 = req.create_entity('Keyword', name=u'kw2', subkeyword_of=kw1, included_in=self.classif1)
+        kw3 = req.create_entity('Keyword', name=u'kw3', subkeyword_of=kw2, included_in=self.classif1)
         self.commit()
-        rql = 'SET K descendant_of KK WHERE K name "kw1", KK name "kw3"'
+        rql = 'SET K1 descendant_of K3 WHERE K1 eid %(kw1)s, K3 eid %(kw3)s' % {'kw1':kw1.eid,  'kw3':kw3.eid}
         self.assertRaises(ValidationError, self.execute, rql)
-        self.execute('INSERT Keyword K: K name "kw4", K included_in C WHERE C name "classif1"')
-        self.execute('INSERT Keyword K: K name "kw5", K included_in C, K subkeyword_of KK WHERE C name "classif1", KK name "kw4"')
+        kw4 = req.create_entity('Keyword', name=u'kw4', included_in=self.classif1)
+        kw5 = req.create_entity('Keyword', name=u'kw4', subkeyword_of=kw4, included_in=self.classif1)
         self.commit()
-        rql = 'SET K descendant_of KK WHERE K name "kw4", KK name "kw5"'
+        rql = 'SET K4 descendant_of K5 WHERE K4 eid %(kw4)s, K5 eid %(kw5)s' % {'kw4':kw4.eid,  'kw5':kw5.eid}
         self.assertRaises(ValidationError, self.execute, rql)
 
 
--- a/test/unittest_security.py	Wed Feb 10 16:22:04 2010 +0100
+++ b/test/unittest_security.py	Mon Jul 05 13:33:55 2010 +0200
@@ -1,14 +1,15 @@
 from cubicweb.devtools.testlib import CubicWebTC
 
 class SecurityTC(CubicWebTC):
-
     def setup_database(self):
-        self.execute('INSERT Classification C: C name "classif1", C classifies ET WHERE ET name "CWGroup"')
-        self.kw1 = self.execute('INSERT Keyword K: K name "kwgroup", K included_in C WHERE C name "classif1"')[0][0]
+        req = self.request()
+        cwgroup = self.execute('Any U WHERE U is ET, U name "CWGroup"').get_entity(0,0)
+        self.classif1 = self.request().create_entity('Classification', name=u"classif1", classifies=cwgroup)
+        self.kw1 = req.create_entity('Keyword', name=u'kw1', included_in=self.classif1)
 
     def test_nonregr_keyword_selection_as_guest(self):
         self.login('anon')
-        self.execute('Any X ORDERBY Z WHERE X modification_date Z, E eid %(e)s, E applied_to X', {'e':self.kw1}, 'e')
+        self.execute('Any X ORDERBY Z WHERE X modification_date Z, K eid %(k)s, K applied_to X', {'k':self.kw1.eid}, 'k')
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main