merge
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
Tue, 09 Feb 2010 18:49:12 +0100
changeset 4526 75dff36ff7a3
parent 4523 d8127c2bd6b5 (diff)
parent 4525 956a0630abc8 (current diff)
child 4527 67ab70e98488
merge
--- a/devtools/__init__.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/devtools/__init__.py	Tue Feb 09 18:49:12 2010 +0100
@@ -31,8 +31,9 @@
 SYSTEM_RELATIONS = schema.META_RTYPES | set((
     # workflow related
     'workflow_of', 'state_of', 'transition_of', 'initial_state', 'allowed_transition',
-    'destination_state', 'in_state', 'wf_info_for', 'from_state', 'to_state',
+    'destination_state', 'from_state', 'to_state',
     'condition', 'subworkflow', 'subworkflow_state', 'subworkflow_exit',
+    'custom_workflow', 'in_state', 'wf_info_for',
     # cwproperty
     'for_user',
     # schema definition
--- a/devtools/fill.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/devtools/fill.py	Tue Feb 09 18:49:12 2010 +0100
@@ -410,9 +410,9 @@
                     yield query, args
 
     def qargs(self, subjeids, objeids, subjcard, objcard, subjeid, objeid):
-        if subjcard in '?1':
+        if subjcard in '?1+':
             subjeids.remove(subjeid)
-        if objcard in '?1':
+        if objcard in '?1+':
             objeids.remove(objeid)
         return {'subjeid' : subjeid, 'objeid' : objeid}
 
--- a/devtools/testlib.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/devtools/testlib.py	Tue Feb 09 18:49:12 2010 +0100
@@ -503,7 +503,7 @@
         else:
             self.fail('expected a Redirect exception')
 
-    def expect_redirect_publish(self, req, path='view'):
+    def expect_redirect_publish(self, req, path='edit'):
         """call the publish method of the application publisher, expecting to
         get a Redirect exception
         """
@@ -692,31 +692,52 @@
 
 from cubicweb.devtools.fill import insert_entity_queries, make_relations_queries
 
+# XXX cleanup unprotected_entities & all mess
+
 def how_many_dict(schema, cursor, how_many, skip):
-    """compute how many entities by type we need to be able to satisfy relations
-    cardinality
+    """given a schema, compute how many entities by type we need to be able to
+    satisfy relations cardinality.
+
+    The `how_many` argument tells how many entities of which type we want at
+    least.
+
+    Return a dictionary with entity types as key, and the number of entities for
+    this type as value.
     """
-    # compute how many entities by type we need to be able to satisfy relation constraint
     relmap = {}
     for rschema in schema.relations():
         if rschema.final:
             continue
         for subj, obj in rschema.rdefs:
             card = rschema.rdef(subj, obj).cardinality
-            if card[0] in '1?' and len(rschema.subjects(obj)) == 1:
+            # if the relation is mandatory, we'll need at least as many subj and
+            # obj to satisfy it
+            if card[0] in '1+' and card[1] in '1?':
+                # subj has to be linked to at least one obj,
+                # but obj can be linked to only one subj
+                # -> we need at least as many subj as obj to satisfy
+                #    cardinalities for this relation
                 relmap.setdefault((rschema, subj), []).append(str(obj))
-            if card[1] in '1?' and len(rschema.objects(subj)) == 1:
+            if card[1] in '1+' and card[0] in '1?':
+                # reverse subj and obj in the above explanation
                 relmap.setdefault((rschema, obj), []).append(str(subj))
     unprotected = unprotected_entities(schema)
-    for etype in skip:
+    for etype in skip: # XXX (syt) duh? explain or kill
         unprotected.add(etype)
     howmanydict = {}
+    # step 1, compute a base number of each entity types: number of already
+    # existing entities of this type + `how_many`
     for etype in unprotected_entities(schema, strict=True):
         howmanydict[str(etype)] = cursor.execute('Any COUNT(X) WHERE X is %s' % etype)[0][0]
         if etype in unprotected:
             howmanydict[str(etype)] += how_many
+    # step 2, augment nb entity per types to satisfy cardinality constraints,
+    # by recomputing for each relation that constrained an entity type:
+    #
+    # new num for etype = max(current num, sum(num for possible target etypes))
+    #
+    # XXX we should first check there is no cycle then propagate changes
     for (rschema, etype), targets in relmap.iteritems():
-        # XXX should 1. check no cycle 2. propagate changes
         relfactor = sum(howmanydict[e] for e in targets)
         howmanydict[str(etype)] = max(relfactor, howmanydict[etype])
     return howmanydict
--- a/hooks/integrity.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/hooks/integrity.py	Tue Feb 09 18:49:12 2010 +0100
@@ -27,6 +27,7 @@
 _UNIQUE_CONSTRAINTS_LOCK = Lock()
 _UNIQUE_CONSTRAINTS_HOLDER = None
 
+
 def _acquire_unique_cstr_lock(session):
     """acquire the _UNIQUE_CONSTRAINTS_LOCK for the session.
 
@@ -34,19 +35,17 @@
     RQLUniqueConstraint in two different transactions, as explained in
     http://intranet.logilab.fr/jpl/ticket/36564
     """
-    global _UNIQUE_CONSTRAINTS_HOLDER
     asession = session.actual_session()
-    if _UNIQUE_CONSTRAINTS_HOLDER is asession:
+    if 'uniquecstrholder' in asession.transaction_data:
         return
     _UNIQUE_CONSTRAINTS_LOCK.acquire()
-    _UNIQUE_CONSTRAINTS_HOLDER = asession
+    asession.transaction_data['uniquecstrholder'] = True
     # register operation responsible to release the lock on commit/rollback
-    _ReleaseUniqueConstraintsOperation(asession)
+    _ReleaseUniqueConstraintsHook(asession)
 
 def _release_unique_cstr_lock(session):
-    global _UNIQUE_CONSTRAINTS_HOLDER
-    if _UNIQUE_CONSTRAINTS_HOLDER is session:
-        _UNIQUE_CONSTRAINTS_HOLDER = None
+    if 'uniquecstrholder' in session.transaction_data:
+        del session.transaction_data['uniquecstrholder']
         _UNIQUE_CONSTRAINTS_LOCK.release()
 
 class _ReleaseUniqueConstraintsOperation(hook.Operation):
--- a/server/hook.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/server/hook.py	Tue Feb 09 18:49:12 2010 +0100
@@ -238,7 +238,13 @@
 # base classes for relation propagation ########################################
 
 class PropagateSubjectRelationHook(Hook):
-    """propagate permissions and nosy list when new entity are added"""
+    """propagate some `main_rtype` relation on entities linked as object of
+    `subject_relations` or as subject of `object_relations` (the watched
+    relations).
+
+    This hook ensure that when one of the watched relation is added, the
+    `main_rtype` relation is added to the target entity of the relation.
+    """
     events = ('after_add_relation',)
 
     # to set in concrete class
@@ -247,9 +253,10 @@
     object_relations = None
 
     def __call__(self):
+        assert self.main_rtype
         for eid in (self.eidfrom, self.eidto):
             etype = self._cw.describe(eid)[0]
-            if not self._cw.vreg.schema.eschema(etype).has_subject_relation(self.main_rtype):
+            if self.main_rtype not in self._cw.vreg.schema.eschema(etype).subjrels:
                 return
         if self.rtype in self.subject_relations:
             meid, seid = self.eidfrom, self.eidto
@@ -263,11 +270,12 @@
 
 
 class PropagateSubjectRelationAddHook(Hook):
-    """propagate on existing entities when a permission or nosy list is added"""
+    """propagate to entities at the end of watched relations when a `main_rtype`
+    relation is added
+    """
     events = ('after_add_relation',)
 
     # to set in concrete class
-    main_rtype = None
     subject_relations = None
     object_relations = None
 
@@ -287,11 +295,12 @@
 
 
 class PropagateSubjectRelationDelHook(Hook):
-    """propagate on existing entities when a permission is deleted"""
+    """propagate to entities at the end of watched relations when a `main_rtype`
+    relation is deleted
+    """
     events = ('after_delete_relation',)
 
     # to set in concrete class
-    main_rtype = None
     subject_relations = None
     object_relations = None
 
--- a/server/repository.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/server/repository.py	Tue Feb 09 18:49:12 2010 +0100
@@ -934,7 +934,7 @@
         """full text index a modified entity"""
         alreadydone = session.transaction_data.setdefault('indexedeids', set())
         if entity.eid in alreadydone:
-            self.info('skipping reindexation of %s, already done', entity.eid)
+            self.debug('skipping reindexation of %s, already done', entity.eid)
             return
         alreadydone.add(entity.eid)
         self.system_source.fti_index_entity(session, entity)
--- a/server/session.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/server/session.py	Tue Feb 09 18:49:12 2010 +0100
@@ -501,7 +501,7 @@
                 except:
                     self.critical('error while %sing', trstate,
                                   exc_info=sys.exc_info())
-            self.debug('%s session %s done', trstate, self.id)
+            self.info('%s session %s done', trstate, self.id)
         finally:
             self._touch()
             self.commit_state = None
--- a/server/sources/native.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/server/sources/native.py	Tue Feb 09 18:49:12 2010 +0100
@@ -552,7 +552,7 @@
     def fti_index_entity(self, session, entity):
         """add text content of a created/modified entity to the full text index
         """
-        self.info('reindexing %r', entity.eid)
+        self.debug('reindexing %r', entity.eid)
         try:
             self.indexer.cursor_reindex_object(entity.eid, entity,
                                                session.pool['system'])
--- a/web/uicfg.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/web/uicfg.py	Tue Feb 09 18:49:12 2010 +0100
@@ -106,6 +106,8 @@
 
 from warnings import warn
 
+from logilab.common.compat import any
+
 from cubicweb import neg_role
 from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet,
                             RelationTagsDict, register_rtag, _ensure_str_key)
--- a/web/views/autoform.py	Tue Feb 09 08:16:29 2010 +0100
+++ b/web/views/autoform.py	Tue Feb 09 18:49:12 2010 +0100
@@ -32,11 +32,6 @@
 
 class InlinedFormField(ff.Field):
     def __init__(self, view=None, **kwargs):
-        if view.role == 'object':
-            fieldset = u'%s_object%s' % view.rtype
-        else:
-            fieldset = view.rtype
-        #kwargs.setdefault('fieldset', fieldset)
         kwargs.setdefault('label', None)
         super(InlinedFormField, self).__init__(name=view.rtype, role=view.role,
                                                eidparam=True, **kwargs)
@@ -730,7 +725,7 @@
             ttype = ttypes[0].type
             if self.should_inline_relation_form(rschema, ttype, role):
                 formviews = list(self.inline_edition_form_view(rschema, ttype, role))
-                card = rschema.rdef(entity.e_schema, ttype).role_cardinality(role)
+                card = rschema.role_rdef(entity.e_schema, ttype, role).role_cardinality(role)
                 # there is no related entity and we need at least one: we need to
                 # display one explicit inline-creation view
                 if self.should_display_inline_creation_form(rschema, formviews, card):