no progress div in reledit, else we get duplicated ids
"""functions for schema / permissions (de)serialization using RQL:organization: Logilab:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr"""__docformat__="restructuredtext en"fromitertoolsimportchainfromlogilab.common.shellutilsimportProgressBarfromyamsimportschemaasschemamod,buildobjsasybofromcubicweb.schemaimportCONSTRAINTS,ETYPE_NAME_MAPdefgroup_mapping(cursor,interactive=True):"""create a group mapping from an rql cursor A group mapping has standard group names as key (managers, owners at least) and the actual EGroup entity's eid as associated value. In interactive mode (the default), missing groups'eid will be prompted from the user. """res={}foreid,nameincursor.execute('Any G, N WHERE G is EGroup, G name N'):res[name]=eidifnotinteractive:returnresmissing=[gforgin('owners','managers','users','guests')ifnotginres]ifmissing:print'some native groups are missing but the following groups have been found:'print'\n'.join('* %s (%s)'%(n,eid)forn,eidinres.items())printprint'enter the eid of a to group to map to each missing native group'print'or just type enter to skip permissions granted to a group'forgroupinmissing:whileTrue:value=raw_input('eid for group %s: '%group).strip()ifnotvalue:continuetry:res[group]=int(value)exceptValueError:print'eid should be an integer'continuereturnres# schema / perms deserialization ##############################################defdeserialize_schema(schema,session):"""return a schema according to information stored in an rql database as ERType and EEType entities """# print 'reading schema from the database...'index={}permsdict=deserialize_ertype_permissions(session)schema.reading_from_database=Trueforeid,etype,desc,metainsession.execute('Any X, N, D, M WHERE ''X is EEType, X name N, ''X description D, X meta M',build_descr=False):# base types are already in the schema, skip themifetypeinschemamod.BASE_TYPES:# just set the eideschema=schema.eschema(etype)eschema.eid=eidindex[eid]=eschemacontinueifetypeinETYPE_NAME_MAP:# XXX <2.45 bw compatprint'fixing etype name from %s to %s'%(etype,ETYPE_NAME_MAP[etype])# can't use write rql queries at this point, use raw sqlsession.system_sql('UPDATE EEType SET name=%(n)s WHERE eid=%(x)s',{'x':eid,'n':ETYPE_NAME_MAP[etype]})session.system_sql('UPDATE entities SET type=%(n)s WHERE type=%(x)s',{'x':etype,'n':ETYPE_NAME_MAP[etype]})session.commit(False)try:session.system_sql('UPDATE deleted_entities SET type=%(n)s WHERE type=%(x)s',{'x':etype,'n':ETYPE_NAME_MAP[etype]})except:passtocleanup=[eid]tocleanup+=(eidforeid,(eidetype,uri,extid)insession.repo._type_source_cache.items()ifetype==eidetype)session.repo.clear_caches(tocleanup)session.commit(False)etype=ETYPE_NAME_MAP[etype]etype=ybo.EntityType(name=etype,description=desc,meta=meta,eid=eid)eschema=schema.add_entity_type(etype)index[eid]=eschemaset_perms(eschema,permsdict.get(eid,{}))try:rset=session.execute('Any XN, ETN WHERE X is EEType, X name XN, ''X specializes ET, ET name ETN')except:# `specializes` relation not available for versions prior to 2.50session.rollback(False)else:foretype,stypeinrset:eschema=schema.eschema(etype)seschema=schema.eschema(stype)eschema._specialized_type=stypeseschema._specialized_by.append(etype)foreid,rtype,desc,meta,sym,ilinsession.execute('Any X,N,D,M,S,I WHERE X is ERType, X name N, X description D, ''X meta M, X symetric S, X inlined I',build_descr=False):try:# bw compat: fulltext_container added in 2.47ft_container=session.execute('Any FTC WHERE X eid %(x)s, X fulltext_container FTC',{'x':eid}).rows[0][0]except:ft_container=Nonesession.rollback(False)rtype=ybo.RelationType(name=rtype,description=desc,meta=bool(meta),symetric=bool(sym),inlined=bool(il),fulltext_container=ft_container,eid=eid)rschema=schema.add_relation_type(rtype)index[eid]=rschemaset_perms(rschema,permsdict.get(eid,{}))cstrsdict=deserialize_rdef_constraints(session)forvaluesinsession.execute('Any X,SE,RT,OE,CARD,ORD,DESC,IDX,FTIDX,I18N,DFLT WHERE X is EFRDef,''X relation_type RT, X cardinality CARD, X ordernum ORD, X indexed IDX,''X description DESC, X internationalizable I18N, X defaultval DFLT,''X fulltextindexed FTIDX, X from_entity SE, X to_entity OE',build_descr=False):rdefeid,seid,reid,teid,card,ord,desc,idx,ftidx,i18n,default=valuesconstraints=cstrsdict.get(rdefeid,())frometype=index[seid].typertype=index[reid].typetoetype=index[teid].typerdef=ybo.RelationDefinition(frometype,rtype,toetype,cardinality=card,order=ord,description=desc,constraints=constraints,indexed=idx,fulltextindexed=ftidx,internationalizable=i18n,default=default,eid=rdefeid)schema.add_relation_def(rdef)forvaluesinsession.execute('Any X,SE,RT,OE,CARD,ORD,DESC,C WHERE X is ENFRDef, X relation_type RT,''X cardinality CARD, X ordernum ORD, X description DESC, ''X from_entity SE, X to_entity OE, X composite C',build_descr=False):rdefeid,seid,reid,teid,card,ord,desc,c=valuesfrometype=index[seid].typertype=index[reid].typetoetype=index[teid].typeconstraints=cstrsdict.get(rdefeid,())rdef=ybo.RelationDefinition(frometype,rtype,toetype,cardinality=card,order=ord,description=desc,composite=c,constraints=constraints,eid=rdefeid)schema.add_relation_def(rdef)schema.infer_specialization_rules()session.commit()schema.reading_from_database=Falsedefdeserialize_ertype_permissions(session):"""return sect action:groups associations for the given entity or relation schema with its eid, according to schema's permissions stored in the database as [read|add|delete|update]_permission relations between EEType/ERType and EGroup entities """res={}foractionin('read','add','update','delete'):rql='Any E,N WHERE G is EGroup, G name N, E %s_permission G'%actionforeid,gnameinsession.execute(rql,build_descr=False):res.setdefault(eid,{}).setdefault(action,[]).append(gname)rql=('Any E,X,EXPR,V WHERE X is RQLExpression, X expression EXPR, ''E %s_permission X, X mainvars V'%action)foreid,expreid,expr,mainvarsinsession.execute(rql,build_descr=False):# we don't know yet if it's a rql expr for an entity or a relation,# so append a tuple to differentiate from groups and so we'll be# able to instantiate it laterres.setdefault(eid,{}).setdefault(action,[]).append((expr,mainvars,expreid))returnresdefset_perms(erschema,permsdict):"""set permissions on the given erschema according to the permission definition dictionary as built by deserialize_ertype_permissions for a given erschema's eid """foractioninerschema.ACTIONS:actperms=[]forsomethinginpermsdict.get(action,()):ifisinstance(something,tuple):actperms.append(erschema.rql_expression(*something))else:# group nameactperms.append(something)erschema.set_permissions(action,actperms)defdeserialize_rdef_constraints(session):"""return the list of relation definition's constraints as instances"""res={}forrdefeid,ceid,ct,valinsession.execute('Any E, X,TN,V WHERE E constrained_by X, X is EConstraint, ''X cstrtype T, T name TN, X value V',build_descr=False):cstr=CONSTRAINTS[ct].deserialize(val)cstr.eid=ceidres.setdefault(rdefeid,[]).append(cstr)returnres# schema / perms serialization ################################################defserialize_schema(cursor,schema,verbose=False):"""synchronize schema and permissions in the database according to current schema """print'serializing the schema, this may take some time'eschemas=schema.entities()aller=eschemas+schema.relations()ifnotverbose:pb_size=len(aller)+len(CONSTRAINTS)+len([xforxineschemasifx.specializes()])pb=ProgressBar(pb_size)forcstrtypeinCONSTRAINTS:rql='INSERT EConstraintType X: X name "%s"'%cstrtypeifverbose:printrqlcursor.execute(rql)ifnotverbose:pb.update()groupmap=group_mapping(cursor,interactive=False)forertypeinaller:# skip eid and has_text relationsifertypein('eid','identity','has_text',):pb.update()continueforrql,kwargsinerschema2rql(schema[ertype]):ifverbose:printrql%kwargscursor.execute(rql,kwargs)forrql,kwargsinerperms2rql(schema[ertype],groupmap):ifverbose:printrqlcursor.execute(rql,kwargs)ifnotverbose:pb.update()forrql,kwargsinspecialize2rql(schema):ifverbose:printrql%kwargscursor.execute(rql,kwargs)ifnotverbose:pb.update()printdef_ervalues(erschema):try:type_=unicode(erschema.type)exceptUnicodeDecodeError,e:raiseException("can't decode %s [was %s]"%(erschema.type,e))try:desc=unicode(erschema.description)oru''exceptUnicodeDecodeError,e:raiseException("can't decode %s [was %s]"%(erschema.description,e))return{'name':type_,'meta':erschema.meta,'final':erschema.is_final(),'description':desc,}defeschema_relations_values(eschema):values=_ervalues(eschema)relations=['X %s%%(%s)s'%(attr,attr)forattrinsorted(values)]returnrelations,values# XXX 2.47 migrationHAS_FULLTEXT_CONTAINER=Truedefrschema_relations_values(rschema):values=_ervalues(rschema)values['final']=rschema.is_final()values['symetric']=rschema.symetricvalues['inlined']=rschema.inlinedifHAS_FULLTEXT_CONTAINER:ifisinstance(rschema.fulltext_container,str):values['fulltext_container']=unicode(rschema.fulltext_container)else:values['fulltext_container']=rschema.fulltext_containerrelations=['X %s%%(%s)s'%(attr,attr)forattrinsorted(values)]returnrelations,valuesdef_rdef_values(rschema,objtype,props):amap={'order':'ordernum'}values={}forprop,defaultinrschema.rproperty_defs(objtype).iteritems():ifpropin('eid','constraints','uid','infered'):continuevalue=props.get(prop,default)ifpropin('indexed','fulltextindexed','internationalizable'):value=bool(value)elifprop=='ordernum':value=int(value)elifisinstance(value,str):value=unicode(value)values[amap.get(prop,prop)]=valuereturnvaluesdefnfrdef_relations_values(rschema,objtype,props):values=_rdef_values(rschema,objtype,props)relations=['X %s%%(%s)s'%(attr,attr)forattrinsorted(values)]returnrelations,valuesdeffrdef_relations_values(rschema,objtype,props):values=_rdef_values(rschema,objtype,props)default=values['default']delvalues['default']ifdefaultisnotNone:ifdefaultisFalse:default=u''elifnotisinstance(default,unicode):default=unicode(default)values['defaultval']=defaultrelations=['X %s%%(%s)s'%(attr,attr)forattrinsorted(values)]returnrelations,valuesdef__rdef2rql(genmap,rschema,subjtype=None,objtype=None,props=None):ifsubjtypeisNone:assertobjtypeisNoneassertpropsisNonetargets=rschema.iter_rdefs()else:assertnotobjtypeisNonetargets=[(subjtype,objtype)]forsubjtype,objtypeintargets:ifpropsisNone:_props=rschema.rproperties(subjtype,objtype)else:_props=props# don't serialize infered relationsif_props.get('infered'):continuegen=genmap[rschema.is_final()]forrql,valuesingen(rschema,subjtype,objtype,_props):yieldrql,valuesdefschema2rql(schema,skip=None,allow=None):"""return a list of rql insert statements to enter the schema in the database as ERType and EEType entities """assertnot(skipisnotNoneandallowisnotNone), \'can\'t use both skip and allow'all=schema.entities()+schema.relations()ifskipisnotNone:returnchain(*[erschema2rql(schema[t])fortinallifnottinskip])elifallowisnotNone:returnchain(*[erschema2rql(schema[t])fortinalliftinallow])returnchain(*[erschema2rql(schema[t])fortinall])deferschema2rql(erschema):ifisinstance(erschema,schemamod.EntitySchema):returneschema2rql(erschema)returnrschema2rql(erschema)defeschema2rql(eschema):"""return a list of rql insert statements to enter an entity schema in the database as an EEType entity """relations,values=eschema_relations_values(eschema)# NOTE: 'specializes' relation can't be inserted here since there's no# way to make sure the parent type is inserted before the child typeyield'INSERT EEType X: %s'%','.join(relations),valuesdefspecialize2rql(schema):foreschemainschema.entities():forrql,kwargsineschemaspecialize2rql(eschema):yieldrql,kwargsdefeschemaspecialize2rql(eschema):specialized_type=eschema.specializes()ifspecialized_type:values={'x':eschema.type,'et':specialized_type.type}yield'SET X specializes ET WHERE X name %(x)s, ET name %(et)s',valuesdefrschema2rql(rschema,addrdef=True):"""return a list of rql insert statements to enter a relation schema in the database as an ERType entity """ifrschema.type=='has_text':returnrelations,values=rschema_relations_values(rschema)yield'INSERT ERType X: %s'%','.join(relations),valuesifaddrdef:forrql,valuesinrdef2rql(rschema):yieldrql,valuesdefrdef2rql(rschema,subjtype=None,objtype=None,props=None):genmap={True:frdef2rql,False:nfrdef2rql}return__rdef2rql(genmap,rschema,subjtype,objtype,props)_LOCATE_RDEF_RQL0='X relation_type ER,X from_entity SE,X to_entity OE'_LOCATE_RDEF_RQL1='SE name %(se)s,ER name %(rt)s,OE name %(oe)s'deffrdef2rql(rschema,subjtype,objtype,props):relations,values=frdef_relations_values(rschema,objtype,props)relations.append(_LOCATE_RDEF_RQL0)values.update({'se':str(subjtype),'rt':str(rschema),'oe':str(objtype)})yield'INSERT EFRDef X: %s WHERE %s'%(','.join(relations),_LOCATE_RDEF_RQL1),valuesforrql,valuesinrdefrelations2rql(rschema,subjtype,objtype,props):yieldrql+', EDEF is EFRDef',valuesdefnfrdef2rql(rschema,subjtype,objtype,props):relations,values=nfrdef_relations_values(rschema,objtype,props)relations.append(_LOCATE_RDEF_RQL0)values.update({'se':str(subjtype),'rt':str(rschema),'oe':str(objtype)})yield'INSERT ENFRDef X: %s WHERE %s'%(','.join(relations),_LOCATE_RDEF_RQL1),valuesforrql,valuesinrdefrelations2rql(rschema,subjtype,objtype,props):yieldrql+', EDEF is ENFRDef',valuesdefrdefrelations2rql(rschema,subjtype,objtype,props):iterators=[]forconstraintinprops['constraints']:iterators.append(constraint2rql(rschema,subjtype,objtype,constraint))returnchain(*iterators)defconstraint2rql(rschema,subjtype,objtype,constraint):values={'ctname':unicode(constraint.type()),'value':unicode(constraint.serialize()),'rt':str(rschema),'se':str(subjtype),'oe':str(objtype)}yield'INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE \CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, \ER name %(rt)s, SE name %(se)s, OE name %(oe)s',valuesdefperms2rql(schema,groupmapping):"""return rql insert statements to enter the schema's permissions in the database as [read|add|delete|update]_permission relations between EEType/ERType and EGroup entities groupmapping is a dictionnary mapping standard group names to eids """foretypeinsorted(schema.entities()):yielderperms2rql(schema[etype],groupmapping)forrtypeinsorted(schema.relations()):yielderperms2rql(schema[rtype],groupmapping)deferperms2rql(erschema,groupmapping):"""return rql insert statements to enter the entity or relation schema's permissions in the database as [read|add|delete|update]_permission relations between EEType/ERType and EGroup entities """etype=isinstance(erschema,schemamod.EntitySchema)and'EEType'or'ERType'foractioninerschema.ACTIONS:forgroupinsorted(erschema.get_groups(action)):try:yield('SET X %s_permission Y WHERE X is %s, X name "%s", Y eid %s'%(action,etype,erschema,groupmapping[group]),None)exceptKeyError:continueforrqlexprinsorted(erschema.get_rqlexprs(action)):yield('INSERT RQLExpression E: E expression %%(e)s, E exprtype %%(t)s, ''E mainvars %%(v)s, X %s_permission E ''WHERE X is %s, X name "%s"'%(action,etype,erschema),{'e':unicode(rqlexpr.expression),'v':unicode(rqlexpr.mainvars),'t':unicode(rqlexpr.__class__.__name__)})defupdateeschema2rql(eschema):relations,values=eschema_relations_values(eschema)values['et']=eschema.typeyield'SET %s WHERE X is EEType, X name %%(et)s'%','.join(relations),valuesdefupdaterschema2rql(rschema):relations,values=rschema_relations_values(rschema)values['rt']=rschema.typeyield'SET %s WHERE X is ERType, X name %%(rt)s'%','.join(relations),valuesdefupdaterdef2rql(rschema,subjtype=None,objtype=None,props=None):genmap={True:updatefrdef2rql,False:updatenfrdef2rql}return__rdef2rql(genmap,rschema,subjtype,objtype,props)defupdatefrdef2rql(rschema,subjtype,objtype,props):relations,values=frdef_relations_values(rschema,objtype,props)values.update({'se':subjtype,'rt':str(rschema),'oe':objtype})yield'SET %s WHERE %s, %s, X is EFRDef'%(','.join(relations),_LOCATE_RDEF_RQL0,_LOCATE_RDEF_RQL1),valuesdefupdatenfrdef2rql(rschema,subjtype,objtype,props):relations,values=nfrdef_relations_values(rschema,objtype,props)values.update({'se':subjtype,'rt':str(rschema),'oe':objtype})yield'SET %s WHERE %s, %s, X is ENFRDef'%(','.join(relations),_LOCATE_RDEF_RQL0,_LOCATE_RDEF_RQL1),values