# HG changeset patch # User Aurelien Campeas # Date 1390565333 -3600 # Node ID 43aace16a9533f53db74a391f75dd4a7bbd9d5aa # Parent a0cf2993b6d3a8882e9220ceec281cfff3e34075 [schema] add composite handling helpers on EntitySchema (related to #3463112) These will help write predicates, hooks, etc. diff -r a0cf2993b6d3 -r 43aace16a953 predicates.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 diff -r a0cf2993b6d3 -r 43aace16a953 schema.py --- 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(): diff -r a0cf2993b6d3 -r 43aace16a953 test/unittest_schema.py --- 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()