fix and begin to document autofill algorithm
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 09 Feb 2010 11:22:40 +0100
changeset 4513 8abf464d2ffe
parent 4512 e7ac20bf3629
child 4517 0f3c10fc42b2
fix and begin to document autofill algorithm
devtools/fill.py
devtools/testlib.py
--- a/devtools/fill.py	Mon Feb 08 22:41:07 2010 +0100
+++ b/devtools/fill.py	Tue Feb 09 11:22:40 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	Mon Feb 08 22:41:07 2010 +0100
+++ b/devtools/testlib.py	Tue Feb 09 11:22:40 2010 +0100
@@ -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