# HG changeset patch # User Lea Capgen # Date 1409243354 -7200 # Node ID 19a683a0047c3bea61863199c34a90378227fa26 # Parent 60a9cd1b3a4bf002f4882081d7c393ae05122c2a [CWEP002] properly handle serialization of computed relations We now: * have CWComputedRelation in the bootstrap schema to store computed relations * properly serialize/deserialize it * test first if the database has been migrated and contains the related table Related to #3546717 [jcr: adjust unittest_querier to pass with the added entity type] diff -r 60a9cd1b3a4b -r 19a683a0047c misc/migration/3.20.0_Any.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/migration/3.20.0_Any.py Thu Aug 28 18:29:14 2014 +0200 @@ -0,0 +1,1 @@ +add_relation_type('CWComputedRType') diff -r 60a9cd1b3a4b -r 19a683a0047c schemas/bootstrap.py --- a/schemas/bootstrap.py Fri Jun 27 16:11:53 2014 +0200 +++ b/schemas/bootstrap.py Thu Aug 28 18:29:14 2014 +0200 @@ -57,6 +57,16 @@ final = Boolean(description=_('automatic')) +class CWComputedRType(EntityType): + """define a virtual relation type, used to build the instance schema""" + __permissions__ = PUB_SYSTEM_ENTITY_PERMS + name = String(required=True, indexed=True, internationalizable=True, + unique=True, maxsize=64) + description = RichString(internationalizable=True, + description=_('semantic description of this relation type')) + rule = String(required=True) + + class CWAttribute(EntityType): """define a final relation: link a final relation type from a non final entity to a final entity type. diff -r 60a9cd1b3a4b -r 19a683a0047c server/schemaserial.py --- a/server/schemaserial.py Fri Jun 27 16:11:53 2014 +0200 +++ b/server/schemaserial.py Thu Aug 28 18:29:14 2014 +0200 @@ -87,6 +87,20 @@ """ repo = cnx.repo dbhelper = repo.system_source.dbhelper + + # Computed Rtype + with cnx.ensure_cnx_set: + tables = set(dbhelper.list_tables(cnx.cnxset.cu)) + has_computed_relations = 'cw_CWComputedRType' in tables + if has_computed_relations: + rset = cnx.execute( + 'Any X, N, R, D WHERE X is CWComputedRType, X name N, ' + 'X rule R, X description D') + for eid, rule_name, rule, description in rset.rows: + rtype = ybo.ComputedRelation(name=rule_name, rule=rule, eid=eid, + description=description) + schema.add_relation_type(rtype) + # XXX bw compat (3.6 migration) with cnx.ensure_cnx_set: sqlcu = cnx.system_sql("SELECT * FROM cw_CWRType WHERE cw_name='symetric'") @@ -252,6 +266,7 @@ eschema._unique_together.append(tuple(sorted(unique_together))) schema.infer_specialization_rules() cnx.commit() + schema.finalize() schema.reading_from_database = False @@ -341,6 +356,9 @@ if pb is not None: pb.update() continue + if rschema.rule: + execschemarql(execute, rschema, crschema2rql(rschema)) + continue execschemarql(execute, rschema, rschema2rql(rschema, addrdef=False)) if rschema.symmetric: rdefs = [rdef for k, rdef in rschema.rdefs.iteritems() @@ -456,7 +474,7 @@ # rtype serialization def rschema2rql(rschema, cstrtypemap=None, addrdef=True, groupmap=None): - """return a list of rql insert statements to enter a relation schema + """generate rql insert statements to enter a relation schema in the database as an CWRType entity """ if rschema.type == 'has_text': @@ -483,10 +501,22 @@ relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] return relations, values +def crschema2rql(crschema): + relations, values = crschema_relations_values(crschema) + yield 'INSERT CWComputedRType X: %s' % ','.join(relations), values + +def crschema_relations_values(crschema): + values = _ervalues(crschema) + values['rule'] = crschema.rule + # XXX why oh why? + del values['final'] + relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)] + return relations, values + # rdef serialization def rdef2rql(rdef, cstrtypemap, groupmap=None): - # don't serialize infered relations + # don't serialize inferred relations if rdef.infered: return relations, values = _rdef_values(rdef) diff -r 60a9cd1b3a4b -r 19a683a0047c server/test/data-cwep002/schema.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/test/data-cwep002/schema.py Thu Aug 28 18:29:14 2014 +0200 @@ -0,0 +1,34 @@ +# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of CubicWeb. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . + +from yams.buildobjs import EntityType, RelationDefinition, Int, ComputedRelation + +class Person(EntityType): + salary = Int() + +class works_for(RelationDefinition): + subject = 'Person' + object = 'Company' + cardinality = '?*' + +class Company(EntityType): + total_salary = Int() + +class has_employee(ComputedRelation): + rule = 'O works_for S' + diff -r 60a9cd1b3a4b -r 19a683a0047c server/test/unittest_querier.py --- a/server/test/unittest_querier.py Fri Jun 27 16:11:53 2014 +0200 +++ b/server/test/unittest_querier.py Thu Aug 28 18:29:14 2014 +0200 @@ -173,11 +173,11 @@ 'ET': 'CWEType', 'ETN': 'String'}]) rql, solutions = partrqls[1] self.assertRQLEqual(rql, 'Any ETN,X WHERE X is ET, ET name ETN, ET is CWEType, ' - 'X is IN(BaseTransition, Bookmark, CWAttribute, CWCache, CWConstraint, ' - ' CWConstraintType, CWEType, CWGroup, CWPermission, CWProperty, CWRType, ' - ' CWRelation, CWSource, CWUniqueTogetherConstraint, CWUser, Card, Comment, ' - ' Division, Email, EmailPart, EmailThread, ExternalUri, File, Folder, Note, ' - ' Old, Personne, RQLExpression, Societe, State, SubDivision, ' + 'X is IN(BaseTransition, Bookmark, CWAttribute, CWCache, CWComputedRType, ' + ' CWConstraint, CWConstraintType, CWEType, CWGroup, CWPermission, CWProperty, ' + ' CWRType, CWRelation, CWSource, CWUniqueTogetherConstraint, CWUser, Card, ' + ' Comment, Division, Email, EmailPart, EmailThread, ExternalUri, File, ' + ' Folder, Note, Old, Personne, RQLExpression, Societe, State, SubDivision, ' ' SubWorkflowExitPoint, Tag, TrInfo, Transition, Workflow, WorkflowTransition)') self.assertListEqual(sorted(solutions), sorted([{'X': 'BaseTransition', 'ETN': 'String', 'ET': 'CWEType'}, @@ -186,6 +186,7 @@ {'X': 'Comment', 'ETN': 'String', 'ET': 'CWEType'}, {'X': 'Division', 'ETN': 'String', 'ET': 'CWEType'}, {'X': 'CWCache', 'ETN': 'String', 'ET': 'CWEType'}, + {'X': 'CWComputedRType', 'ETN': 'String', 'ET': 'CWEType'}, {'X': 'CWConstraint', 'ETN': 'String', 'ET': 'CWEType'}, {'X': 'CWConstraintType', 'ETN': 'String', 'ET': 'CWEType'}, {'X': 'CWEType', 'ETN': 'String', 'ET': 'CWEType'}, @@ -602,18 +603,18 @@ 'WHERE RT name N, RDEF relation_type RT ' 'HAVING COUNT(RDEF) > 10') self.assertListEqual(rset.rows, - [[u'description_format', 12], - [u'description', 13], - [u'name', 17], - [u'created_by', 43], - [u'creation_date', 43], - [u'cw_source', 43], - [u'cwuri', 43], - [u'in_basket', 43], - [u'is', 43], - [u'is_instance_of', 43], - [u'modification_date', 43], - [u'owned_by', 43]]) + [[u'description_format', 13], + [u'description', 14], + [u'name', 18], + [u'created_by', 44], + [u'creation_date', 44], + [u'cw_source', 44], + [u'cwuri', 44], + [u'in_basket', 44], + [u'is', 44], + [u'is_instance_of', 44], + [u'modification_date', 44], + [u'owned_by', 44]]) def test_select_aggregat_having_dumb(self): # dumb but should not raise an error diff -r 60a9cd1b3a4b -r 19a683a0047c server/test/unittest_schemaserial.py --- a/server/test/unittest_schemaserial.py Fri Jun 27 16:11:53 2014 +0200 +++ b/server/test/unittest_schemaserial.py Thu Aug 28 18:29:14 2014 +0200 @@ -25,6 +25,7 @@ from cubicweb import Binary from cubicweb.schema import CubicWebSchemaLoader from cubicweb.devtools import TestServerConfiguration +from cubicweb.devtools.testlib import CubicWebTC from cubicweb.server.schemaserial import (updateeschema2rql, updaterschema2rql, rschema2rql, eschema2rql, rdef2rql, specialize2rql, @@ -425,7 +426,17 @@ # self.assertListEqual(perms2rql(schema, self.GROUP_MAPPING), # ['INSERT CWEType X: X name 'Societe', X final FALSE']) +class ComputedAttributeAndRelationTC(CubicWebTC): + appid = 'data-cwep002' + def test(self): + # force to read schema from the database + self.repo.set_schema(self.repo.deserialize_schema(), resetvreg=False) + schema = self.repo.schema + self.assertEqual([('Company', 'Person')], list(schema['has_employee'].rdefs)) + self.assertEqual('O works_for S', + schema['has_employee'].rule) + self.assertEqual([('Company', 'Int')], list(schema['total_salary'].rdefs)) if __name__ == '__main__': unittest_main() diff -r 60a9cd1b3a4b -r 19a683a0047c test/unittest_schema.py --- a/test/unittest_schema.py Fri Jun 27 16:11:53 2014 +0200 +++ b/test/unittest_schema.py Thu Aug 28 18:29:14 2014 +0200 @@ -163,9 +163,10 @@ entities = sorted([str(e) for e in schema.entities()]) expected_entities = ['Ami', 'BaseTransition', 'BigInt', 'Bookmark', 'Boolean', 'Bytes', 'Card', 'Date', 'Datetime', 'Decimal', - 'CWCache', 'CWConstraint', 'CWConstraintType', 'CWDataImport', - 'CWEType', 'CWAttribute', 'CWGroup', 'EmailAddress', 'CWRelation', - 'CWPermission', 'CWProperty', 'CWRType', + 'CWCache', 'CWComputedRType', 'CWConstraint', + 'CWConstraintType', 'CWDataImport', 'CWEType', + 'CWAttribute', 'CWGroup', 'EmailAddress', + 'CWRelation', 'CWPermission', 'CWProperty', 'CWRType', 'CWSource', 'CWSourceHostConfig', 'CWSourceSchemaConfig', 'CWUniqueTogetherConstraint', 'CWUser', 'ExternalUri', 'File', 'Float', 'Int', 'Interval', 'Note', @@ -209,7 +210,7 @@ 'parser', 'path', 'pkey', 'prefered_form', 'prenom', 'primary_email', - 'read_permission', 'relation_type', 'relations', 'require_group', + 'read_permission', 'relation_type', 'relations', 'require_group', 'rule', 'specializes', 'start_timestamp', 'state_of', 'status', 'subworkflow', 'subworkflow_exit', 'subworkflow_state', 'surname', 'symmetric', 'synopsis', @@ -452,6 +453,7 @@ ('cw_source', 'Bookmark', 'CWSource', 'object'), ('cw_source', 'CWAttribute', 'CWSource', 'object'), ('cw_source', 'CWCache', 'CWSource', 'object'), + ('cw_source', 'CWComputedRType', 'CWSource', 'object'), ('cw_source', 'CWConstraint', 'CWSource', 'object'), ('cw_source', 'CWConstraintType', 'CWSource', 'object'), ('cw_source', 'CWDataImport', 'CWSource', 'object'),