Metadata is now correctly wired up to the database and Datasets. Please review this as I got stuck on a bug for a few hours and may have something blatantly stupid going on. Next step is to add the metadata to all of the datatypes and do the edit.tml

This commit is contained in:
Ian Schenck
2007-07-05 19:37:22 +00:00
parent 6aa6c5a2d8
commit c96a8e7c3a
6 changed files with 64 additions and 50 deletions
+2 -2
View File
@@ -27,8 +27,8 @@ class Data( object ):
'test'
>>> DataTest.get_metadata_spec()['test'].desc
>>> DataTest.get_metadata_spec()['test'].attributes
>>> DataTest.get_metadata_spec()['test'].wrapper
<class 'galaxy.datatypes.metadata.MetadataWrapper'>
>>> DataTest.get_metadata_spec()['test'].param
<class 'galaxy.datatypes.metadata.MetadataParameter'>
"""
__metaclass__ = DataMeta
+42 -30
View File
@@ -1,4 +1,5 @@
import sys
from cookbook.patterns import Bunch
# Taken in part from Elixir and how they do it: http://elixir.ematia.de
@@ -22,16 +23,7 @@ class MetadataSpecCollection( dict ):
def iter( self ):
return self.itervalues()
class MetadataWrapper( object ):
def __init__( self, metadata ):
self.set_value( metadata )
def get_value( self ):
return self.value
def set_value( self, value ):
value = self.marshal(value)
self.validate(value)
self.value = value
class MetadataParameter( object ):
def marshal( self, value ):
'''
This method should/can be overridden to convert the incomming
@@ -47,36 +39,56 @@ class MetadataWrapper( object ):
class MetadataElementSpec( object ):
READONLY = 1
def __init__( self, datatype, name=None, desc=None, wrapper=MetadataWrapper, attributes=None, default=None ):
def __init__( self, datatype, name=None, desc=None, param=MetadataParameter, attributes=None, default=None ):
self.name = name
self.desc = desc
self.wrapper = wrapper
self.param = param
self.attributes = attributes
self.default = default
datatype._metadataspec.append( self )
def hasAttribute( self, attribute ):
return ((self.permission & attribute) == attribute)
def wrap( self, metadata ):
return self.wrapper(metadata)
return self.param(metadata)
class MetadataCollection( dict ):
def __init__( self, spec=None, *args ):
dict.__init__( self )
self.spec = spec
def __setitem__( self, key, value ):
if spec:
wrapper = spec[key].wrap( value )
dict.__setitem__( self, key, wrapper )
else:
dict.__setitem__( self, key, value )
def __getitem__( self, key ):
if spec:
if key not in self:
return self.spec[key].default
class MetadataCollection:
"""
MetadataCollection is not a collection at all, but rather a proxy
to the real metadata which is stored as a Bunch. This class
handles updating the reference to the Bunch when it is changed (so
that SQLAlchemy knows to update it) as well as returning default
values in cases when metadata is not set.
"""
def __init__(self, parent, spec):
self.parent = parent
self.bunch = parent._metadata or Bunch()
self.spec = spec or dict()
def __iter__(self):
return self.bunch.__iter__()
def get( self, key, default=None ):
if self.spec:
if self.spec.get(name, None):
default = default or self.spec[name].default
return self.bunch.get( key, default )
def items(self):
return self.bunch.items()
def __str__(self):
return self.bunch.__str__()
def __nonzero__(self):
return self.bunch.__nonzero__()
def __getattr__(self, name):
try:
return self.bunch.get( name )
except AttributeError, e:
if self.spec.get(name, None):
return self.spec[name].default
else:
return dict.__getitem__(self, key).get_value()
return None
def __setattr__(self, name, value):
if name in ["parent","bunch","spec"]:
self.__dict__[name] = value
else:
return dict.__getitem__(self, key)
setattr(self.__dict__["bunch"], name, value)
self.bunch = self.parent._metadata = Bunch( **self.bunch.__dict__ )
MetadataElement = Statement(MetadataElementSpec)
+17 -15
View File
@@ -12,6 +12,7 @@ from cookbook.patterns import Bunch
from galaxy import util
import tempfile
import galaxy.datatypes.registry
from galaxy.datatypes.metadata import MetadataCollection
datatypes_registry = galaxy.datatypes.registry.Registry() #Default Value Required for unit tests
@@ -152,7 +153,7 @@ class Dataset( object ):
self.extension = extension
self.dbkey = dbkey
self.state = state
self.metadata = metadata or Bunch()
self._metadata = metadata or Bunch()
self.parent_id = parent_id
self.designation = designation
self.deleted = False
@@ -160,6 +161,9 @@ class Dataset( object ):
# Relationships
self.history = history
self.validation_errors = validation_errors
# Deprecated: the metadatacollection now updates the object
# reference whenever any metadata is changed
def mark_metadata_changed( self ):
"""
Register changes to metadata with the history
@@ -167,8 +171,10 @@ class Dataset( object ):
FIXME: This is just temporary, I'd like to implement some kind of
object proxy mapper property for SQLAlchemy that handles
this magically.
Fixed. INS, 7/5/2007 Waiting to remove...
"""
self.metadata = Bunch( **self.metadata.__dict__ )
self._metadata = Bunch( **self._metadata.__dict__ )
@property
def ext( self ):
@@ -180,6 +186,15 @@ class Dataset( object ):
@property
def datatype( self ):
return datatypes_registry.get_datatype_by_extension( self.extension )
def get_metadata( self ):
if not self._metadata: self._metadata = Bunch()
return MetadataCollection( self, self.datatype.get_metadata_spec() )
def set_metadata( self, bunch ):
# Needs to accept a MetadataCollection, a bunch, or a dict
self._metadata = Bunch( **dict( bunch.items() ) )
metadata = property( get_metadata, set_metadata )
def change_datatype( self, new_ext ):
datatypes_registry.change_datatype( self, new_ext )
def get_size( self ):
@@ -286,19 +301,6 @@ class Dataset( object ):
except OSError, e:
log.critical('%s delete error %s' % (self.__class__.__name__, e))
# change datatype
def set_datatype( self, ext ):
current = datatypes_registry.get_datatype_by_extension( self.extension )
new = datatypes_registry.get_datatype_by_extension( ext )
# Copy over metadata that applies...according to spec (?)
new_meta = new.get_metadata_spec()
new_meta_bunch = Bunch()
for item in new_meta:
if item.name in self.metadata:
new_meta_bunch[item.name] = self.metadata[item.name]
self.metadata = new_meta_bunch
class ValidationError( object ):
def __init__( self, message=None, err_type=None, attributes=None ):
self.message = message
+1 -1
View File
@@ -65,7 +65,7 @@ Dataset.table = Table( "dataset", metadata,
Column( "extension", TrimmedString( 64 ) ),
Column( "dbkey", TrimmedString( 64 ) ),
Column( "state", TrimmedString( 64 ) ),
Column( "metadata", PickleType() ),
Column( "metadata", PickleType(), key="_metadata" ),
Column( "parent_id", Integer, nullable=True ),
Column( "designation", TrimmedString( 255 ) ),
Column( "deleted", Boolean ),
+1 -1
View File
@@ -31,7 +31,7 @@ class MappingTests( unittest.TestCase ):
assert hists[1].name == ( "H" * 255 )
assert hists[0].user == users[0]
assert hists[1].user is None
assert hists[1].datasets[0].metadata['chromCol'] == 1
assert hists[1].datasets[0].metadata.chromCol == 1
assert hists[1].datasets[0].file_name == "/tmp/dataset_%d.dat" % hists[1].datasets[0].id
# Do an update and check
hists[1].name = "History 2b"
+1 -1
View File
@@ -117,7 +117,7 @@ class DefaultToolAction( object ):
# FIXME: init_meta should take a dataset to copy from as an
# argument
if metadata_source:
data.metadata = Bunch( ** inp_data[metadata_source].metadata.__dict__ )
data.metadata = inp_data[metadata_source].metadata
else:
data.init_meta()
# Take dbkey from LAST input