from sqlalchemy.ext.declarative import declarative_base
from sfa.util.sfalogging import logger
+from sfa.util.sfatime import utcparse, datetime_to_string
from sfa.util.xml import XML
from sfa.trust.gid import GID
if isinstance(v, StringTypes) and v.lower() in ['true']: v=True
if isinstance(v, StringTypes) and v.lower() in ['false']: v=False
setattr(self,k,v)
-
+
+ def validate_datetime (self, key, incoming):
+ if isinstance (incoming, datetime): return incoming
+ elif isinstance (incoming, (int,float)):return datetime.fromtimestamp (incoming)
+
# in addition we provide convenience for converting to and from xml records
# for this purpose only, we need the subclasses to define 'fields' as either
# a list or a dictionary
xml_record.parse_dict (input_dict)
return xml_record.toxml()
- def dump(self, dump_parents=False):
- for key in self.fields:
- if key == 'gid' and self.gid:
- gid = GID(string=self.gid)
- print " %s:" % key
- gid.dump(8, dump_parents)
- elif getattr(self,key,None):
- print " %s: %s" % (key, getattr(self,key))
+ def dump(self, format=None, dump_parents=False):
+ if not format:
+ format = 'text'
+ else:
+ format = format.lower()
+ if format == 'text':
+ self.dump_text(dump_parents)
+ elif format == 'xml':
+ print self.save_to_string()
+ elif format == 'simple':
+ print self.dump_simple()
+ else:
+ raise Exception, "Invalid format %s" % format
+
+ # xxx fixme
+ # turns out the date_created field is received by the client as a 'created' int
+ # (and 'last_updated' does not make it at all)
+ # let's be flexible
+ def date_repr (self,fields):
+ if not isinstance(fields,list): fields=[fields]
+ for field in fields:
+ value=getattr(self,field,None)
+ if isinstance (value,datetime):
+ return datetime_to_string (value)
+ elif isinstance (value,(int,float)):
+ return datetime_to_string(utcparse(value))
+ # fallback
+ return "** undef_datetime **"
+
+ def dump_text(self, dump_parents=False):
+ # print core fields in this order
+ core_fields = [ 'hrn', 'type', 'authority', 'date_created', 'created', 'last_updated', 'gid', ]
+ print "".join(['=' for i in range(40)])
+ print "RECORD"
+ print " hrn:", self.hrn
+ print " type:", self.type
+ print " authority:", self.authority
+ print " date created:", self.date_repr( ['date_created','created'] )
+ print " last updated:", self.date_repr('last_updated')
+ print " gid:"
+ print self.get_gid_object().dump_string(8, dump_parents)
+
+ # print remaining fields
+ for attrib_name in dir(self):
+ attrib = getattr(self, attrib_name)
+ # skip internals
+ if attrib_name.startswith('_'): continue
+ # skip core fields
+ if attrib_name in core_fields: continue
+ # skip callables
+ if callable (attrib): continue
+ print " %s: %s" % (attrib_name, attrib)
+ def dump_simple(self):
+ return "%s"%self
+
# # only intended for debugging
# def inspect (self, logger, message=""):
# logger.info("%s -- Inspecting AlchemyObj -- attrs"%message)
record_id = Column (Integer, primary_key=True)
# this is the discriminator that tells which class to use
classtype = Column (String)
+ # in a first version type was the discriminator
+ # but that could not accomodate for 'authority+sa' and the like
type = Column (String)
hrn = Column (String)
gid = Column (String)
if dict: self.load_from_dict (dict)
def __repr__(self):
- result="[Record id=%s, type=%s, hrn=%s, authority=%s, pointer=%s" % \
+ result="<Record id=%s, type=%s, hrn=%s, authority=%s, pointer=%s" % \
(self.record_id, self.type, self.hrn, self.authority, self.pointer)
# skip the uniform '--- BEGIN CERTIFICATE --' stuff
if self.gid: result+=" gid=%s..."%self.gid[28:36]
else: result+=" nogid"
- result += "]"
+ result += ">"
return result
@validates ('gid')
elif isinstance(gid, StringTypes): return gid
else: return gid.save_to_string(save_parents=True)
+ @validates ('date_created')
+ def validate_date_created (self, key, incoming): return self.validate_datetime (key, incoming)
+
+ @validates ('last_updated')
+ def validate_last_updated (self, key, incoming): return self.validate_datetime (key, incoming)
+
# xxx - there might be smarter ways to handle get/set'ing gid using validation hooks
def get_gid_object (self):
if not self.gid: return None
self.last_updated=now
##############################
+# all subclasses define a convenience constructor with a default value for type,
+# and when applicable a way to define local fields in a kwd=value argument
+####################
class RegAuthority (RegRecord):
__tablename__ = 'authorities'
__mapper_args__ = { 'polymorphic_identity' : 'authority' }
record_id = Column (Integer, ForeignKey ("records.record_id"), primary_key=True)
+ def __init__ (self, **kwds):
+ # fill in type if not previously set
+ if 'type' not in kwds: kwds['type']='authority'
+ # base class constructor
+ RegRecord.__init__(self, **kwds)
+
# no proper data yet, just hack the typename
def __repr__ (self):
return RegRecord.__repr__(self).replace("Record","Authority")
-##############################
+####################
+# slice x user (researchers) association
+slice_researcher_table = \
+ Table ( 'slice_researcher', Base.metadata,
+ Column ('slice_id', Integer, ForeignKey ('records.record_id'), primary_key=True),
+ Column ('researcher_id', Integer, ForeignKey ('records.record_id'), primary_key=True),
+ )
+
+####################
class RegSlice (RegRecord):
__tablename__ = 'slices'
__mapper_args__ = { 'polymorphic_identity' : 'slice' }
record_id = Column (Integer, ForeignKey ("records.record_id"), primary_key=True)
-
+ #### extensions come here
+ reg_researchers = relationship \
+ ('RegUser',
+ secondary=slice_researcher_table,
+ primaryjoin=RegRecord.record_id==slice_researcher_table.c.slice_id,
+ secondaryjoin=RegRecord.record_id==slice_researcher_table.c.researcher_id,
+ backref="reg_slices_as_researcher")
+
+ def __init__ (self, **kwds):
+ if 'type' not in kwds: kwds['type']='slice'
+ RegRecord.__init__(self, **kwds)
+
def __repr__ (self):
return RegRecord.__repr__(self).replace("Record","Slice")
-##############################
+####################
class RegNode (RegRecord):
__tablename__ = 'nodes'
__mapper_args__ = { 'polymorphic_identity' : 'node' }
record_id = Column (Integer, ForeignKey ("records.record_id"), primary_key=True)
+ def __init__ (self, **kwds):
+ if 'type' not in kwds: kwds['type']='node'
+ RegRecord.__init__(self, **kwds)
+
def __repr__ (self):
return RegRecord.__repr__(self).replace("Record","Node")
-##############################
+####################
class RegUser (RegRecord):
__tablename__ = 'users'
# these objects will have type='user' in the records table
__mapper_args__ = { 'polymorphic_identity' : 'user' }
record_id = Column (Integer, ForeignKey ("records.record_id"), primary_key=True)
+ #### extensions come here
email = Column ('email', String)
- keys = relationship ('RegKey', backref='user')
+ # can't use name 'keys' here because when loading from xml we're getting
+ # a 'keys' tag, and assigning a list of strings in a reference column like this crashes
+ reg_keys = relationship \
+ ('RegKey', backref='reg_user',
+ cascade="all, delete, delete-orphan")
+ # so we can use RegUser (email=.., hrn=..) and the like
def __init__ (self, **kwds):
# handle local settings
if 'email' in kwds: self.email=kwds.pop('email')
- # fill in type if not previously set
if 'type' not in kwds: kwds['type']='user'
RegRecord.__init__(self, **kwds)
# append stuff at the end of the record __repr__
def __repr__ (self):
result = RegRecord.__repr__(self).replace("Record","User")
- result.replace ("]"," email=%s"%self.email)
- result += "]"
+ result.replace (">"," email=%s"%self.email)
+ result += ">"
return result
-
+
@validates('email')
def validate_email(self, key, address):
assert '@' in address
# xxx tocheck : not sure about eager loading of this one
# meaning, when querying the whole records, we expect there should
# be a single query to fetch all the keys
+# or, is it enough that we issue a single query to retrieve all the keys
class RegKey (Base):
__tablename__ = 'keys'
key_id = Column (Integer, primary_key=True)
if pointer: self.pointer=pointer
def __repr__ (self):
- result="[key key=%s..."%self.key[8:16]
- try: result += " user=%s"%self.user.record_id
- except: result += " <orphan>"
- result += "]"
+ result="<key id=%s key=%s..."%(self.key_id,self.key[8:16],)
+ try: result += " user=%s"%self.reg_user.record_id
+ except: result += " no-user"
+ result += ">"
return result
##############################
-# although the db needs of course to be reachable,
+# although the db needs of course to be reachable for the following functions
# the schema management functions are here and not in alchemy
# because the actual details of the classes need to be known
# migrations: this code has no notion of the previous versions
xml_dict = xml_record.todict()
logger.info("load from xml, keys=%s"%xml_dict.keys())
return make_record_dict (xml_dict)
+