hidden usage of datetime function which has been moved to lgc
"""provide replacement classes for gae db module, so that a gae model can beused as base for a cubicweb application by simply replacing :: from google.appengine.ext import dbby from cubicweb.goa import dbThe db.model api should be fully featured by replacement classes, with thefollowing differences:* all methods returning `google.appengine.ext.db.Model` instance(s) will return `cubicweb.goa.db.Model` instance instead (though you should see almost no difference since those instances have the same api)* class methods returning model instance take a `req` as first argument, unless they are called through an instance, representing the current request (accessible through `self.req` on almost all objects)* XXX no instance.<modelname>_set attributes, use instance.reverse_<attr name> instead* XXX reference property always return a list of objects, not the instance* XXX name/collection_name argument of properties constructor are ignored* XXX ListProperty:organization: Logilab:copyright: 2008-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"fromcopyimportdeepcopyfromlogilab.common.decoratorsimportcached,iclassmethodfromcubicwebimportBinary,entitiesfromcubicweb.reqimportRequestSessionBasefromcubicweb.rsetimportResultSetfromcubicweb.entityimportmetaentityfromcubicweb.server.utilsimportcrypt_passwordfromcubicweb.goaimportMODEfromcubicweb.goa.dbinitimportinit_relationsfromgoogle.appengine.api.datastoreimportGet,Put,Key,Entity,Queryfromgoogle.appengine.api.datastoreimportNormalizeAndTypeCheck,RunInTransactionfromgoogle.appengine.api.datastore_typesimportText,Blobfromgoogle.appengine.api.datastore_errorsimportBadKeyError# XXX remove this dependancyfromgoogle.appengine.extimportdbdefrset_from_objs(req,objs,attrs=('eid',),rql=None,args=None):"""return a ResultSet instance for list of objects"""ifobjsisNone:objs=()elifisinstance(objs,Entity):objs=(objs,)ifrqlisNone:rql='Any X'rows=[]description=[]rset=ResultSet(rows,rql,args,description=description)vreg=req.vregfori,objinenumerate(objs):line=[]linedescr=[]eschema=vreg.schema.eschema(obj.kind())forj,attrinenumerate(attrs):ifattr=='eid':value=obj.key()obj.row,obj.col=i,jdescr=eschema.typevalue=str(value)else:value=obj[attr]descr=str(eschema.destination(attr))line.append(value)linedescr.append(descr)rows.append(line)description.append(linedescr)forj,attrinenumerate(attrs):ifattr=='eid':entity=vreg.etype_class(eschema.type)(req,rset,i,j)rset._get_entity_cache_={(i,j):entity}rset.rowcount=len(rows)req.decorate_rset(rset)returnrsetdefneedrequest(wrapped):defwrapper(cls,*args,**kwargs):req=kwargs.pop('req',None)ifreqisNoneandargsandisinstance(args[0],RequestSessionBase):args=list(args)req=args.pop(0)ifreqisNone:req=getattr(cls,'req',None)ifreqisNone:raiseException('either call this method on an instance or ''specify the req argument')returnwrapped(cls,req,*args,**kwargs)returniclassmethod(wrapper)classgaedbmetaentity(metaentity):"""metaclass for goa.db.Model classes: filter entity / db model part, put aside the db model part for later creation of db model class. """def__new__(mcs,name,bases,classdict):ifnot'id'inclassdict:classdict['id']=nameentitycls=super(gaedbmetaentity,mcs).__new__(mcs,name,bases,classdict)returnentityclsTEST_MODELS={}defextract_dbmodel(entitycls):ifMODE=='test'andentityclsinTEST_MODELS:dbclassdict=TEST_MODELS[entitycls]else:dbclassdict={}forattr,valueinentitycls.__dict__.items():ifisinstance(value,db.Property)orisinstance(value,ReferencePropertyStub):dbclassdict[attr]=value# don't remove attr from entitycls, this make tests fail, and it's anyway# overwritten by descriptor at class initialization time#delattr(entitycls, attr)ifMODE=='test':TEST_MODELS[entitycls]=dbclassdictdbclassdict=deepcopy(dbclassdict)forpropname,propinTEST_MODELS[entitycls].iteritems():ifgetattr(prop,'reference_class',None)isdb._SELF_REFERENCE:dbclassdict[propname].reference_class=db._SELF_REFERENCEreturndbclassdictclassModel(entities.AnyEntity):id='Any'__metaclass__=gaedbmetaentityrow=col=0@classmethoddef__initialize__(cls):super(Model,cls).__initialize__()cls._attributes=frozenset(rschemaforrschemaincls.e_schema.subject_relations()ifrschema.final)def__init__(self,*args,**kwargs):# db.Model prototype:# __init__(self, parent=None, key_name=None, **kw)## Entity prototype:# __init__(self, req, rset, row=None, col=0)ifargsandisinstance(args[0],RequestSessionBase)or'req'inkwargs:super(Model,self).__init__(*args,**kwargs)self._gaeinitargs=Noneelse:super(Model,self).__init__(None,None)# if Model instances are given in kwargs, turn them into db modelforkey,valinkwargs.iteritems():ifkeyinself.e_schema.subject_relations()andnotself.e_schema.schema[key].final:ifisinstance(kwargs,(list,tuple)):val=[isinstance(x,Model)andx._dbmodelorxforxinval]elifisinstance(val,Model):val=val._dbmodelkwargs[key]=val.key()self._gaeinitargs=(args,kwargs)def__repr__(self):return'<ModelEntity %s%s%s at %s>'%(self.e_schema,self.eid,self.keys(),id(self))def_cubicweb_to_datastore(self,attr,value):attr=attr[2:]# remove 's_' / 'o_' prefixifattrinself._attributes:tschema=self.e_schema.destination(attr)iftschema=='String':iflen(value)>500:value=Text(value)eliftschema=='Password':# if value is a Binary instance, this mean we got it# from a query result and so it is already encryptedifisinstance(value,Binary):value=value.getvalue()else:value=crypt_password(value)eliftschema=='Bytes':ifisinstance(value,Binary):value=value.getvalue()value=Blob(value)else:value=Key(value)returnvaluedef_to_gae_dict(self,convert=True):gaedict={}forattr,valueinself.iteritems():attr='s_'+attrifvalueisnotNoneandconvert:value=self._cubicweb_to_datastore(attr,value)gaedict[attr]=valuereturngaedictdefto_gae_model(self):dbmodel=self._dbmodeldbmodel.update(self._to_gae_dict())returndbmodel@property@cacheddef_dbmodel(self):ifself.has_eid():assertself._gaeinitargsisNonetry:returnself.req.datastore_get(self.eid)exceptAttributeError:# self.req is not a server sessionreturnGet(self.eid)self.set_defaults()values=self._to_gae_dict(convert=False)parent=key_name=_app=Noneifself._gaeinitargsisnotNone:args,kwargs=self._gaeinitargsargs=list(args)ifargs:parent=args.pop(0)ifargs:key_name=args.pop(0)ifargs:_app=args.pop(0)assertnotargsif'parent'inkwargs:assertparentisNoneparent=kwargs.pop('parent')if'key_name'inkwargs:assertkey_nameisNonekey_name=kwargs.pop('key_name')if'_app'inkwargs:assert_appisNone_app=kwargs.pop('_app')forkey,valueinkwargs.iteritems():ifkeyinself._attributes:values['s_'+key]=valueelse:kwargs=Noneifkey_nameisNone:key_name=self.db_key_name()ifkey_nameisnotNone:key_name='key_'+key_nameforkey,valueinvalues.iteritems():ifvalueisNone:continuevalues[key]=self._cubicweb_to_datastore(key,value)entity=Entity(self.id,parent,_app,key_name)entity.update(values)init_relations(entity,self.e_schema)returnentitydefdb_key_name(self):"""override this method to control datastore key name that should be used at entity creation. Note that if this function return something else than None, the returned value will be prefixed by 'key_' to build the actual key name. """returnNonedefmetainformation(self):return{'type':self.id,'source':{'uri':'system'},'extid':None}defview(self,vid,__registry='views',**kwargs):"""shortcut to apply a view on this entity"""returnself.vreg[__registry].render(vid,self.req,rset=self.rset,row=self.row,col=self.col,**kwargs)@classmethoddef_rest_attr_info(cls):mainattr,needcheck=super(Model,cls)._rest_attr_info()ifneedcheck:return'eid',Falsereturnmainattr,needcheckdefget_value(self,name):try:value=self[name]exceptKeyError:ifnotself.has_eid():returnNonevalue=self._dbmodel.get('s_'+name)ifvalueisnotNone:ifisinstance(value,Text):value=unicode(value)elifisinstance(value,Blob):value=Binary(str(value))self[name]=valuereturnvaluedefhas_eid(self):ifself.eidisNone:returnFalsetry:Key(self.eid)returnTrueexceptBadKeyError:returnFalsedefcomplete(self,skip_bytes=True):passdefunrelated(self,rtype,targettype,role='subject',limit=None,ordermethod=None):# XXX dumb implementationiflimitisnotNone:objs=Query(str(targettype)).Get(limit)else:objs=Query(str(targettype)).Run()returnrset_from_objs(self.req,objs,('eid',),'Any X WHERE X is %s'%targettype)defkey(self):returnKey(self.eid)defput(self,req=None):ifreqisnotNoneandself.reqisNone:self.req=reqdbmodel=self.to_gae_model()key=Put(dbmodel)self.set_eid(str(key))ifself.reqisnotNoneandself.rsetisNone:self.rset=rset_from_objs(self.req,dbmodel,('eid',),'Any X WHERE X eid %(x)s',{'x':self.eid})self.row=self.col=0returndbmodel@needrequestdefget(cls,req,keys):# if check if this is a dict.key callifisinstance(cls,Model)andkeysincls._attributes:returnsuper(Model,cls).get(keys)rset=rset_from_objs(req,Get(keys),('eid',),'Any X WHERE X eid IN %(x)s',{'x':keys})returnlist(rset.entities())@needrequestdefget_by_id(cls,req,ids,parent=None):ifisinstance(parent,Model):parent=parent.key()ids,multiple=NormalizeAndTypeCheck(ids,(int,long))keys=[Key.from_path(cls.kind(),id,parent=parent)foridinids]rset=rset_from_objs(req,Get(keys))returnlist(rset.entities())@classmethoddefget_by_key_name(cls,req,key_names,parent=None):ifisinstance(parent,Model):parent=parent.key()key_names,multiple=NormalizeAndTypeCheck(key_names,basestring)keys=[Key.from_path(cls.kind(),name,parent=parent)fornameinkey_names]rset=rset_from_objs(req,Get(keys))returnlist(rset.entities())@classmethoddefget_or_insert(cls,req,key_name,**kwds):deftxn():entity=cls.get_by_key_name(key_name,parent=kwds.get('parent'))ifentityisNone:entity=cls(key_name=key_name,**kwds)entity.put()returnentityreturnRunInTransaction(txn)@classmethoddefall(cls,req):rset=rset_from_objs(req,Query(cls.id).Run())returnlist(rset.entities())@classmethoddefgql(cls,req,query_string,*args,**kwds):raiseNotImplementedError('use rql')@classmethoddefkind(cls):returncls.id@classmethoddefproperties(cls):raiseNotImplementedError('use eschema')defdynamic_properties(self):raiseNotImplementedError('use eschema')defis_saved(self):returnself.has_eid()defparent(self):parent=self._dbmodel.parent()ifnotparentisNone:rset=rset_from_objs(self.req,(parent,),('eid',),'Any X WHERE X eid %(x)s',{'x':parent.key()})parent=rset.get_entity(0,0)returnparentdefparent_key(self):returnself.parent().key()defto_xml(self):returnself._dbmodel.ToXml()# hijack AnyEntity classentities.AnyEntity=ModelBooleanProperty=db.BooleanPropertyURLProperty=db.URLPropertyDateProperty=db.DatePropertyDateTimeProperty=db.DateTimePropertyTimeProperty=db.TimePropertyStringProperty=db.StringPropertyTextProperty=db.TextPropertyBlobProperty=db.BlobPropertyIntegerProperty=db.IntegerPropertyFloatProperty=db.FloatPropertyListProperty=db.ListPropertySelfReferenceProperty=db.SelfReferencePropertyUserProperty=db.UserPropertyclassReferencePropertyStub(object):def__init__(self,cls,args,kwargs):self.cls=clsself.args=argsself.kwargs=kwargsself.required=Falseself.__dict__.update(kwargs)self.creation_counter=db.Property.creation_counterdb.Property.creation_counter+=1@propertydefdata_type(self):classFakeDataType(object):@staticmethoddefkind():returnself.cls.__name__returnFakeDataTypedefReferenceProperty(cls,*args,**kwargs):ifissubclass(cls,db.Model):cls=db.class_for_kind(cls.__name__)returndb.ReferenceProperty(cls,*args,**kwargs)returnReferencePropertyStub(cls,args,kwargs)