Added tag 3.21.1, debian/3.21.1-1, centos/3.21.1-1 for changeset a8a0de0298a5
# copyright 2003-2015 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 <http://www.gnu.org/licenses/>."""Stores are responsible to insert properly formatted entities and relations into the database. Theyhave the following API:: >>> user_eid = store.prepare_insert_entity('CWUser', login=u'johndoe') >>> group_eid = store.prepare_insert_entity('CWUser', name=u'unknown') >>> store.relate(user_eid, 'in_group', group_eid) >>> store.flush() >>> store.commit() >>> store.finish()Some store **requires a flush** to copy data in the database, so if you want to have storeindependant code you should explicitly call it. (There may be multiple flushes during theprocess, or only one at the end if there is no memory issue). This is different from thecommit which validates the database transaction. At last, the `finish()` method should be called incase the store requires additional work once everything is done.* ``prepare_insert_entity(<entity type>, **kwargs) -> eid``: given an entity type, attributes and inlined relations, return the eid of the entity to be inserted, *with no guarantee that anything has been inserted in database*,* ``prepare_update_entity(<entity type>, eid, **kwargs) -> None``: given an entity type and eid, promise for update given attributes and inlined relations *with no guarantee that anything has been inserted in database*,* ``prepare_insert_relation(eid_from, rtype, eid_to) -> None``: indicate that a relation ``rtype`` should be added between entities with eids ``eid_from`` and ``eid_to``. Similar to ``prepare_insert_entity()``, *there is no guarantee that the relation will be inserted in database*,* ``flush() -> None``: flush any temporary data to database. May be called several times during an import,* ``commit() -> None``: commit the database transaction,* ``finish() -> None``: additional stuff to do after import is terminated... autoclass:: cubicweb.dataimport.stores.RQLObjectStore.. autoclass:: cubicweb.dataimport.stores.NoHookRQLObjectStore.. autoclass:: cubicweb.dataimport.stores.MetaGenerator"""importinspectimportwarningsfromdatetimeimportdatetimefromcopyimportcopyfromlogilab.common.deprecationimportdeprecatedfromlogilab.common.decoratorsimportcachedfromcubicweb.schemaimportMETA_RTYPES,VIRTUAL_RTYPESfromcubicweb.server.editionimportEditedEntityclassRQLObjectStore(object):"""Store that works by making RQL queries, hence with all the cubicweb's machinery activated. """def__init__(self,cnx,commit=None):ifcommitisnotNone:warnings.warn('[3.19] commit argument should not be specified ''as the cnx object already provides it.',DeprecationWarning,stacklevel=2)self._cnx=cnxself._commit=commitorcnx.commit# XXX 3.21 deprecated attributesself.eids={}self.types={}defrql(self,*args):"""Execute a RQL query. This is NOT part of the store API."""returnself._cnx.execute(*args)defprepare_insert_entity(self,*args,**kwargs):"""Given an entity type, attributes and inlined relations, returns the inserted entity's eid. """entity=self._cnx.create_entity(*args,**kwargs)self.eids[entity.eid]=entityself.types.setdefault(args[0],[]).append(entity.eid)returnentity.eiddefprepare_update_entity(self,etype,eid,**kwargs):"""Given an entity type and eid, updates the corresponding entity with specified attributes and inlined relations. """entity=self._cnx.entity_from_eid(eid)assertentity.cw_etype==etype,'Trying to update with wrong type {}'.format(etype)# XXX some inlined relations may already existsentity.cw_set(**kwargs)defprepare_insert_relation(self,eid_from,rtype,eid_to,**kwargs):"""Insert into the database a relation ``rtype`` between entities with eids ``eid_from`` and ``eid_to``. """self.rql('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s'%rtype,{'x':int(eid_from),'y':int(eid_to)})defflush(self):"""Nothing to flush for this store."""passdefcommit(self):"""Commit the database transaction."""returnself._commit()@propertydefsession(self):warnings.warn('[3.19] deprecated property.',DeprecationWarning,stacklevel=2)returnself._cnx.repo._get_session(self._cnx.sessionid)@deprecated("[3.19] use cnx.find(*args, **kwargs).entities() instead")deffind_entities(self,*args,**kwargs):returnself._cnx.find(*args,**kwargs).entities()@deprecated("[3.19] use cnx.find(*args, **kwargs).one() instead")deffind_one_entity(self,*args,**kwargs):returnself._cnx.find(*args,**kwargs).one()@deprecated('[3.21] use prepare_insert_entity instead')defcreate_entity(self,*args,**kwargs):eid=self.prepare_insert_entity(*args,**kwargs)returnself._cnx.entity_from_eid(eid)@deprecated('[3.21] use prepare_insert_relation instead')defrelate(self,eid_from,rtype,eid_to,**kwargs):self.prepare_insert_relation(eid_from,rtype,eid_to,**kwargs)classNoHookRQLObjectStore(RQLObjectStore):"""Store that works by accessing low-level CubicWeb's source API, with all hooks deactivated. It must be given a metadata generator object to handle metadata which are usually handled by hooks (see :class:`MetaGenerator`). """def__init__(self,cnx,metagen=None):super(NoHookRQLObjectStore,self).__init__(cnx)self.source=cnx.repo.system_sourceself.rschema=cnx.repo.schema.rschemaself.add_relation=self.source.add_relationifmetagenisNone:metagen=MetaGenerator(cnx)self.metagen=metagenself._nb_inserted_entities=0self._nb_inserted_types=0self._nb_inserted_relations=0# deactivate securitycnx.read_security=Falsecnx.write_security=Falsedefprepare_insert_entity(self,etype,**kwargs):"""Given an entity type, attributes and inlined relations, returns the inserted entity's eid. """fork,vinkwargs.iteritems():kwargs[k]=getattr(v,'eid',v)entity,rels=self.metagen.base_etype_dicts(etype)# make a copy to keep cached entity pristineentity=copy(entity)entity.cw_edited=copy(entity.cw_edited)entity.cw_clear_relation_cache()entity.cw_edited.update(kwargs,skipsec=False)entity_source,extid=self.metagen.init_entity(entity)cnx=self._cnxself.source.add_info(cnx,entity,entity_source,extid)self.source.add_entity(cnx,entity)kwargs=dict()ifinspect.getargspec(self.add_relation).keywords:kwargs['subjtype']=entity.cw_etypeforrtype,targeteidsinrels.iteritems():# targeteids may be a single eid or a list of eidsinlined=self.rschema(rtype).inlinedtry:fortargeteidintargeteids:self.add_relation(cnx,entity.eid,rtype,targeteid,inlined,**kwargs)exceptTypeError:self.add_relation(cnx,entity.eid,rtype,targeteids,inlined,**kwargs)self._nb_inserted_entities+=1returnentity.eid# XXX: prepare_update_entity is inherited from RQLObjectStore, it should be reimplemented to# actually skip hooks as prepare_insert_entitydefprepare_insert_relation(self,eid_from,rtype,eid_to,**kwargs):"""Insert into the database a relation ``rtype`` between entities with eids ``eid_from`` and ``eid_to``. """assertnotrtype.startswith('reverse_')self.add_relation(self._cnx,eid_from,rtype,eid_to,self.rschema(rtype).inlined)ifself.rschema(rtype).symmetric:self.add_relation(self._cnx,eid_to,rtype,eid_from,self.rschema(rtype).inlined)self._nb_inserted_relations+=1@property@deprecated('[3.21] deprecated')defnb_inserted_entities(self):returnself._nb_inserted_entities@property@deprecated('[3.21] deprecated')defnb_inserted_types(self):returnself._nb_inserted_types@property@deprecated('[3.21] deprecated')defnb_inserted_relations(self):returnself._nb_inserted_relationsclassMetaGenerator(object):"""Class responsible for generating standard metadata for imported entities. You may want to derive it to add application specific's metadata. Parameters: * `cnx`: connection to the repository * `baseurl`: optional base URL to be used for `cwuri` generation - default to config['base-url'] * `source`: optional source to be used as `cw_source` for imported entities """META_RELATIONS=(META_RTYPES-VIRTUAL_RTYPES-set(('eid','cwuri','is','is_instance_of','cw_source')))def__init__(self,cnx,baseurl=None,source=None):self._cnx=cnxifbaseurlisNone:config=cnx.vreg.configbaseurl=config['base-url']orconfig.default_base_url()ifnotbaseurl[-1]=='/':baseurl+='/'self.baseurl=baseurlifsourceisNone:source=cnx.repo.system_sourceself.source=sourceself.create_eid=cnx.repo.system_source.create_eidself.time=datetime.now()# attributes/relations shared by all entities of the same typeself.etype_attrs=[]self.etype_rels=[]# attributes/relations specific to each entityself.entity_attrs=['cwuri']#self.entity_rels = [] XXX not handled (YAGNI?)schema=cnx.vreg.schemarschema=schema.rschemaforrtypeinself.META_RELATIONS:# skip owned_by / created_by if user is the internal managerifcnx.user.eid==-1andrtypein('owned_by','created_by'):continueifrschema(rtype).final:self.etype_attrs.append(rtype)else:self.etype_rels.append(rtype)@cacheddefbase_etype_dicts(self,etype):entity=self._cnx.vreg['etypes'].etype_class(etype)(self._cnx)# entity are "surface" copied, avoid shared dict between copiesdelentity.cw_extra_kwargsentity.cw_edited=EditedEntity(entity)forattrinself.etype_attrs:genfunc=self.generate(attr)ifgenfunc:entity.cw_edited.edited_attribute(attr,genfunc(entity))rels={}forrelinself.etype_rels:genfunc=self.generate(rel)ifgenfunc:rels[rel]=genfunc(entity)returnentity,relsdefinit_entity(self,entity):entity.eid=self.create_eid(self._cnx)extid=entity.cw_edited.get('cwuri')forattrinself.entity_attrs:ifattrinentity.cw_edited:# already set, skip this attributecontinuegenfunc=self.generate(attr)ifgenfunc:entity.cw_edited.edited_attribute(attr,genfunc(entity))ifisinstance(extid,unicode):extid=extid.encode('utf-8')returnself.source,extiddefgenerate(self,rtype):returngetattr(self,'gen_%s'%rtype,None)defgen_cwuri(self,entity):assertself.baseurl,'baseurl is None while generating cwuri'returnu'%s%s'%(self.baseurl,entity.eid)defgen_creation_date(self,entity):returnself.timedefgen_modification_date(self,entity):returnself.timedefgen_created_by(self,entity):returnself._cnx.user.eiddefgen_owned_by(self,entity):returnself._cnx.user.eid