3 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/9da06a2f3db4/ Changeset: 9da06a2f3db4 Branch: search User: Kyle Ellrott Date: 2013-10-24 21:50:53 Summary: Default merge Affected #: 45 files diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/galaxy/datatypes/binary.py --- a/lib/galaxy/datatypes/binary.py +++ b/lib/galaxy/datatypes/binary.py @@ -42,11 +42,20 @@ Binary.unsniffable_binary_formats.append(ext) @staticmethod - def is_sniffable_binary(filename): + def is_sniffable_binary( filename ): + format_information = None for format in Binary.sniffable_binary_formats: - if format["class"]().sniff(filename): - return (format["type"], format["ext"]) - return None + format_instance = format[ "class" ]() + try: + if format_instance.sniff(filename): + format_information = ( format["type"], format[ "ext" ] ) + break + except Exception: + # Sniffer raised exception, could be any number of + # reasons for this so there is not much to do besides + # trying next sniffer. + pass + return format_information @staticmethod def is_ext_unsniffable(ext): diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/galaxy/datatypes/checkers.py --- a/lib/galaxy/datatypes/checkers.py +++ b/lib/galaxy/datatypes/checkers.py @@ -1,5 +1,6 @@ import os, gzip, re, gzip, zipfile, binascii, bz2, imghdr from galaxy import util +from StringIO import StringIO try: import Image as PIL @@ -53,20 +54,15 @@ if file_path: temp = open( name, "U" ) else: - temp = name + temp = StringIO( name ) chars_read = 0 - for chars in temp: - for char in chars: - chars_read += 1 + try: + for char in temp.read( 100 ): if util.is_binary( char ): is_binary = True break - if chars_read > 100: - break - if chars_read > 100: - break - if file_path: - temp.close() + finally: + temp.close( ) return is_binary def check_gzip( file_path ): diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/galaxy/datatypes/metadata.py --- a/lib/galaxy/datatypes/metadata.py +++ b/lib/galaxy/datatypes/metadata.py @@ -5,7 +5,6 @@ from galaxy import eggs eggs.require("simplejson") - import copy import cPickle import logging @@ -37,10 +36,12 @@ """ def __init__( self, target ): self.target = target + def __call__( self, *args, **kwargs ): class_locals = sys._getframe( 1 ).f_locals #get the locals dictionary of the frame object one down in the call stack (i.e. the Datatype class calling MetadataElement) statements = class_locals.setdefault( STATEMENTS, [] ) #get and set '__galaxy_statments__' to an empty list if not in locals dict statements.append( ( self, args, kwargs ) ) #add Statement containing info to populate a MetadataElementSpec + @classmethod def process( cls, element ): for statement, args, kwargs in getattr( element, STATEMENTS, [] ): @@ -59,29 +60,38 @@ #initialize dict if needed if self.parent._metadata is None: self.parent._metadata = {} + def get_parent( self ): if "_parent" in self.__dict__: return self.__dict__["_parent"]() return None + def set_parent( self, parent ): self.__dict__["_parent"] = weakref.ref( parent ) # use weakref to prevent a circular reference interfering with garbage collection: hda/lda (parent) <--> MetadataCollection (self) ; needs to be hashable, so cannot use proxy. parent = property( get_parent, set_parent ) + @property def spec( self ): return self.parent.datatype.metadata_spec + def __iter__( self ): return self.parent._metadata.__iter__() + def get( self, key, default=None ): try: return self.__getattr__( key ) or default except: return default + def items(self): return iter( [ ( k, self.get( k ) ) for k in self.spec.iterkeys() ] ) + def __str__(self): return dict( self.items() ).__str__() + def __nonzero__( self ): return bool( self.parent._metadata ) + def __getattr__( self, name ): if name in self.spec: if name in self.parent._metadata: @@ -89,6 +99,7 @@ return self.spec[name].wrap( self.spec[name].default, object_session( self.parent ) ) if name in self.parent._metadata: return self.parent._metadata[name] + def __setattr__( self, name, value ): if name == "parent": return self.set_parent( value ) @@ -97,14 +108,17 @@ self.parent._metadata[name] = self.spec[name].unwrap( value ) else: self.parent._metadata[name] = value + def element_is_set( self, name ): return bool( self.parent._metadata.get( name, False ) ) + def get_html_by_name( self, name, **kwd ): if name in self.spec: rval = self.spec[name].param.get_html( value=getattr( self, name ), context=self, **kwd ) if rval is None: return self.spec[name].no_value return rval + def make_dict_copy( self, to_copy ): """Makes a deep copy of input iterable to_copy according to self.spec""" rval = {} @@ -112,6 +126,7 @@ if key in self.spec: rval[key] = self.spec[key].param.make_copy( value, target_context=self, source_context=to_copy ) return rval + def from_JSON_dict( self, filename ): dataset = self.parent log.debug( 'loading metadata from file for: %s %s' % ( dataset.__class__.__name__, dataset.id ) ) @@ -123,6 +138,7 @@ #if the metadata value is not found in our externally set metadata but it has a value in the 'old' #metadata associated with our dataset, we'll delete it from our dataset's metadata dict del dataset._metadata[ name ] + def to_JSON_dict( self, filename ): #galaxy.model.customtypes.json_encoder.encode() meta_dict = {} @@ -131,6 +147,7 @@ if name in dataset_meta_dict: meta_dict[ name ] = spec.param.to_external_value( dataset_meta_dict[ name ] ) simplejson.dump( meta_dict, open( filename, 'wb+' ) ) + def __getstate__( self ): return None #cannot pickle a weakref item (self._parent), when data._metadata_collection is None, it will be recreated on demand @@ -163,10 +180,12 @@ def __init__( self, spec ): self.spec = spec - def get_html_field( self, value=None, context={}, other_values={}, **kwd ): + def get_html_field( self, value=None, context=None, other_values=None, **kwd ): + context = context or {} + other_values = other_values or {} return form_builder.TextField( self.spec.name, value=value ) - def get_html( self, value, context={}, other_values={}, **kwd ): + def get_html( self, value, context=None, other_values=None, **kwd ): """ The "context" is simply the metadata collection/bunch holding this piece of metadata. This is passed in to allow for @@ -175,6 +194,9 @@ example, a column assignment should validate against the number of columns in the dataset. """ + context = context or {} + other_values = other_values or {} + if self.spec.get("readonly"): return value if self.spec.get("optional"): @@ -296,7 +318,10 @@ value = [value] return ",".join( map( str, value ) ) - def get_html_field( self, value=None, context={}, other_values={}, values=None, **kwd ): + def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd ): + context = context or {} + other_values = other_values or {} + field = form_builder.SelectField( self.spec.name, multiple=self.multiple, display=self.spec.get("display") ) if self.values: value_list = self.values @@ -316,7 +341,10 @@ field.add_option( val, label, selected=False ) return field - def get_html( self, value, context={}, other_values={}, values=None, **kwd ): + def get_html( self, value, context=None, other_values=None, values=None, **kwd ): + context = context or {} + other_values = other_values or {} + if self.spec.get("readonly"): if value in [ None, [] ]: return str( self.spec.no_value ) @@ -338,21 +366,30 @@ if not isinstance( value, list ): return [value] return value + class DBKeyParameter( SelectParameter ): - def get_html_field( self, value=None, context={}, other_values={}, values=None, **kwd): + + def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd): + context = context or {} + other_values = other_values or {} try: values = kwd['trans'].db_builds except KeyError: pass return super(DBKeyParameter, self).get_html_field( value, context, other_values, values, **kwd) - def get_html( self, value=None, context={}, other_values={}, values=None, **kwd): + + def get_html( self, value=None, context=None, other_values=None, values=None, **kwd): + context = context or {} + other_values = other_values or {} try: values = kwd['trans'].db_builds except KeyError: pass return super(DBKeyParameter, self).get_html( value, context, other_values, values, **kwd) + class RangeParameter( SelectParameter ): + def __init__( self, spec ): SelectParameter.__init__( self, spec ) # The spec must be set with min and max values @@ -360,12 +397,18 @@ self.max = spec.get( "max" ) or 1 self.step = self.spec.get( "step" ) or 1 - def get_html_field( self, value=None, context={}, other_values={}, values=None, **kwd ): + def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd ): + context = context or {} + other_values = other_values or {} + if values is None: values = zip( range( self.min, self.max, self.step ), range( self.min, self.max, self.step )) return SelectParameter.get_html_field( self, value=value, context=context, other_values=other_values, values=values, **kwd ) - def get_html( self, value, context={}, other_values={}, values=None, **kwd ): + def get_html( self, value, context=None, other_values=None, values=None, **kwd ): + context = context or {} + other_values = other_values or {} + if values is None: values = zip( range( self.min, self.max, self.step ), range( self.min, self.max, self.step )) return SelectParameter.get_html( self, value, context=context, other_values=other_values, values=values, **kwd ) @@ -376,35 +419,46 @@ values = [ int(x) for x in value ] return values + class ColumnParameter( RangeParameter ): - def get_html_field( self, value=None, context={}, other_values={}, values=None, **kwd ): + def get_html_field( self, value=None, context=None, other_values=None, values=None, **kwd ): + context = context or {} + other_values = other_values or {} + if values is None and context: column_range = range( 1, ( context.columns or 0 ) + 1, 1 ) values = zip( column_range, column_range ) return RangeParameter.get_html_field( self, value=value, context=context, other_values=other_values, values=values, **kwd ) - def get_html( self, value, context={}, other_values={}, values=None, **kwd ): + def get_html( self, value, context=None, other_values=None, values=None, **kwd ): + context = context or {} + other_values = other_values or {} + if values is None and context: column_range = range( 1, ( context.columns or 0 ) + 1, 1 ) values = zip( column_range, column_range ) return RangeParameter.get_html( self, value, context=context, other_values=other_values, values=values, **kwd ) + class ColumnTypesParameter( MetadataParameter ): def to_string( self, value ): return ",".join( map( str, value ) ) + class ListParameter( MetadataParameter ): def to_string( self, value ): return ",".join( [str(x) for x in value] ) + class DictParameter( MetadataParameter ): def to_string( self, value ): return simplejson.dumps( value ) + class PythonObjectParameter( MetadataParameter ): def to_string( self, value ): @@ -412,16 +466,21 @@ return self.spec._to_string( self.spec.no_value ) return self.spec._to_string( value ) - def get_html_field( self, value=None, context={}, other_values={}, **kwd ): + def get_html_field( self, value=None, context=None, other_values=None, **kwd ): + context = context or {} + other_values = other_values or {} return form_builder.TextField( self.spec.name, value=self._to_string( value ) ) - def get_html( self, value=None, context={}, other_values={}, **kwd ): + def get_html( self, value=None, context=None, other_values=None, **kwd ): + context = context or {} + other_values = other_values or {} return str( self ) @classmethod def marshal( cls, value ): return value + class FileParameter( MetadataParameter ): def to_string( self, value ): @@ -429,10 +488,14 @@ return str( self.spec.no_value ) return value.file_name - def get_html_field( self, value=None, context={}, other_values={}, **kwd ): + def get_html_field( self, value=None, context=None, other_values=None, **kwd ): + context = context or {} + other_values = other_values or {} return form_builder.TextField( self.spec.name, value=str( value.id ) ) - def get_html( self, value=None, context={}, other_values={}, **kwd ): + def get_html( self, value=None, context=None, other_values=None, **kwd ): + context = context or {} + other_values = other_values or {} return "<div>No display available for Metadata Files</div>" def wrap( self, value, session ): @@ -497,12 +560,15 @@ #we do not include 'dataset' in the kwds passed, as from_JSON_value() will handle this for us return MetadataTempFile( **kwds ) + #This class is used when a database file connection is not available class MetadataTempFile( object ): tmp_dir = 'database/tmp' #this should be overwritten as necessary in calling scripts + def __init__( self, **kwds ): self.kwds = kwds self._filename = None + @property def file_name( self ): if self._filename is None: @@ -510,17 +576,21 @@ self._filename = abspath( tempfile.NamedTemporaryFile( dir = self.tmp_dir, prefix = "metadata_temp_file_" ).name ) open( self._filename, 'wb+' ) #create an empty file, so it can't be reused using tempfile return self._filename + def to_JSON( self ): return { '__class__':self.__class__.__name__, 'filename':self.file_name, 'kwds':self.kwds } + @classmethod def from_JSON( cls, json_dict ): #need to ensure our keywords are not unicode rval = cls( **stringify_dictionary_keys( json_dict['kwds'] ) ) rval._filename = json_dict['filename'] return rval + @classmethod def is_JSONified_value( cls, value ): return ( isinstance( value, dict ) and value.get( '__class__', None ) == cls.__name__ ) + @classmethod def cleanup_from_JSON_dict_filename( cls, filename ): try: @@ -533,12 +603,15 @@ except Exception, e: log.debug( 'Failed to cleanup MetadataTempFile temp files from %s: %s' % ( filename, e ) ) + #Class with methods allowing set_meta() to be called externally to the Galaxy head class JobExternalOutputMetadataWrapper( object ): #this class allows access to external metadata filenames for all outputs associated with a job #We will use JSON as the medium of exchange of information, except for the DatasetInstance object which will use pickle (in the future this could be JSONified as well) + def __init__( self, job ): self.job_id = job.id + def get_output_filenames_by_dataset( self, dataset, sa_session ): if isinstance( dataset, galaxy.model.HistoryDatasetAssociation ): return sa_session.query( galaxy.model.JobExternalOutputMetadata ) \ @@ -549,12 +622,15 @@ .filter_by( job_id = self.job_id, library_dataset_dataset_association_id = dataset.id ) \ .first() #there should only be one or None return None + def get_dataset_metadata_key( self, dataset ): # Set meta can be called on library items and history items, # need to make different keys for them, since ids can overlap return "%s_%d" % ( dataset.__class__.__name__, dataset.id ) + def setup_external_metadata( self, datasets, sa_session, exec_dir=None, tmp_dir=None, dataset_files_path=None, - output_fnames=None, config_root=None, config_file=None, datatypes_config=None, job_metadata=None, kwds={} ): + output_fnames=None, config_root=None, config_file=None, datatypes_config=None, job_metadata=None, kwds=None ): + kwds = kwds or {} #fill in metadata_files_dict and return the command with args required to set metadata def __metadata_files_list_to_cmd_line( metadata_files ): def __get_filename_override(): @@ -652,6 +728,7 @@ os.remove( fname ) except Exception, e: log.debug( 'Failed to cleanup external metadata file (%s) for %s: %s' % ( key, dataset_key, e ) ) + def set_job_runner_external_pid( self, pid, sa_session ): for metadata_files in sa_session.query( galaxy.model.Job ).get( self.job_id ).external_output_metadata: metadata_files.job_runner_external_pid = pid diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/galaxy/datatypes/registry.py --- a/lib/galaxy/datatypes/registry.py +++ b/lib/galaxy/datatypes/registry.py @@ -547,8 +547,11 @@ display_app.id = tool_dict[ 'guid' ] break if deactivate: - del self.display_applications[ display_app.id ] - del self.datatypes_by_extension[ extension ].display_applications[ display_app.id ] + if display_app.id in self.display_applications: + del self.display_applications[ display_app.id ] + if extension in self.datatypes_by_extension: + if display_app.id in self.datatypes_by_extension[ extension ].display_applications: + del self.datatypes_by_extension[ extension ].display_applications[ display_app.id ] if inherit and ( self.datatypes_by_extension[ extension ], display_app ) in self.inherit_display_application_by_class: self.inherit_display_application_by_class.remove( ( self.datatypes_by_extension[ extension ], display_app ) ) self.log.debug( "Deactivated display application '%s' for datatype '%s'." % ( display_app.id, extension ) ) diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/galaxy/webapps/galaxy/api/history_contents.py --- a/lib/galaxy/webapps/galaxy/api/history_contents.py +++ b/lib/galaxy/webapps/galaxy/api/history_contents.py @@ -311,8 +311,9 @@ payload = self._validate_and_parse_update_payload( payload ) hda = self.get_dataset( trans, id, check_ownership=True, check_accessible=True, check_state=True ) - # additional checks here (security, etc.) - changed = self.set_hda_from_dict( trans, hda, payload ) + # get_dataset can return a string during an error + if hda and isinstance( hda, trans.model.HistoryDatasetAssociation ): + changed = self.set_hda_from_dict( trans, hda, payload ) except Exception, exception: log.error( 'Update of history (%s), HDA (%s) failed: %s', diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/galaxy/webapps/galaxy/api/users.py --- a/lib/galaxy/webapps/galaxy/api/users.py +++ b/lib/galaxy/webapps/galaxy/api/users.py @@ -4,7 +4,7 @@ import logging from paste.httpexceptions import HTTPBadRequest, HTTPNotImplemented from galaxy import util, web -from galaxy.web.base.controller import BaseAPIController, url_for +from galaxy.web.base.controller import BaseAPIController log = logging.getLogger( __name__ ) @@ -22,27 +22,19 @@ query = trans.sa_session.query( trans.app.model.User ) deleted = util.string_as_bool( deleted ) if deleted: - route = 'deleted_user' query = query.filter( trans.app.model.User.table.c.deleted == True ) # only admins can see deleted users if not trans.user_is_admin(): return [] - else: - route = 'user' query = query.filter( trans.app.model.User.table.c.deleted == False ) # special case: user can see only their own user if not trans.user_is_admin(): item = trans.user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) - item['url'] = url_for( route, id=item['id'] ) - item['quota_percent'] = trans.app.quota_agent.get_percent( trans=trans ) return [item] - for user in query: item = user.to_dict( value_mapper={ 'id': trans.security.encode_id } ) #TODO: move into api_values - item['quota_percent'] = trans.app.quota_agent.get_percent( trans=trans ) - item['url'] = url_for( route, id=item['id'] ) rval.append( item ) return rval diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/galaxy/webapps/tool_shed/controllers/repository.py --- a/lib/galaxy/webapps/tool_shed/controllers/repository.py +++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py @@ -2741,6 +2741,9 @@ changeset_revision = kwd.get( 'changeset_revision', None ) repository = suc.get_repository_by_name_and_owner( trans.app, name, owner ) if repository: + repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, + trans.security.encode_id( repository.id ), + changeset_revision ) repo_dir = repository.repo_path( trans.app ) repo = hg.repository( suc.get_configured_ui(), repo_dir ) tool_shed_status_dict = {} @@ -2751,21 +2754,24 @@ tool_shed_status_dict[ 'latest_installable_revision' ] = 'True' else: next_installable_revision = suc.get_next_downloadable_changeset_revision( repository, repo, changeset_revision ) - if next_installable_revision: - tool_shed_status_dict[ 'latest_installable_revision' ] = 'False' + if repository_metadata is None: + if next_installable_revision: + tool_shed_status_dict[ 'latest_installable_revision' ] = 'True' + else: + tool_shed_status_dict[ 'latest_installable_revision' ] = 'False' else: - tool_shed_status_dict[ 'latest_installable_revision' ] = 'True' + if next_installable_revision: + tool_shed_status_dict[ 'latest_installable_revision' ] = 'False' + else: + tool_shed_status_dict[ 'latest_installable_revision' ] = 'True' # Handle revision updates. if changeset_revision == repository.tip( trans.app ): tool_shed_status_dict[ 'revision_update' ] = 'False' else: - repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, - trans.security.encode_id( repository.id ), - changeset_revision ) - if repository_metadata: + if repository_metadata is None: + tool_shed_status_dict[ 'revision_update' ] = 'True' + else: tool_shed_status_dict[ 'revision_update' ] = 'False' - else: - tool_shed_status_dict[ 'revision_update' ] = 'True' # Handle revision upgrades. ordered_metadata_changeset_revisions = suc.get_ordered_metadata_changeset_revisions( repository, repo, downloadable=True ) num_metadata_revisions = len( ordered_metadata_changeset_revisions ) diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py --- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py +++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py @@ -502,7 +502,6 @@ pre_cmd = './configure %s && make && make install' % configure_opts else: pre_cmd = './configure prefix=$INSTALL_DIR %s && make && make install' % configure_opts - cmd = install_environment.build_command( td_common_util.evaluate_template( pre_cmd, install_dir ) ) return_code = handle_command( app, tool_dependency, install_dir, cmd ) if return_code: diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/tool_shed/util/commit_util.py --- a/lib/tool_shed/util/commit_util.py +++ b/lib/tool_shed/util/commit_util.py @@ -318,22 +318,23 @@ error_message = 'Unable to locate repository with name %s and owner %s. ' % ( str( name ), str( owner ) ) return revised, elem, error_message -def handle_set_environment_for_install( trans, package_altered, altered, actions_elem, action_index, action_elem, unpopulate=False ): +def handle_repository_dependency_sub_elem( trans, package_altered, altered, actions_elem, action_index, action_elem, unpopulate=False ): + # This method populates the toolshed and changeset_revision attributes for each of the following. # <action type="set_environment_for_install"> - # <repository name="package_eigen_2_0" owner="test" changeset_revision="09eb05087cd0"> - # <package name="eigen" version="2.0.17" /> - # </repository> - # </action> + # <action type="setup_r_environment"> + # <action type="setup_ruby_environment"> for repo_index, repo_elem in enumerate( action_elem ): - revised, repository_elem, error_message = handle_repository_dependency_elem( trans, repo_elem, unpopulate=unpopulate ) - if error_message: - exception_message = 'The tool_dependencies.xml file contains an invalid <repository> tag. %s' % error_message - raise Exception( exception_message ) - if revised: - action_elem[ repo_index ] = repository_elem - package_altered = True - if not altered: - altered = True + # Make sure to skip comments and tags that are not <repository>. + if repo_elem.tag == 'repository': + revised, repository_elem, error_message = handle_repository_dependency_elem( trans, repo_elem, unpopulate=unpopulate ) + if error_message: + exception_message = 'The tool_dependencies.xml file contains an invalid <repository> tag. %s' % error_message + raise Exception( exception_message ) + if revised: + action_elem[ repo_index ] = repository_elem + package_altered = True + if not altered: + altered = True if package_altered: actions_elem[ action_index ] = action_elem return package_altered, altered, actions_elem @@ -402,29 +403,28 @@ last_actions_elem[ last_actions_elem_package_index ] = last_actions_elem_package_elem actions_group_elem[ last_actions_index ] = last_actions_elem else: - last_actions_elem_action_type = last_actions_elem.get( 'type' ) - if last_actions_elem_action_type == 'set_environment_for_install': - last_actions_package_altered, altered, last_actions_elem = \ - handle_set_environment_for_install( trans, - last_actions_package_altered, - altered, - actions_group_elem, - last_actions_index, - last_actions_elem, - unpopulate=unpopulate ) + # Inspect the sub elements of last_actions_elem to locate all <repository> tags and + # populate them with toolshed and changeset_revision attributes if necessary. + last_actions_package_altered, altered, last_actions_elem = \ + handle_repository_dependency_sub_elem( trans, + last_actions_package_altered, + altered, + actions_group_elem, + last_actions_index, + last_actions_elem, + unpopulate=unpopulate ) elif actions_elem.tag == 'actions': # We are not in an <actions_group> tag set, so we must be in an <actions> tag set. for action_index, action_elem in enumerate( actions_elem ): - - action_type = action_elem.get( 'type' ) - if action_type == 'set_environment_for_install': - package_altered, altered, actions_elem = handle_set_environment_for_install( trans, - package_altered, - altered, - actions_elem, - action_index, - action_elem, - unpopulate=unpopulate ) + # Inspect the sub elements of last_actions_elem to locate all <repository> tags and populate them with + # toolshed and changeset_revision attributes if necessary. + package_altered, altered, actions_elem = handle_repository_dependency_sub_elem( trans, + package_altered, + altered, + actions_elem, + action_index, + action_elem, + unpopulate=unpopulate ) if package_altered: package_elem[ actions_index ] = actions_elem if package_altered: diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 lib/tool_shed/util/common_install_util.py --- a/lib/tool_shed/util/common_install_util.py +++ b/lib/tool_shed/util/common_install_util.py @@ -483,7 +483,7 @@ app.model.ToolDependency.installation_status.ERROR ]: installed_tool_dependencies.append( tool_dependency ) elif elem.tag == 'set_environment': - env_var_name = env_var_elem.get( 'name', None ) + env_var_name = elem.get( 'name', None ) if env_var_name: # Tool dependencies of type "set_environmnet" always have the version attribute set to None. attr_tup = ( env_var_name, None, 'set_environment' ) diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 static/scripts/galaxy.upload.js --- a/static/scripts/galaxy.upload.js +++ b/static/scripts/galaxy.upload.js @@ -278,7 +278,7 @@ sy.addClass(this.state.success); // update galaxy history - Galaxy.currHistoryPanel.refresh(); + Galaxy.currHistoryPanel.refreshHdas(); }, // error diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 static/scripts/mvc/dataset/hda-base.js --- a/static/scripts/mvc/dataset/hda-base.js +++ b/static/scripts/mvc/dataset/hda-base.js @@ -5,7 +5,7 @@ //============================================================================== /** @class Read only view for HistoryDatasetAssociation. * @name HDABaseView - * + * * @augments Backbone.View * @borrows LoggableMixin#logger as #logger * @borrows LoggableMixin#log as #log @@ -23,7 +23,7 @@ fxSpeed : 'fast', - // ......................................................................... SET UP + // ......................................................................... set up /** Set up the view, cache url templates, bind listeners * @param {Object} attributes * @config {Object} urlTemplates nested object containing url templates for this view @@ -44,16 +44,28 @@ this._setUpListeners(); }, + /** event listeners */ _setUpListeners : function(){ + // re-rendering on any model changes - this.model.on( 'change', this.render, this ); + this.model.on( 'change', function( model, options ){ + + // if the model moved into the ready state and is expanded without details, fetch those details now + if( this.model.changedAttributes().state && this.model.inReadyState() + && this.expanded && !this.model.hasDetails() ){ + this.model.fetch(); // will render automatically (due to lines below) + + } else { + this.render(); + } + }, this ); //this.on( 'all', function( event ){ // this.log( event ); //}, this ); }, - // ......................................................................... RENDER MAIN + // ......................................................................... render main /** Render this HDA, set up ui. * @fires rendered:ready when rendered and NO running HDAs * @fires rendered when rendered and running HDAs @@ -114,7 +126,6 @@ _setUpBehaviors : function( $container ){ $container = $container || this.$el; // set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.) - //TODO: we can potentially skip this step and call popupmenu directly on the download button make_popup_menus( $container ); $container.find( '[title]' ).tooltip({ placement : 'bottom' }); }, @@ -205,13 +216,10 @@ * @returns {jQuery} rendered DOM */ _render_titleLink : function(){ - return $( jQuery.trim( HDABaseView.templates.titleLink( - //TODO?? does this need urls? - _.extend( this.model.toJSON(), { urls: this.urls } ) - ))); + return $( jQuery.trim( HDABaseView.templates.titleLink( this.model.toJSON() ))); }, - // ......................................................................... RENDER BODY + // ......................................................................... body /** Render the data/metadata summary (format, size, misc info, etc.). * @returns {jQuery} rendered DOM */ @@ -275,7 +283,7 @@ /** Render links to external genome display applications (igb, gbrowse, etc.). * @param {jQuery} $parent the jq node to search for .display-apps and render into to (defaults to this.$el) */ - //TODO: not a fan of the style on these +//TODO: move into visualization button _render_displayApps : function( $parent ){ $parent = $parent || this.$el; var $displayAppsDiv = $parent.find( 'div.display-apps' ), @@ -302,7 +310,6 @@ /** Render the data peek. * @returns {jQuery} rendered DOM */ - //TODO: curr. pre-formatted into table on the server side - may not be ideal/flexible _render_peek : function(){ var peek = this.model.get( 'peek' ); if( !peek ){ return null; } @@ -318,74 +325,38 @@ /** Render the enclosing div of the hda body and, if expanded, the html in the body * @returns {jQuery} rendered DOM */ - //TODO: only render these on expansion (or already expanded) _render_body : function(){ - var body = $( '<div/>' ) + var $body = $( '<div/>' ) .attr( 'id', 'info-' + this.model.get( 'id' ) ) .addClass( 'historyItemBody' ) .attr( 'style', 'display: none' ); if( this.expanded ){ // only render the body html if it's being shown - this._render_body_html( body ); - //TODO: switch back when jq -> 1.9 - //body.show(); - body.css( 'display', 'block' ); + this._render_body_html( $body ); + $body.show(); } - return body; + return $body; }, /** Render the (expanded) body of an HDA, dispatching to other functions based on the HDA state * @param {jQuery} body the body element to append the html to */ - //TODO: only render these on expansion (or already expanded) - _render_body_html : function( body ){ + _render_body_html : function( $body ){ //this.log( this + '_render_body' ); - body.html( '' ); - //TODO: not a fan of this dispatch - switch( this.model.get( 'state' ) ){ - case hdaModel.HistoryDatasetAssociation.STATES.NEW : - this._render_body_new( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.NOT_VIEWABLE : - this._render_body_not_viewable( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.UPLOAD : - this._render_body_uploading( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.PAUSED: - this._render_body_paused( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.QUEUED : - this._render_body_queued( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.RUNNING : - this._render_body_running( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.ERROR : - this._render_body_error( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.DISCARDED : - this._render_body_discarded( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.SETTING_METADATA : - this._render_body_setting_metadata( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.EMPTY : - this._render_body_empty( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.FAILED_METADATA : - this._render_body_failed_metadata( body ); - break; - case hdaModel.HistoryDatasetAssociation.STATES.OK : - this._render_body_ok( body ); - break; - default: - //??: no body? - body.append( $( '<div>Error: unknown dataset state "' + this.model.get( 'state' ) + '".</div>' ) ); + $body.empty(); + + var modelState = this.model.get( 'state' ); + // cheesy get function by assumed matching name + var renderFnName = '_render_body_' + modelState, + renderFn = this[ renderFnName ]; + if( _.isFunction( renderFn ) ){ + this[ renderFnName ]( $body ); + } else { + $body.append( $( '<div>Error: unknown dataset state "' + this.model.get( 'state' ) + '".</div>' ) ); } - body.append( '<div style="clear: both"></div>' ); - this._setUpBehaviors( body ); + $body.append( '<div style="clear: both"></div>' ); + this._setUpBehaviors( $body ); }, /** Render a new dataset - this should be a transient state that's never shown @@ -400,15 +371,14 @@ /** Render inaccessible, not-owned by curr user. * @param {jQuery} parent DOM to which to append this body */ - _render_body_not_viewable : function( parent ){ - //TODO: revisit - still showing display, edit, delete (as common) - that CAN'T be right - parent.append( $( '<div>' + _l( 'You do not have permission to view dataset' ) + '</div>' ) ); + _render_body_noPermission : function( parent ){ + parent.append( $( '<div>' + _l( 'You do not have permission to view this dataset' ) + '</div>' ) ); }, /** Render an HDA still being uploaded. * @param {jQuery} parent DOM to which to append this body */ - _render_body_uploading : function( parent ){ + _render_body_upload : function( parent ){ parent.append( $( '<div>' + _l( 'Dataset is uploading' ) + '</div>' ) ); }, @@ -424,7 +394,8 @@ * @param {jQuery} parent DOM to which to append this body */ _render_body_paused: function( parent ){ - parent.append( $( '<div>' + _l( 'Job is paused. Use the history menu to resume' ) + '</div>' ) ); + parent.append( $( '<div>' + _l( 'Job is paused. ' + + 'Use the "Resume Paused Jobs" in the history menu to resume' ) + '</div>' ) ); parent.append( this._render_primaryActionButtons( this.defaultPrimaryActionButtonRenderers )); }, @@ -469,8 +440,6 @@ * @param {jQuery} parent DOM to which to append this body */ _render_body_empty : function( parent ){ - //TODO: replace i with dataset-misc-info class - //?? why are we showing the file size when we know it's zero?? parent.append( $( '<div>' + _l( 'No data' ) + ': <i>' + this.model.get( 'misc_blurb' ) + '</i></div>' ) ); parent.append( this._render_primaryActionButtons( this.defaultPrimaryActionButtonRenderers )); }, @@ -479,7 +448,6 @@ * @param {jQuery} parent DOM to which to append this body */ _render_body_failed_metadata : function( parent ){ - //TODO: the css for this box is broken (unlike the others) // add a message box about the failure at the top of the body... parent.append( $( HDABaseView.templates.failedMetadata( _.extend( this.model.toJSON(), { urls: this.urls } ) @@ -496,7 +464,6 @@ parent.append( this._render_hdaSummary() ); // return shortened form if del'd - //TODO: is this correct? maybe only on purged if( this.model.isDeletedOrPurged() ){ parent.append( this._render_primaryActionButtons([ this._render_downloadButton, @@ -517,44 +484,55 @@ parent.append( this._render_peek() ); }, - // ......................................................................... EVENTS + // ......................................................................... events /** event map */ events : { + // expand the body when the title is clicked 'click .historyItemTitle' : 'toggleBodyVisibility' }, - /** Render an HDA that's done running and where everything worked. + /** Show or hide the body/details of an HDA. + * note: if the model does not have detailed data, fetch that data before showing the body * @param {Event} event the event that triggered this (@link HDABaseView#events) * @param {Boolean} expanded if true, expand; if false, collapse * @fires body-expanded when a body has been expanded * @fires body-collapsed when a body has been collapsed */ toggleBodyVisibility : function( event, expand ){ - var hdaView = this; expand = ( expand === undefined )?( !this.body.is( ':visible' ) ):( expand ); if( expand ){ - if( this.model.inReadyState() && !this.model.hasDetails() ){ - var xhr = this.model.fetch(); - xhr.done( function( model ){ - hdaView.expandBody(); - }); - } else { - this.expandBody(); - } + this.expandBody(); } else { this.collapseBody(); } }, + /** Render and show the full, detailed body of this view including extra data and controls. + * @fires body-expanded when a body has been expanded + */ expandBody : function(){ var hdaView = this; - hdaView._render_body_html( hdaView.body ); - this.body.slideDown( hdaView.fxSpeed, function(){ - hdaView.expanded = true; - hdaView.trigger( 'body-expanded', hdaView.model.get( 'id' ) ); - }); + + function _renderBodyAndExpand(){ + hdaView._render_body_html( hdaView.body ); + hdaView.body.slideDown( hdaView.fxSpeed, function(){ + hdaView.expanded = true; + hdaView.trigger( 'body-expanded', hdaView.model.get( 'id' ) ); + }); + } + // fetch first if no details in the model + if( this.model.inReadyState() && !this.model.hasDetails() ){ + this.model.fetch().done( function( model ){ + _renderBodyAndExpand(); + }); + } else { + _renderBodyAndExpand(); + } }, + /** Hide the body/details of an HDA. + * @fires body-collapsed when a body has been collapsed + */ collapseBody : function(){ var hdaView = this; this.body.slideUp( hdaView.fxSpeed, function(){ @@ -563,7 +541,10 @@ }); }, - // ......................................................................... DELETION + // ......................................................................... removal + /** Remove this view's html from the DOM and remove all event listeners. + * @param {Function} callback an optional function called when removal is done + */ remove : function( callback ){ var hdaView = this; this.$el.fadeOut( hdaView.fxSpeed, function(){ @@ -573,7 +554,8 @@ }); }, - // ......................................................................... MISC + // ......................................................................... misc + /** String representation */ toString : function(){ var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); return 'HDABaseView(' + modelString + ')'; @@ -594,5 +576,5 @@ //============================================================================== return { - HDABaseView : HDABaseView, + HDABaseView : HDABaseView };}); diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 static/scripts/mvc/dataset/hda-edit.js --- a/static/scripts/mvc/dataset/hda-edit.js +++ b/static/scripts/mvc/dataset/hda-edit.js @@ -14,7 +14,7 @@ var HDAEditView = hdaBase.HDABaseView.extend( LoggableMixin ).extend( /** @lends HDAEditView.prototype */{ - // ......................................................................... SET UP + // ......................................................................... set up /** Set up the view, cache url templates, bind listeners. * Overrides HDABaseView.initialize to change default actions (adding re-run). * @param {Object} attributes @@ -43,7 +43,7 @@ //var hdaView = this; }, - // ......................................................................... RENDER WARNINGS + // ......................................................................... render warnings /** Render any hda warnings including: is deleted, is purged, is hidden. * Overrides _render_warnings to include links to further actions (undelete, etc.)). * @returns {Object} the templated urls @@ -71,13 +71,14 @@ return buttonDiv; }, +//TODO: move titleButtons into state renderers, remove state checks in the buttons + /** Render icon-button to edit the attributes (format, permissions, etc.) this hda. * @returns {jQuery} rendered DOM */ _render_editButton : function(){ // don't show edit while uploading, in-accessible // DO show if in error (ala previous history panel) - //TODO??: not viewable/accessible are essentially the same (not viewable set from accessible) if( ( this.model.get( 'state' ) === hdaModel.HistoryDatasetAssociation.STATES.NEW ) || ( this.model.get( 'state' ) === hdaModel.HistoryDatasetAssociation.STATES.UPLOAD ) || ( this.model.get( 'state' ) === hdaModel.HistoryDatasetAssociation.STATES.NOT_VIEWABLE ) @@ -114,7 +115,6 @@ */ _render_deleteButton : function(){ // don't show delete if... - //TODO??: not viewable/accessible are essentially the same (not viewable set from accessible) if( ( this.model.get( 'state' ) === hdaModel.HistoryDatasetAssociation.STATES.NEW ) || ( this.model.get( 'state' ) === hdaModel.HistoryDatasetAssociation.STATES.NOT_VIEWABLE ) || ( !this.model.get( 'accessible' ) ) ){ @@ -147,7 +147,7 @@ return this.deleteButton.render().$el; }, - // ......................................................................... RENDER BODY + // ......................................................................... render body /** Render the data/metadata summary (format, size, misc info, etc.). * Overrides _render_hdaSummary to include edit link in dbkey. * @see HDABaseView#_render_hdaSummary @@ -158,7 +158,6 @@ // if there's no dbkey and it's editable : pass a flag to the template to render a link to editing in the '?' if( this.model.get( 'metadata_dbkey' ) === '?' && !this.model.isDeletedOrPurged() ){ - //TODO: use HDABaseView and select/replace base on this switch _.extend( modelData, { dbkey_unknown_and_editable : true }); } return hdaBase.HDABaseView.templates.hdaSummary( modelData ); @@ -238,10 +237,8 @@ var $icon = this.visualizationsButton.render().$el; $icon.addClass( 'visualize-icon' ); // needed? - //TODO: make this more concise // map a function to each visualization in the icon's attributes // create a popupmenu from that map - /** @inner */ function create_viz_action( visualization ) { switch( visualization ){ @@ -269,7 +266,6 @@ // >1: Populate menu dict with visualization fns, make the popupmenu } else { _.each( visualizations, function( visualization ) { - //TODO: move to utils var titleCaseVisualization = visualization.charAt( 0 ).toUpperCase() + visualization.slice( 1 ); popup_menu_dict[ _l( titleCaseVisualization ) ] = create_viz_action( visualization ); }); @@ -334,9 +330,7 @@ /** Render icon-button to load and display tagging html. * @returns {jQuery} rendered DOM */ - //TODO: these should be a sub-MV _render_tagButton : function(){ - //TODO: check for User if( !this.hasUser || !this.urls.tags.get ){ this.tagButton = null; return null; @@ -354,9 +348,7 @@ /** Render icon-button to load and display annotation html. * @returns {jQuery} rendered DOM */ - //TODO: these should be a sub-MV _render_annotateButton : function(){ - //TODO: check for User if( !this.hasUser || !this.urls.annotation.get ){ this.annotateButton = null; return null; @@ -370,33 +362,6 @@ return this.annotateButton.render().$el; }, - // ......................................................................... other elements - /** Render area to display tags. - * @returns {jQuery} rendered DOM - */ -//TODO: into sub-MV -//TODO: check for User - _render_tagArea : function(){ - if( !this.urls.tags.set ){ return null; } - //TODO: move to mvc/tags.js - return $( HDAEditView.templates.tagArea( - _.extend( this.model.toJSON(), { urls: this.urls } ) - ).trim() ); - }, - - /** Render area to display annotation. - * @returns {jQuery} rendered DOM - */ -//TODO: into sub-MV -//TODO: check for User - _render_annotationArea : function(){ - if( !this.urls.annotation.get ){ return null; } - //TODO: move to mvc/annotations.js - return $( HDAEditView.templates.annotationArea( - _.extend( this.model.toJSON(), { urls: this.urls } ) - ).trim() ); - }, - // ......................................................................... state body renderers /** Render an HDA whose job has failed. * Overrides _render_body_error to prepend error report button to primary actions strip. @@ -415,7 +380,6 @@ * @see HDABaseView#_render_body_ok */ _render_body_ok : function( parent ){ - //TODO: should call super somehow and insert the needed... // most common state renderer and the most complicated parent.append( this._render_hdaSummary() ); @@ -451,7 +415,7 @@ parent.append( this._render_peek() ); }, - // ......................................................................... EVENTS + // ......................................................................... events /** event map */ events : { 'click .historyItemTitle' : 'toggleBodyVisibility', @@ -463,16 +427,28 @@ 'click a.icon-button.annotate' : 'loadAndDisplayAnnotation' }, - // ......................................................................... STATE CHANGES / MANIPULATION + /** listener for item purge */ confirmPurge : function _confirmPurge( ev ){ - //TODO: confirm dialog +//TODO: confirm dialog this.model.purge({ url: this.urls.purge }); return false; }, + // ......................................................................... tags + /** Render area to display tags. + * @returns {jQuery} rendered DOM + */ +//TODO: into sub-MV + _render_tagArea : function(){ + if( !this.hasUser || !this.urls.tags.set ){ return null; } + return $( HDAEditView.templates.tagArea( + _.extend( this.model.toJSON(), { urls: this.urls } ) + ).trim() ); + }, + /** Find the tag area and, if initial: load the html (via ajax) for displaying them; otherwise, unhide/hide */ - //TODO: into sub-MV +//TODO: into sub-MV loadAndDisplayTags : function( event ){ //BUG: broken with latest //TODO: this is a drop in from history.mako - should use MV as well @@ -508,6 +484,18 @@ } return false; }, + + // ......................................................................... annotations + /** Render area to display annotation. + * @returns {jQuery} rendered DOM + */ +//TODO: into sub-MV + _render_annotationArea : function(){ + if( !this.hasUser || !this.urls.annotation.get ){ return null; } + return $( HDAEditView.templates.annotationArea( + _.extend( this.model.toJSON(), { urls: this.urls } ) + ).trim() ); + }, /** Find the annotation area and, if initial: load the html (via ajax) for displaying them; otherwise, unhide/hide */ @@ -555,7 +543,7 @@ return false; }, - // ......................................................................... UTILTIY + // ......................................................................... misc /** string rep */ toString : function(){ var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); @@ -678,5 +666,5 @@ //============================================================================== return { - HDAEditView : HDAEditView, + HDAEditView : HDAEditView };}); diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 static/scripts/mvc/dataset/hda-model.js --- a/static/scripts/mvc/dataset/hda-model.js +++ b/static/scripts/mvc/dataset/hda-model.js @@ -53,41 +53,42 @@ misc_info : '' }, - /** fetch location of this history in the api */ + /** fetch location of this HDA's history in the api */ urlRoot: 'api/histories/', + /** full url spec. for this HDA */ url : function(){ return this.urlRoot + this.get( 'history_id' ) + '/contents/' + this.get( 'id' ); }, + /** controller urls assoc. with this HDA */ urls : function(){ var id = this.get( 'id' ); if( !id ){ return {}; } var urls = { - 'delete' : '/datasets/' + id + '/delete_async', - 'purge' : '/datasets/' + id + '/purge_async', - 'unhide' : '/datasets/' + id + '/unhide', - 'undelete' : '/datasets/' + id + '/undelete', + 'purge' : galaxy_config.root + 'datasets/' + id + '/purge_async', - 'display' : '/datasets/' + id + '/display/?preview=True', - 'download' : '/datasets/' + id + '/display?to_ext=' + this.get( 'file_ext' ), - 'edit' : '/datasets/' + id + '/edit', - 'report_error': '/dataset/errors?id=' + id, - 'rerun' : '/tool_runner/rerun?id=' + id, - 'show_params': '/datasets/' + id + '/show_params', - 'visualization': '/visualization', + 'display' : galaxy_config.root + 'datasets/' + id + '/display/?preview=True', + 'download' : galaxy_config.root + 'datasets/' + id + '/display?to_ext=' + this.get( 'file_ext' ), + 'edit' : galaxy_config.root + 'datasets/' + id + '/edit', + 'report_error' : galaxy_config.root + 'dataset/errors?id=' + id, + 'rerun' : galaxy_config.root + 'tool_runner/rerun?id=' + id, + 'show_params' : galaxy_config.root + 'datasets/' + id + '/show_params', + 'visualization' : galaxy_config.root + 'visualization', - 'annotation': { 'get': '/dataset/get_annotation_async?id=' + id, - 'set': '/dataset/annotate_async?id=' + id }, - 'tags' : { 'get': '/tag/get_tagging_elt_async?item_id=' + id + '&item_class=HistoryDatasetAssociation', - 'set': '/tag/retag?item_id=' + id + '&item_class=HistoryDatasetAssociation' } + 'annotation': { 'get': galaxy_config.root + 'dataset/get_annotation_async?id=' + id, + 'set': galaxy_config.root + 'dataset/annotate_async?id=' + id }, + 'tags' : { 'get': galaxy_config.root + 'tag/get_tagging_elt_async?item_id=' + + id + '&item_class=HistoryDatasetAssociation', + 'set': galaxy_config.root + 'tag/retag?item_id=' + + id + '&item_class=HistoryDatasetAssociation' } }; - //'meta_download': '/dataset/get_metadata_file?hda_id=%3C%25%3D+id+%25%3E&metadata_name=%3C%25%3D+file_type+%25%3E', + // download links to assoc. metadata files (bam indeces, etc.) var meta_files = this.get( 'meta_files' ); if( meta_files ){ urls.meta_download = _.map( meta_files, function( meta_file ){ return { - //url : _.template( urlTemplate, { id: modelJson.id, file_type: meta_file.file_type }), - url : '/dataset/get_metadata_file?hda_id=' + id + '&metadata_name=' + meta_file.file_type, + url : galaxy_config.root + 'dataset/get_metadata_file?hda_id=' + + id + '&metadata_name=' + meta_file.file_type, file_type : meta_file.file_type }; }); @@ -110,6 +111,9 @@ this._setUpListeners(); }, + /** set up any event listeners + * event: state:ready fired when this HDA moves into a ready state + */ _setUpListeners : function(){ // if the state has changed and the new state is a ready state, fire an event this.on( 'change:state', function( currModel, newState ){ @@ -121,8 +125,7 @@ }, // ........................................................................ common queries - /** Is this hda deleted or purged? - */ + /** Is this hda deleted or purged? */ isDeletedOrPurged : function(){ return ( this.get( 'deleted' ) || this.get( 'purged' ) ); }, @@ -132,7 +135,6 @@ * @param {Boolean} show_deleted are we showing deleted hdas? * @param {Boolean} show_hidden are we showing hidden hdas? */ - //TODO: too many 'visible's isVisible : function( show_deleted, show_hidden ){ var isVisible = true; if( ( !show_deleted ) @@ -146,6 +148,7 @@ return isVisible; }, + /** the more common alias of visible */ hidden : function(){ return !this.get( 'visible' ); }, @@ -158,33 +161,37 @@ return ( this.isDeletedOrPurged() || ready ); }, + /** Does this model already contain detailed data (as opposed to just summary level data)? */ hasDetails : function(){ //?? this may not be reliable return _.has( this.attributes, 'genome_build' ); }, - /** Convenience function to match hda.has_data. - */ + /** Convenience function to match hda.has_data. */ hasData : function(){ - //TODO:?? is this equivalent to all possible hda.has_data calls? return ( this.get( 'file_size' ) > 0 ); }, // ........................................................................ ajax + /** save this HDA, _Mark_ing it as deleted (just a flag) */ 'delete' : function _delete( options ){ return this.save( { deleted: true }, options ); }, + /** save this HDA, _Mark_ing it as undeleted */ undelete : function _undelete( options ){ return this.save( { deleted: false }, options ); }, + /** save this HDA as not visible */ hide : function _hide( options ){ return this.save( { visible: false }, options ); }, + /** save this HDA as visible */ unhide : function _uhide( options ){ return this.save( { visible: true }, options ); }, + /** purge this HDA and remove the underlying dataset file from the server's fs */ purge : function _purge( options ){ //TODO: ideally this would be a DELETE call to the api // using purge async for now @@ -210,10 +217,15 @@ }, // ........................................................................ sorting/filtering + /** what attributes of an HDA will be used in a search */ searchKeys : [ 'name', 'file_ext', 'genome_build', 'misc_blurb', 'misc_info', 'annotation', 'tags' ], + /** search this HDA for the string searchFor + * @param {String} searchFor look for this string in all attributes listed in searchKeys (above) using indexOf + * @returns {Array} an array of attribute keys where searchFor was found + */ search : function( searchFor ){ var model = this; searchFor = searchFor.toLowerCase(); @@ -223,13 +235,16 @@ }); }, + /** alias of search, but returns a boolean + * @param {String} matchesWhat look for this string in all attributes listed in searchKeys (above) using indexOf + * @returns {Boolean} was matchesWhat found in any attributes + */ matches : function( matchesWhat ){ return !!this.search( matchesWhat ).length; }, // ........................................................................ misc - /** String representation - */ + /** String representation */ toString : function(){ var nameAndId = this.get( 'id' ) || ''; if( this.get( 'name' ) ){ @@ -274,6 +289,7 @@ ERROR : 'error' }; +/** states that are in a final state (the underlying job is complete) */ HistoryDatasetAssociation.READY_STATES = [ HistoryDatasetAssociation.STATES.NEW, HistoryDatasetAssociation.STATES.OK, @@ -285,6 +301,7 @@ HistoryDatasetAssociation.STATES.ERROR ]; +/** states that will change (the underlying job is not finished) */ HistoryDatasetAssociation.NOT_READY_STATES = [ HistoryDatasetAssociation.STATES.UPLOAD, HistoryDatasetAssociation.STATES.QUEUED, @@ -306,7 +323,10 @@ ///** logger used to record this.log messages, commonly set to console */ //// comment this out to suppress log output //logger : console, - urlRoot : '/api/histories', + + /** root api url */ + urlRoot : galaxy_config.root + 'api/histories', + /** complete api url */ url : function(){ return this.urlRoot + '/' + this.historyId + '/contents'; }, @@ -331,6 +351,9 @@ return this.map( function( hda ){ return hda.id; }); }, + /** Get hdas that are not ready + * @returns array of HDAs + */ notReady : function(){ return this.filter( function( hda ){ return !hda.inReadyState(); @@ -370,11 +393,13 @@ }, // ........................................................................ ajax + /** fetch detailed model data for all HDAs in this collection */ fetchAllDetails : function(){ return this.fetch({ data : { details : 'all' } }); }, // ........................................................................ sorting/filtering + /** return a new collection of HDAs whose attributes contain the substring matchesWhat */ matches : function( matchesWhat ){ return this.filter( function( hda ){ return hda.matches( matchesWhat ); diff -r f82bbdc5c22b24d329a6710a20ce4c049f124064 -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 static/scripts/mvc/history/history-model.js --- a/static/scripts/mvc/history/history-model.js +++ b/static/scripts/mvc/history/history-model.js @@ -13,7 +13,6 @@ */ var History = Backbone.Model.extend( LoggableMixin ).extend( /** @lends History.prototype */{ - //TODO: bind change events from items and collection to this (itemLengths, states) ///** logger used to record this.log messages, commonly set to console */ //// comment this out to suppress log output @@ -31,29 +30,33 @@ }, // ........................................................................ urls - urlRoot: 'api/histories', + urlRoot: galaxy_config.root + 'api/histories', + /** url for changing the name of the history */ renameUrl : function(){ //TODO: just use this.save() var id = this.get( 'id' ); if( !id ){ return undefined; } - return '/history/rename_async?id=' + this.get( 'id' ); + return galaxy_config.root + 'history/rename_async?id=' + this.get( 'id' ); }, + /** url for changing the annotation of the history */ annotateUrl : function(){ var id = this.get( 'id' ); if( !id ){ return undefined; } - return '/history/annotate_async?id=' + this.get( 'id' ); + return galaxy_config.root + 'history/annotate_async?id=' + this.get( 'id' ); }, + /** url for changing the tags of the history */ tagUrl : function(){ var id = this.get( 'id' ); if( !id ){ return undefined; } - return '/tag/get_tagging_elt_async?item_id=' + this.get( 'id' ) + '&item_class=History'; + return galaxy_config.root + 'tag/get_tagging_elt_async?item_id=' + this.get( 'id' ) + '&item_class=History'; }, // ........................................................................ set up/tear down - /** Set up the hdas collection + /** Set up the model * @param {Object} historyJSON model data for this History * @param {Object[]} hdaJSON array of model data for this History's HDAs + * @param {Object} options any extra settings including logger * @see BaseModel#initialize */ initialize : function( historyJSON, hdaJSON, options ){ @@ -69,17 +72,24 @@ } this._setUpListeners(); - this._installAjaxErrorHandler( this.ajaxErrorHandler ); + /** cached timeout id for the HDA updater */ + this.updateTimeoutId = null; // set up update timeout if needed this.checkForUpdates(); }, + /** set up any event listeners for this history including those to the contained HDAs + * events: error:hdas if an error occurred with the HDA collection + */ _setUpListeners : function(){ + this.on( 'error', function( model, xhr, options, msg, details ){ + this.errorHandler( model, xhr, options, msg, details ); + }); + // hda collection listening if( this.hdas ){ this.listenTo( this.hdas, 'error', function(){ - //this.ajaxErrorHandler.apply( this, arguments ); this.trigger.apply( this, [ 'error:hdas' ].concat( jQuery.makeArray( arguments ) ) ); }); } @@ -105,34 +115,29 @@ // } //}, + /** event listener for errors. Generally errors are handled outside this model */ + errorHandler : function( model, xhr, options, msg, details ){ + // clear update timeout on model err + this.clearUpdateTimeout(); + }, + // ........................................................................ common queries + /** is this model already associated with a user? */ hasUser : function(){ var user = this.get( 'user' ); return !!( user && user.id ); }, // ........................................................................ ajax - // override to add ajax error event - //TODO: into mixin/base - _installAjaxErrorHandler : function( handler ){ - this.sync = function _sync( method, model, options ){ - return Backbone.Model.prototype.sync.call( model, method, model, options ) - .fail( function( xhr, status, message ){ - handler.call( model, model, xhr, options, method ); - }); - }; - }, - - ajaxErrorHandler : function( model, xhr, options, method ){ - }, - - // get the history's state from it's cummulative ds states, delay + update if needed - // events: ready + /** does the HDA collection indicate they're still running and need to be updated later? delay + update if needed + * @param {Function} onReadyCallback function to run when all HDAs are in the ready state + * events: ready + */ checkForUpdates : function( onReadyCallback ){ //console.info( 'checkForUpdates' ) // get overall History state from collection, run updater if History has running/queued hdas - // boiling it down on the client to running/not + // boiling it down on the client to running/not if( this.hdas.running().length ){ this.setUpdateTimeout(); @@ -145,8 +150,8 @@ return this; }, + /** create a timeout (after UPDATE_DELAY or delay ms) to refetch the HDA collection. Clear any prev. timeout */ setUpdateTimeout : function( delay ){ - //TODO: callbacks? delay = delay || History.UPDATE_DELAY; var history = this; @@ -158,6 +163,7 @@ return this.updateTimeoutId; }, + /** clear the timeout and the cached timeout id */ clearUpdateTimeout : function(){ if( this.updateTimeoutId ){ clearTimeout( this.updateTimeoutId ); @@ -165,25 +171,26 @@ } }, - // update this history, find any hda's running/queued, update ONLY those that have changed states, - // set up to run this again in some interval of time - // events: ready + /* update the HDA collection getting full detailed model data for any hda whose id is in detailIds + * set up to run this again in some interval of time + * @param {String[]} detailIds list of HDA ids to get detailed model data for + * @param {Object} options std. backbone fetch options map + */ refresh : function( detailIds, options ){ //console.info( 'refresh:', detailIds, this.hdas ); detailIds = detailIds || []; options = options || {}; var history = this; + // add detailIds to options as CSV string options.data = options.data || {}; if( detailIds.length ){ options.data.details = detailIds.join( ',' ); } var xhr = this.hdas.fetch( options ); xhr.done( function( hdaModels ){ - //this.trigger( 'hdas-refreshed', this, hdaModels ); - history.checkForUpdates(function(){ - // fetch the history after an update in order to recalc history size - //TODO: move to event? + history.checkForUpdates( function(){ + // fetch the history inside onReadyCallback in order to recalc history size this.fetch(); }); }); @@ -214,7 +221,7 @@ function getHistory( id ){ // get the history data //return jQuery.ajax( '/generate_json_error' ); - return jQuery.ajax( '/api/histories/' + historyId ); + return jQuery.ajax( galaxy_config.root + 'api/histories/' + historyId ); } function countHdasFromHistory( historyData ){ // get the number of hdas accrd. to the history @@ -232,7 +239,7 @@ hdaDetailIds = hdaDetailIds( historyData ); } var data = ( hdaDetailIds.length )?( { details : hdaDetailIds.join( ',' ) } ):( {} ); - return jQuery.ajax( '/api/histories/' + historyData.id + '/contents', { data: data }); + return jQuery.ajax( galaxy_config.root + 'api/histories/' + historyData.id + '/contents', { data: data }); //return jQuery.ajax( '/generate_json_error' ); } @@ -283,7 +290,7 @@ var HistoryCollection = Backbone.Collection.extend( LoggableMixin ).extend( /** @lends HistoryCollection.prototype */{ model : History, - urlRoot : 'api/histories' + urlRoot : galaxy_config.root + 'api/histories' ///** logger used to record this.log messages, commonly set to console */ //// comment this out to suppress log output This diff is so big that we needed to truncate the remainder. https://bitbucket.org/galaxy/galaxy-central/commits/35a847f0f33d/ Changeset: 35a847f0f33d Branch: search User: Kyle Ellrott Date: 2013-10-25 00:04:33 Summary: Adding missing import Affected #: 1 file diff -r 9da06a2f3db4bf535cf179af6ba66b98741e7ea1 -r 35a847f0f33d2944b8033aefeb8d5ef0c734c8c6 lib/galaxy/model/search.py --- a/lib/galaxy/model/search.py +++ b/lib/galaxy/model/search.py @@ -35,7 +35,7 @@ History, Library, LibraryFolder, LibraryDataset,StoredWorkflowTagAssociation, StoredWorkflow, HistoryTagAssociation,HistoryDatasetAssociationTagAssociation, ExtendedMetadata, ExtendedMetadataIndex, HistoryAnnotationAssociation, Job, JobParameter, -JobToInputDatasetAssociation, JobToOutputDatasetAssociation, ToolVersion) +JobToInputLibraryDatasetAssociation, JobToInputDatasetAssociation, JobToOutputDatasetAssociation, ToolVersion ) from galaxy.util.json import to_json_string from sqlalchemy import and_ https://bitbucket.org/galaxy/galaxy-central/commits/97cb7306dc34/ Changeset: 97cb7306dc34 User: dannon Date: 2013-10-25 00:10:01 Summary: Merged in kellrott/galaxy-central/search (pull request #241) Fixing missing import in search.py Affected #: 1 file diff -r bf0057b13f58f5c5438f1a6477b27bfceb14a2ce -r 97cb7306dc345ce5e81861e421c5277701161802 lib/galaxy/model/search.py --- a/lib/galaxy/model/search.py +++ b/lib/galaxy/model/search.py @@ -35,7 +35,7 @@ History, Library, LibraryFolder, LibraryDataset,StoredWorkflowTagAssociation, StoredWorkflow, HistoryTagAssociation,HistoryDatasetAssociationTagAssociation, ExtendedMetadata, ExtendedMetadataIndex, HistoryAnnotationAssociation, Job, JobParameter, -JobToInputDatasetAssociation, JobToOutputDatasetAssociation, ToolVersion) +JobToInputLibraryDatasetAssociation, JobToInputDatasetAssociation, JobToOutputDatasetAssociation, ToolVersion ) from galaxy.util.json import to_json_string from sqlalchemy import and_ Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.