[schema] add composite handling helpers on EntitySchema (related to #3463112)
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Fri, 24 Jan 2014 13:08:53 +0100
changeset 9547 43aace16a953
parent 9546 a0cf2993b6d3
child 9548 be001628edad
[schema] add composite handling helpers on EntitySchema (related to #3463112) These will help write predicates, hooks, etc.
predicates.py
schema.py
test/unittest_schema.py
--- a/predicates.py	Mon Feb 17 15:01:23 2014 +0100
+++ b/predicates.py	Fri Jan 24 13:08:53 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -707,6 +707,22 @@
 
 # entity predicates #############################################################
 
+class composite_etype(Predicate):
+    """Return 1 for composite entities.
+
+    A composite entity has an etype for which at least one relation
+    definition points in its direction with the
+    composite='subject'/'object' notation.
+    """
+
+    def __call__(self, cls, req, **kwargs):
+        entity = kwargs.pop('entity', None)
+        if entity is None:
+            return 0
+        return entity.e_schema.is_composite
+
+
+
 class non_final_entity(EClassPredicate):
     """Return 1 for entity of a non final entity type(s). Remember, "final"
     entity types are String, Int, etc... This is equivalent to
--- a/schema.py	Mon Feb 17 15:01:23 2014 +0100
+++ b/schema.py	Fri Jan 24 13:08:53 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -669,6 +669,32 @@
             eid = getattr(edef, 'eid', None)
         self.eid = eid
 
+    def targets(self, role):
+        assert role in ('subject', 'object')
+        if role == 'subject':
+            return self.subjrels.values()
+        return self.objrels.values()
+
+    @cachedproperty
+    def composite_rdef_roles(self):
+        """Return all relation definitions that define the current entity
+        type as a composite.
+        """
+        rdef_roles = []
+        for role in ('subject', 'object'):
+            for rschema in self.targets(role):
+                if rschema.final:
+                    continue
+                for rdef in rschema.rdefs.values():
+                    crole = rdef.composite
+                    if crole == role:
+                        rdef_roles.append((rdef, role))
+        return rdef_roles
+
+    @cachedproperty
+    def is_composite(self):
+        return bool(len(self.composite_rdef_roles))
+
     def check_permission_definitions(self):
         super(CubicWebEntitySchema, self).check_permission_definitions()
         for groups in self.permissions.itervalues():
--- a/test/unittest_schema.py	Mon Feb 17 15:01:23 2014 +0100
+++ b/test/unittest_schema.py	Fri Jan 24 13:08:53 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -355,5 +355,129 @@
         tr = self.request().user.cw_adapt_to('IWorkflowable').fire_transition('deactivate')
         self.assertEqual(tr.comment_format, 'text/plain')
 
+
+class CompositeSchemaTC(CubicWebTC):
+    composites = {
+        'BaseTransition': [('condition', 'BaseTransition', 'RQLExpression', 'subject'),
+                           ('condition', 'Transition', 'RQLExpression', 'subject'),
+                           ('condition', 'WorkflowTransition', 'RQLExpression', 'subject')],
+        'CWAttribute': [('add_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                        ('add_permission', 'CWEType', 'RQLExpression', 'subject'),
+                        ('add_permission', 'CWRelation', 'RQLExpression', 'subject'),
+                        ('constrained_by', 'CWAttribute', 'CWConstraint', 'subject'),
+                        ('constrained_by', 'CWRelation', 'CWConstraint', 'subject'),
+                        ('read_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                        ('read_permission', 'CWEType', 'RQLExpression', 'subject'),
+                        ('read_permission', 'CWRelation', 'RQLExpression', 'subject'),
+                        ('update_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                        ('update_permission', 'CWEType', 'RQLExpression', 'subject')],
+        'CWEType': [('add_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                    ('add_permission', 'CWEType', 'RQLExpression', 'subject'),
+                    ('add_permission', 'CWRelation', 'RQLExpression', 'subject'),
+                    ('constraint_of', 'CWUniqueTogetherConstraint', 'CWEType', 'object'),
+                    ('cw_schema', 'CWSourceSchemaConfig', 'CWEType', 'object'),
+                    ('cw_schema', 'CWSourceSchemaConfig', 'CWRType', 'object'),
+                    ('cw_schema', 'CWSourceSchemaConfig', 'CWRelation', 'object'),
+                    ('delete_permission', 'CWEType', 'RQLExpression', 'subject'),
+                    ('delete_permission', 'CWRelation', 'RQLExpression', 'subject'),
+                    ('from_entity', 'CWAttribute', 'CWEType', 'object'),
+                    ('from_entity', 'CWRelation', 'CWEType', 'object'),
+                    ('read_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                    ('read_permission', 'CWEType', 'RQLExpression', 'subject'),
+                    ('read_permission', 'CWRelation', 'RQLExpression', 'subject'),
+                    ('to_entity', 'CWAttribute', 'CWEType', 'object'),
+                    ('to_entity', 'CWRelation', 'CWEType', 'object'),
+                    ('update_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                    ('update_permission', 'CWEType', 'RQLExpression', 'subject')],
+        'CWRType': [('cw_schema', 'CWSourceSchemaConfig', 'CWEType', 'object'),
+                    ('cw_schema', 'CWSourceSchemaConfig', 'CWRType', 'object'),
+                    ('cw_schema', 'CWSourceSchemaConfig', 'CWRelation', 'object'),
+                    ('relation_type', 'CWAttribute', 'CWRType', 'object'),
+                    ('relation_type', 'CWRelation', 'CWRType', 'object')],
+        'CWRelation': [('add_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                       ('add_permission', 'CWEType', 'RQLExpression', 'subject'),
+                       ('add_permission', 'CWRelation', 'RQLExpression', 'subject'),
+                       ('constrained_by', 'CWAttribute', 'CWConstraint', 'subject'),
+                       ('constrained_by', 'CWRelation', 'CWConstraint', 'subject'),
+                       ('cw_schema', 'CWSourceSchemaConfig', 'CWEType', 'object'),
+                       ('cw_schema', 'CWSourceSchemaConfig', 'CWRType', 'object'),
+                       ('cw_schema', 'CWSourceSchemaConfig', 'CWRelation', 'object'),
+                       ('delete_permission', 'CWEType', 'RQLExpression', 'subject'),
+                       ('delete_permission', 'CWRelation', 'RQLExpression', 'subject'),
+                       ('read_permission', 'CWAttribute', 'RQLExpression', 'subject'),
+                       ('read_permission', 'CWEType', 'RQLExpression', 'subject'),
+                       ('read_permission', 'CWRelation', 'RQLExpression', 'subject')],
+        'CWSource': [('cw_for_source', 'CWSourceSchemaConfig', 'CWSource', 'object'),
+                     ('cw_host_config_of', 'CWSourceHostConfig', 'CWSource', 'object'),
+                     ('cw_import_of', 'CWDataImport', 'CWSource', 'object'),
+                     ('cw_source', 'Ami', 'CWSource', 'object'),
+                     ('cw_source', 'BaseTransition', 'CWSource', 'object'),
+                     ('cw_source', 'Bookmark', 'CWSource', 'object'),
+                     ('cw_source', 'CWAttribute', 'CWSource', 'object'),
+                     ('cw_source', 'CWCache', 'CWSource', 'object'),
+                     ('cw_source', 'CWConstraint', 'CWSource', 'object'),
+                     ('cw_source', 'CWConstraintType', 'CWSource', 'object'),
+                     ('cw_source', 'CWDataImport', 'CWSource', 'object'),
+                     ('cw_source', 'CWEType', 'CWSource', 'object'),
+                     ('cw_source', 'CWGroup', 'CWSource', 'object'),
+                     ('cw_source', 'CWPermission', 'CWSource', 'object'),
+                     ('cw_source', 'CWProperty', 'CWSource', 'object'),
+                     ('cw_source', 'CWRType', 'CWSource', 'object'),
+                     ('cw_source', 'CWRelation', 'CWSource', 'object'),
+                     ('cw_source', 'CWSource', 'CWSource', 'object'),
+                     ('cw_source', 'CWSourceHostConfig', 'CWSource', 'object'),
+                     ('cw_source', 'CWSourceSchemaConfig', 'CWSource', 'object'),
+                     ('cw_source', 'CWUniqueTogetherConstraint', 'CWSource', 'object'),
+                     ('cw_source', 'CWUser', 'CWSource', 'object'),
+                     ('cw_source', 'Card', 'CWSource', 'object'),
+                     ('cw_source', 'EmailAddress', 'CWSource', 'object'),
+                     ('cw_source', 'ExternalUri', 'CWSource', 'object'),
+                     ('cw_source', 'File', 'CWSource', 'object'),
+                     ('cw_source', 'Note', 'CWSource', 'object'),
+                     ('cw_source', 'Personne', 'CWSource', 'object'),
+                     ('cw_source', 'Produit', 'CWSource', 'object'),
+                     ('cw_source', 'RQLExpression', 'CWSource', 'object'),
+                     ('cw_source', 'Service', 'CWSource', 'object'),
+                     ('cw_source', 'Societe', 'CWSource', 'object'),
+                     ('cw_source', 'State', 'CWSource', 'object'),
+                     ('cw_source', 'StateFull', 'CWSource', 'object'),
+                     ('cw_source', 'SubNote', 'CWSource', 'object'),
+                     ('cw_source', 'SubWorkflowExitPoint', 'CWSource', 'object'),
+                     ('cw_source', 'Tag', 'CWSource', 'object'),
+                     ('cw_source', 'TrInfo', 'CWSource', 'object'),
+                     ('cw_source', 'Transition', 'CWSource', 'object'),
+                     ('cw_source', 'Usine', 'CWSource', 'object'),
+                     ('cw_source', 'Workflow', 'CWSource', 'object'),
+                     ('cw_source', 'WorkflowTransition', 'CWSource', 'object')],
+        'CWUser': [('for_user', 'CWProperty', 'CWUser', 'object'),
+                   ('use_email', 'CWUser', 'EmailAddress', 'subject'),
+                   ('wf_info_for', 'TrInfo', 'CWUser', 'object'),
+                   ('wf_info_for', 'TrInfo', 'StateFull', 'object')],
+        'StateFull': [('wf_info_for', 'TrInfo', 'CWUser', 'object'),
+                      ('wf_info_for', 'TrInfo', 'StateFull', 'object')],
+        'Transition': [('condition', 'BaseTransition', 'RQLExpression', 'subject'),
+                       ('condition', 'Transition', 'RQLExpression', 'subject'),
+                       ('condition', 'WorkflowTransition', 'RQLExpression', 'subject')],
+        'Workflow': [('state_of', 'State', 'Workflow', 'object'),
+                     ('transition_of', 'BaseTransition', 'Workflow', 'object'),
+                     ('transition_of', 'Transition', 'Workflow', 'object'),
+                     ('transition_of', 'WorkflowTransition', 'Workflow', 'object')],
+        'WorkflowTransition': [('condition', 'BaseTransition', 'RQLExpression', 'subject'),
+                               ('condition', 'Transition', 'RQLExpression', 'subject'),
+                               ('condition', 'WorkflowTransition', 'RQLExpression', 'subject'),
+                               ('subworkflow_exit', 'WorkflowTransition', 'SubWorkflowExitPoint', 'subject')]
+    }
+
+    def test_composite_entities(self):
+        schema = self.vreg.schema
+        self.assertEqual(sorted(self.composites.keys()),
+                         [eschema.type
+                          for eschema in sorted(schema.entities())
+                          if eschema.is_composite])
+        for etype in self.composites:
+            self.assertEqual(self.composites[etype],
+                             sorted([(r.rtype.type, r.subject.type, r.object.type, role)
+                                     for r, role in sorted(schema[etype].composite_rdef_roles)]))
+
 if __name__ == '__main__':
     unittest_main()