commit/galaxy-central: greg: Enhance the update_available feature for installed tool shed repositories to enable any type of status from the tool shed for each repository. The Installed tool shed repositories grid now displays whether there are revision updates available, revision upgrades available, whether the revision is the latest installable revision, and whether the repository has been deprecated in the tool shed. This change set includes a 0016 db table migration script for which it
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/8f6f926f912e/ Changeset: 8f6f926f912e User: greg Date: 2013-08-30 17:47:27 Summary: Enhance the update_available feature for installed tool shed repositories to enable any type of status from the tool shed for each repository. The Installed tool shed repositories grid now displays whether there are revision updates available, revision upgrades available, whether the revision is the latest installable revision, and whether the repository has been deprecated in the tool shed. This change set includes a 0016 db table migration script for which it has been discovered that it is no longer possible to drop a column from a database table using a migration script if the database is sqlite. Affected #: 12 files diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3306,11 +3306,12 @@ class APIKeys( object ): pass + class ToolShedRepository( object ): dict_collection_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes', - 'update_available', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' ) + 'tool_shed_status', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' ) dict_element_visible_keys = ( 'id', 'tool_shed', 'name', 'owner', 'installed_changeset_revision', 'changeset_revision', 'ctx_rev', 'includes_datatypes', - 'update_available', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' ) + 'tool_shed_status', 'deleted', 'uninstalled', 'dist_to_shed', 'status', 'error_message' ) installation_status = Bunch( NEW='New', CLONING='Cloning', SETTING_TOOL_VERSIONS='Setting tool versions', @@ -3326,8 +3327,9 @@ WARNING = 'queued', ERROR = 'error', UNINSTALLED = 'deleted_new' ) + def __init__( self, id=None, create_time=None, tool_shed=None, name=None, description=None, owner=None, installed_changeset_revision=None, - changeset_revision=None, ctx_rev=None, metadata=None, includes_datatypes=False, update_available=False, deleted=False, + changeset_revision=None, ctx_rev=None, metadata=None, includes_datatypes=False, tool_shed_status=None, deleted=False, uninstalled=False, dist_to_shed=False, status=None, error_message=None ): self.id = id self.create_time = create_time @@ -3340,38 +3342,72 @@ self.ctx_rev = ctx_rev self.metadata = metadata self.includes_datatypes = includes_datatypes - self.update_available = update_available + self.tool_shed_status = tool_shed_status self.deleted = deleted self.uninstalled = uninstalled self.dist_to_shed = dist_to_shed self.status = status self.error_message = error_message + def as_dict( self, value_mapper=None ): return self.dictify( view='element', value_mapper=value_mapper ) - def repo_files_directory( self, app ): - repo_path = self.repo_path( app ) - if repo_path: - return os.path.join( repo_path, self.name ) - return None - def repo_path( self, app ): - tool_shed_url = self.tool_shed - if tool_shed_url.find( ':' ) > 0: - # Eliminate the port, if any, since it will result in an invalid directory name. - tool_shed_url = tool_shed_url.split( ':' )[ 0 ] - tool_shed = tool_shed_url.rstrip( '/' ) - for index, shed_tool_conf_dict in enumerate( app.toolbox.shed_tool_confs ): - tool_path = shed_tool_conf_dict[ 'tool_path' ] - relative_path = os.path.join( tool_path, tool_shed, 'repos', self.owner, self.name, self.installed_changeset_revision ) - if os.path.exists( relative_path ): - return relative_path - return None + @property - def tool_shed_path_name( self ): - tool_shed_url = self.tool_shed - if tool_shed_url.find( ':' ) > 0: - # Eliminate the port, if any, since it will result in an invalid directory name. - tool_shed_url = tool_shed_url.split( ':' )[ 0 ] - return tool_shed_url.rstrip( '/' ) + def can_install( self ): + return self.status == self.installation_status.NEW + + @property + def can_reset_metadata( self ): + return self.status == self.installation_status.INSTALLED + + @property + def can_uninstall( self ): + return self.status != self.installation_status.UNINSTALLED + + @property + def can_deactivate( self ): + return self.status not in [ self.installation_status.DEACTIVATED, self.installation_status.UNINSTALLED ] + + @property + def can_reinstall_or_activate( self ): + return self.deleted + + def dictify( self, view='collection', value_mapper=None ): + if value_mapper is None: + value_mapper = {} + rval = {} + try: + visible_keys = self.__getattribute__( 'dict_' + view + '_visible_keys' ) + except AttributeError: + raise Exception( 'Unknown API view: %s' % view ) + for key in visible_keys: + try: + rval[ key ] = self.__getattribute__( key ) + if key in value_mapper: + rval[ key ] = value_mapper.get( key, rval[ key ] ) + except AttributeError: + rval[ key ] = None + return rval + + def get_shed_config_filename( self ): + shed_config_filename = None + if self.metadata: + shed_config_filename = self.metadata.get( 'shed_config_filename', shed_config_filename ) + return shed_config_filename + + def get_shed_config_dict( self, app, default=None ): + """ + Return the in-memory version of the shed_tool_conf file, which is stored in the config_elems entry + in the shed_tool_conf_dict. + """ + if not self.shed_config_filename: + self.guess_shed_config( app, default=default ) + if self.shed_config_filename: + for shed_tool_conf_dict in app.toolbox.shed_tool_confs: + if self.shed_config_filename == shed_tool_conf_dict[ 'config_filename' ]: + return shed_tool_conf_dict + return default + def get_tool_relative_path( self, app ): shed_conf_dict = self.get_shed_config_dict( app ) tool_path = None @@ -3380,14 +3416,7 @@ tool_path = shed_conf_dict[ 'tool_path' ] relative_path = os.path.join( self.tool_shed_path_name, 'repos', self.owner, self.name, self.installed_changeset_revision ) return tool_path, relative_path - def get_shed_config_filename( self ): - shed_config_filename = None - if self.metadata: - shed_config_filename = self.metadata.get( 'shed_config_filename', shed_config_filename ) - return shed_config_filename - def set_shed_config_filename( self, value ): - self.metadata[ 'shed_config_filename' ] = value - shed_config_filename = property( get_shed_config_filename, set_shed_config_filename ) + def guess_shed_config( self, app, default=None ): tool_ids = [] metadata = self.metadata or {} @@ -3423,59 +3452,163 @@ self.shed_config_filename = shed_tool_conf_dict[ 'config_filename' ] return shed_tool_conf_dict return default - def get_shed_config_dict( self, app, default=None ): - """ - Return the in-memory version of the shed_tool_conf file, which is stored in the config_elems entry - in the shed_tool_conf_dict. - """ - if not self.shed_config_filename: - self.guess_shed_config( app, default=default ) - if self.shed_config_filename: - for shed_tool_conf_dict in app.toolbox.shed_tool_confs: - if self.shed_config_filename == shed_tool_conf_dict[ 'config_filename' ]: - return shed_tool_conf_dict - return default - def dictify( self, view='collection', value_mapper=None ): - if value_mapper is None: - value_mapper = {} - rval = {} - try: - visible_keys = self.__getattribute__( 'dict_' + view + '_visible_keys' ) - except AttributeError: - raise Exception( 'Unknown API view: %s' % view ) - for key in visible_keys: - try: - rval[ key ] = self.__getattribute__( key ) - if key in value_mapper: - rval[ key ] = value_mapper.get( key, rval[ key ] ) - except AttributeError: - rval[ key ] = None - return rval - @property - def can_install( self ): - return self.status == self.installation_status.NEW - @property - def can_reset_metadata( self ): - return self.status == self.installation_status.INSTALLED - @property - def can_uninstall( self ): - return self.status != self.installation_status.UNINSTALLED - @property - def can_deactivate( self ): - return self.status not in [ self.installation_status.DEACTIVATED, self.installation_status.UNINSTALLED ] - @property - def can_reinstall_or_activate( self ): - return self.deleted + @property def has_readme_files( self ): if self.metadata: return 'readme_files' in self.metadata return False + @property def has_repository_dependencies( self ): if self.metadata: return 'repository_dependencies' in self.metadata return False + + @property + def in_error_state( self ): + return self.status == self.installation_status.ERROR + + @property + def includes_data_managers( self ): + if self.metadata: + return bool( len( self.metadata.get( 'data_manager', {} ).get( 'data_managers', {} ) ) ) + return False + + @property + def includes_tools( self ): + if self.metadata: + return 'tools' in self.metadata + return False + + @property + def includes_tools_for_display_in_tool_panel( self ): + if self.includes_tools: + tool_dicts = self.metadata[ 'tools' ] + for tool_dict in tool_dicts: + if tool_dict.get( 'add_to_tool_panel', True ): + return True + return False + + @property + def includes_tool_dependencies( self ): + if self.metadata: + return 'tool_dependencies' in self.metadata + return False + + @property + def includes_workflows( self ): + if self.metadata: + return 'workflows' in self.metadata + return False + + @property + def installed_repository_dependencies( self ): + """Return the repository's repository dependencies that are currently installed.""" + installed_required_repositories = [] + for required_repository in self.repository_dependencies: + if required_repository.status == self.installation_status.INSTALLED: + installed_required_repositories.append( required_repository ) + return installed_required_repositories + + @property + def installed_tool_dependencies( self ): + """Return the repository's tool dependencies that are currently installed.""" + installed_dependencies = [] + for tool_dependency in self.tool_dependencies: + if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED, ToolDependency.installation_status.ERROR ]: + installed_dependencies.append( tool_dependency ) + return installed_dependencies + + @property + def is_deprecated_in_tool_shed( self ): + if self.tool_shed_status: + return galaxy.util.asbool( self.tool_shed_status.get( 'repository_deprecated', False ) ) + return False + + @property + def is_latest_installable_revision( self ): + if self.tool_shed_status: + return galaxy.util.asbool( self.tool_shed_status.get( 'latest_installable_revision', False ) ) + return False + + @property + def missing_repository_dependencies( self ): + """Return the repository's repository dependencies that are not currently installed, and may not ever have been installed.""" + missing_required_repositories = [] + for required_repository in self.repository_dependencies: + if required_repository.status not in [ self.installation_status.INSTALLED ]: + missing_required_repositories.append( required_repository ) + return missing_required_repositories + + @property + def missing_tool_dependencies( self ): + """Return the repository's tool dependencies that are not currently installed, and may not ever have been installed.""" + missing_dependencies = [] + for tool_dependency in self.tool_dependencies: + if tool_dependency.status not in [ ToolDependency.installation_status.INSTALLED ]: + missing_dependencies.append( tool_dependency ) + return missing_dependencies + + def repo_files_directory( self, app ): + repo_path = self.repo_path( app ) + if repo_path: + return os.path.join( repo_path, self.name ) + return None + + def repo_path( self, app ): + tool_shed_url = self.tool_shed + if tool_shed_url.find( ':' ) > 0: + # Eliminate the port, if any, since it will result in an invalid directory name. + tool_shed_url = tool_shed_url.split( ':' )[ 0 ] + tool_shed = tool_shed_url.rstrip( '/' ) + for index, shed_tool_conf_dict in enumerate( app.toolbox.shed_tool_confs ): + tool_path = shed_tool_conf_dict[ 'tool_path' ] + relative_path = os.path.join( tool_path, tool_shed, 'repos', self.owner, self.name, self.installed_changeset_revision ) + if os.path.exists( relative_path ): + return relative_path + return None + + @property + def repository_dependencies( self ): + required_repositories = [] + for rrda in self.required_repositories: + repository_dependency = rrda.repository_dependency + required_repository = repository_dependency.repository + if required_repository: + required_repositories.append( required_repository ) + return required_repositories + + @property + def repository_dependencies_being_installed( self ): + """Return the repository's repository dependencies that are currently being installed.""" + required_repositories_being_installed = [] + for required_repository in self.repository_dependencies: + if required_repository.status == self.installation_status.INSTALLING: + required_repositories_being_installed.append( required_repository ) + return required_repositories_being_installed + + @property + def repository_dependencies_missing_or_being_installed( self ): + """Return the repository's repository dependencies that are either missing or currently being installed.""" + required_repositories_missing_or_being_installed = [] + for required_repository in self.repository_dependencies: + if required_repository.status in [ self.installation_status.ERROR, + self.installation_status.INSTALLING, + self.installation_status.NEVER_INSTALLED, + self.installation_status.UNINSTALLED ]: + required_repositories_missing_or_being_installed.append( required_repository ) + return required_repositories_missing_or_being_installed + + @property + def repository_dependencies_with_installation_errors( self ): + """Return the repository's repository dependencies that have installation errors.""" + required_repositories_with_installation_errors = [] + for required_repository in self.repository_dependencies: + if required_repository.status == self.installation_status.ERROR: + required_repositories_with_installation_errors.append( required_repository ) + return required_repositories_with_installation_errors + @property def requires_prior_installation_of( self ): """ @@ -3501,113 +3634,22 @@ if prior_installation_required: required_rd_tups_that_must_be_installed.append( ( tool_shed, name, owner, changeset_revision, prior_installation_required ) ) return required_rd_tups_that_must_be_installed + @property - def includes_data_managers( self ): - if self.metadata: - return bool( len( self.metadata.get( 'data_manager', {} ).get( 'data_managers', {} ) ) ) + def revision_update_available( self ): + # This method should be named update_available, but since it is no longer possible to drop a table column using migration scripts + # with the sqlite database (see ~/galaxy/model/migrate/versions/0016_drop_update_available_col_add_tool_shed_status_col.py), we + # have to name it in such a way that it will not conflict with the eliminated tool_shed_repository.update_available column (which + # cannot be eliminated if using the sqlite database). + if self.tool_shed_status: + return galaxy.util.asbool( self.tool_shed_status.get( 'revision_update', False ) ) return False - @property - def includes_tools( self ): - if self.metadata: - return 'tools' in self.metadata - return False - @property - def includes_tools_for_display_in_tool_panel( self ): - if self.includes_tools: - tool_dicts = self.metadata[ 'tools' ] - for tool_dict in tool_dicts: - if tool_dict.get( 'add_to_tool_panel', True ): - return True - return False - @property - def includes_tool_dependencies( self ): - if self.metadata: - return 'tool_dependencies' in self.metadata - return False - @property - def includes_workflows( self ): - if self.metadata: - return 'workflows' in self.metadata - return False - @property - def in_error_state( self ): - return self.status == self.installation_status.ERROR - @property - def repository_dependencies( self ): - required_repositories = [] - for rrda in self.required_repositories: - repository_dependency = rrda.repository_dependency - required_repository = repository_dependency.repository - if required_repository: - required_repositories.append( required_repository ) - return required_repositories - @property - def installed_repository_dependencies( self ): - """Return the repository's repository dependencies that are currently installed.""" - installed_required_repositories = [] - for required_repository in self.repository_dependencies: - if required_repository.status == self.installation_status.INSTALLED: - installed_required_repositories.append( required_repository ) - return installed_required_repositories - @property - def missing_repository_dependencies( self ): - """Return the repository's repository dependencies that are not currently installed, and may not ever have been installed.""" - missing_required_repositories = [] - for required_repository in self.repository_dependencies: - if required_repository.status not in [ self.installation_status.INSTALLED ]: - missing_required_repositories.append( required_repository ) - return missing_required_repositories - @property - def repository_dependencies_being_installed( self ): - """Return the repository's repository dependencies that are currently being installed.""" - required_repositories_being_installed = [] - for required_repository in self.repository_dependencies: - if required_repository.status == self.installation_status.INSTALLING: - required_repositories_being_installed.append( required_repository ) - return required_repositories_being_installed - @property - def repository_dependencies_missing_or_being_installed( self ): - """Return the repository's repository dependencies that are either missing or currently being installed.""" - required_repositories_missing_or_being_installed = [] - for required_repository in self.repository_dependencies: - if required_repository.status in [ self.installation_status.ERROR, - self.installation_status.INSTALLING, - self.installation_status.NEVER_INSTALLED, - self.installation_status.UNINSTALLED ]: - required_repositories_missing_or_being_installed.append( required_repository ) - return required_repositories_missing_or_being_installed - @property - def repository_dependencies_with_installation_errors( self ): - """Return the repository's repository dependencies that have installation errors.""" - required_repositories_with_installation_errors = [] - for required_repository in self.repository_dependencies: - if required_repository.status == self.installation_status.ERROR: - required_repositories_with_installation_errors.append( required_repository ) - return required_repositories_with_installation_errors - @property - def uninstalled_repository_dependencies( self ): - """Return the repository's repository dependencies that have been uninstalled.""" - uninstalled_required_repositories = [] - for required_repository in self.repository_dependencies: - if required_repository.status == self.installation_status.UNINSTALLED: - uninstalled_required_repositories.append( required_repository ) - return uninstalled_required_repositories - @property - def installed_tool_dependencies( self ): - """Return the repository's tool dependencies that are currently installed.""" - installed_dependencies = [] - for tool_dependency in self.tool_dependencies: - if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED, ToolDependency.installation_status.ERROR ]: - installed_dependencies.append( tool_dependency ) - return installed_dependencies - @property - def missing_tool_dependencies( self ): - """Return the repository's tool dependencies that are not currently installed, and may not ever have been installed.""" - missing_dependencies = [] - for tool_dependency in self.tool_dependencies: - if tool_dependency.status not in [ ToolDependency.installation_status.INSTALLED ]: - missing_dependencies.append( tool_dependency ) - return missing_dependencies + + def set_shed_config_filename( self, value ): + self.metadata[ 'shed_config_filename' ] = value + + shed_config_filename = property( get_shed_config_filename, set_shed_config_filename ) + @property def tool_dependencies_being_installed( self ): dependencies_being_installed = [] @@ -3615,6 +3657,7 @@ if tool_dependency.status == ToolDependency.installation_status.INSTALLING: dependencies_being_installed.append( tool_dependency ) return dependencies_being_installed + @property def tool_dependencies_missing_or_being_installed( self ): dependencies_missing_or_being_installed = [] @@ -3625,6 +3668,7 @@ ToolDependency.installation_status.UNINSTALLED ]: dependencies_missing_or_being_installed.append( tool_dependency ) return dependencies_missing_or_being_installed + @property def tool_dependencies_with_installation_errors( self ): dependencies_with_installation_errors = [] @@ -3632,6 +3676,24 @@ if tool_dependency.status == ToolDependency.installation_status.ERROR: dependencies_with_installation_errors.append( tool_dependency ) return dependencies_with_installation_errors + + @property + def tool_shed_path_name( self ): + tool_shed_url = self.tool_shed + if tool_shed_url.find( ':' ) > 0: + # Eliminate the port, if any, since it will result in an invalid directory name. + tool_shed_url = tool_shed_url.split( ':' )[ 0 ] + return tool_shed_url.rstrip( '/' ) + + @property + def uninstalled_repository_dependencies( self ): + """Return the repository's repository dependencies that have been uninstalled.""" + uninstalled_required_repositories = [] + for required_repository in self.repository_dependencies: + if required_repository.status == self.installation_status.UNINSTALLED: + uninstalled_required_repositories.append( required_repository ) + return uninstalled_required_repositories + @property def uninstalled_tool_dependencies( self ): """Return the repository's tool dependencies that have been uninstalled.""" @@ -3641,11 +3703,22 @@ uninstalled_tool_dependencies.append( tool_dependency ) return uninstalled_tool_dependencies + @property + def upgrade_available( self ): + if self.tool_shed_status: + if self.is_deprecated_in_tool_shed: + # Only allow revision upgrades if the repository is not deprecated in the tool shed. + return False + return galaxy.util.asbool( self.tool_shed_status.get( 'revision_upgrade', False ) ) + return False + + class RepositoryRepositoryDependencyAssociation( object ): def __init__( self, tool_shed_repository_id=None, repository_dependency_id=None ): self.tool_shed_repository_id = tool_shed_repository_id self.repository_dependency_id = repository_dependency_id + class RepositoryDependency( object ): def __init__( self, tool_shed_repository_id=None ): self.tool_shed_repository_id = tool_shed_repository_id diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -405,7 +405,7 @@ Column( "ctx_rev", TrimmedString( 10 ) ), Column( "metadata", JSONType, nullable=True ), Column( "includes_datatypes", Boolean, index=True, default=False ), - Column( "update_available", Boolean, default=False ), + Column( "tool_shed_status", JSONType, nullable=True ), Column( "deleted", Boolean, index=True, default=False ), Column( "uninstalled", Boolean, default=False ), Column( "dist_to_shed", Boolean, default=False ), diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/galaxy/model/migrate/versions/0116_drop_update_available_col_add_tool_shed_status_col.py --- /dev/null +++ b/lib/galaxy/model/migrate/versions/0116_drop_update_available_col_add_tool_shed_status_col.py @@ -0,0 +1,77 @@ +""" +Migration script to drop the update_available Boolean column and replace it with the tool_shed_status JSONType column in the tool_shed_repository table. +""" + +from sqlalchemy import * +from sqlalchemy.orm import * +from migrate import * +from migrate.changeset import * +import sys, logging +from galaxy.model.custom_types import * +from sqlalchemy.exc import * +import datetime +now = datetime.datetime.utcnow + +log = logging.getLogger( __name__ ) +log.setLevel( logging.DEBUG ) +handler = logging.StreamHandler( sys.stdout ) +format = "%(name)s %(levelname)s %(asctime)s %(message)s" +formatter = logging.Formatter( format ) +handler.setFormatter( formatter ) +log.addHandler( handler ) + +metadata = MetaData() + +def default_false( migrate_engine ): + if migrate_engine.name == 'mysql' or migrate_engine.name == 'sqlite': + return "0" + elif migrate_engine.name in [ 'postgresql', 'postgres' ]: + return "false" + +def upgrade( migrate_engine ): + metadata.bind = migrate_engine + print __doc__ + metadata.reflect() + try: + ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + except NoSuchTableError: + ToolShedRepository_table = None + log.debug( "Failed loading table tool_shed_repository" ) + if ToolShedRepository_table is not None: + # For some unknown reason it is no longer possible to drop a column in a migration script if using the sqlite database. + if migrate_engine.name != 'sqlite': + try: + col = ToolShedRepository_table.c.update_available + col.drop() + except Exception, e: + print "Dropping column update_available from the tool_shed_repository table failed: %s" % str( e ) + c = Column( "tool_shed_status", JSONType, nullable=True ) + try: + c.create( ToolShedRepository_table ) + assert c is ToolShedRepository_table.c.tool_shed_status + except Exception, e: + print "Adding tool_shed_status column to the tool_shed_repository table failed: %s" % str( e ) + +def downgrade( migrate_engine ): + metadata.bind = migrate_engine + metadata.reflect() + try: + ToolShedRepository_table = Table( "tool_shed_repository", metadata, autoload=True ) + except NoSuchTableError: + ToolShedRepository_table = None + log.debug( "Failed loading table tool_shed_repository" ) + if ToolShedRepository_table is not None: + # For some unknown reason it is no longer possible to drop a column in a migration script if using the sqlite database. + if migrate_engine.name != 'sqlite': + try: + col = ToolShedRepository_table.c.tool_shed_status + col.drop() + except Exception, e: + print "Dropping column tool_shed_status from the tool_shed_repository table failed: %s" % str( e ) + c = Column( "update_available", Boolean, default=False ) + try: + c.create( ToolShedRepository_table ) + assert c is ToolShedRepository_table.c.update_available + migrate_engine.execute( "UPDATE tool_shed_repository SET update_available=%s" % default_false( migrate_engine ) ) + except Exception, e: + print "Adding column update_available to the tool_shed_repository table failed: %s" % str( e ) diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py +++ b/lib/galaxy/web/framework/helpers/grids.py @@ -39,6 +39,7 @@ cur_filter_pref_name = ".filter" cur_sort_key_pref_name = ".sort_key" pass_through_operations = {} + legend = None def __init__( self ): # Determine if any multiple row operations are defined self.has_multiple_item_operations = False diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py --- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py +++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py @@ -94,6 +94,10 @@ return trans.response.send_redirect( web.url_for( controller='admin_toolshed', action='check_for_updates', **kwd ) ) + if operation == "update tool shed status": + return trans.response.send_redirect( web.url_for( controller='admin_toolshed', + action='update_tool_shed_status_for_installed_repository', + **kwd ) ) if operation == "reset to install": kwd[ 'reset_repository' ] = True return trans.response.send_redirect( web.url_for( controller='admin_toolshed', @@ -146,8 +150,6 @@ return trans.response.send_redirect( web.url_for( controller='admin_toolshed', action='deactivate_or_uninstall_repository', **kwd ) ) - if 'message' not in kwd or not kwd[ 'message' ]: - kwd[ 'message' ] = 'Names of repositories for which updates are available are highlighted in yellow.' return self.installed_repository_grid( trans, **kwd ) @web.expose @@ -1627,10 +1629,15 @@ updating_installed_repository=True, persist=True ) repository.metadata = metadata_dict - # Update the repository changeset_revision in the database. + # Update the repository.changeset_revision column in the database. repository.changeset_revision = latest_changeset_revision repository.ctx_rev = latest_ctx_rev - repository.update_available = False + # Update the repository.tool_shed_status column in the database. + tool_shed_status_dict = suc.get_tool_shed_status_for_installed_repository( trans.app, repository ) + if tool_shed_status_dict: + repository.tool_shed_status = tool_shed_status_dict + else: + repository.tool_shed_status = None trans.sa_session.add( repository ) trans.sa_session.flush() if 'tools' in metadata_dict: @@ -1678,6 +1685,46 @@ @web.expose @web.require_admin + def update_tool_shed_status_for_installed_repository( self, trans, all_installed_repositories=False, **kwd ): + message = kwd.get( 'message', '' ) + status = kwd.get( 'status', 'done' ) + if all_installed_repositories: + success_count = 0 + repository_names_not_updated = [] + updated_count = 0 + for repository in trans.sa_session.query( trans.model.ToolShedRepository ) \ + .filter( trans.model.ToolShedRepository.table.c.deleted == False ): + ok, updated = suc.check_or_update_tool_shed_status_for_installed_repository( trans, repository ) + if ok: + success_count += 1 + else: + repository_names_not_updated.append( '<b>%s</b>' % str( repository.name ) ) + if updated: + updated_count += 1 + message = "Checked the status in the tool shed for %d repositories. " % success_count + message += "Updated the tool shed status for %d repositories. " % updated_count + if repository_names_not_updated: + message += "Unable to retrieve status from the tool shed for the following repositories:\n" + message += ", ".join( repository_names_not_updated ) + else: + repository_id = kwd.get( 'id', None ) + repository = suc.get_tool_shed_repository_by_id( trans, repository_id ) + ok, updated = suc.check_or_update_tool_shed_status_for_installed_repository( trans, repository ) + if ok: + if updated: + message = "The tool shed status for repository <b>%s</b> has been updated." % str( repository.name ) + else: + message = "The status has not changed in the tool shed for repository <b>%s</b>." % str( repository.name ) + else: + message = "Unable to retrieve status from the tool shed for repository <b>%s</b>." % str( repository.name ) + status = 'error' + return trans.response.send_redirect( web.url_for( controller='admin_toolshed', + action='browse_repositories', + message=message, + status=status ) ) + + @web.expose + @web.require_admin def view_tool_metadata( self, trans, repository_id, tool_id, **kwd ): message = kwd.get( 'message', '' ) status = kwd.get( 'status', 'done' ) diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 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 @@ -2651,6 +2651,59 @@ owner=owner ) ) @web.expose + def status_for_installed_repository( self, trans, **kwd ): + """ + Handle a request from a local Galaxy instance, returning a dictionary with boolean values for whether there are updates available + for the repository revision, newer installable revisions available, the revision is the latest installable revision, or if the repository + is deprecated. + """ + name = kwd.get( 'name', None ) + owner = kwd.get( 'owner', None ) + changeset_revision = kwd.get( 'changeset_revision', None ) + repository = suc.get_repository_by_name_and_owner( trans.app, name, owner ) + if repository: + repo_dir = repository.repo_path( trans.app ) + repo = hg.repository( suc.get_configured_ui(), repo_dir ) + tool_shed_status_dict = {} + # Handle repository deprecation. + tool_shed_status_dict[ 'repository_deprecated' ] = str( repository.deprecated ) + # Handle latest installable revision. + if changeset_revision == repository.tip( trans.app ): + 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' + 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: + 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 ) + for index, metadata_changeset_revision in enumerate( ordered_metadata_changeset_revisions ): + if index == num_metadata_revisions: + tool_shed_status_dict[ 'revision_upgrade' ] = 'False' + break + if metadata_changeset_revision == changeset_revision: + if num_metadata_revisions - index > 0: + tool_shed_status_dict[ 'revision_upgrade' ] = 'True' + else: + tool_shed_status_dict[ 'revision_upgrade' ] = 'False' + break + return encoding_util.tool_shed_encode( tool_shed_status_dict ) + return encoding_util.tool_shed_encode( {} ) + + @web.expose def updated_changeset_revisions( self, trans, **kwd ): """ Handle a request from a local Galaxy instance to retrieve the list of changeset revisions to which an installed repository can be updated. This diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py --- a/lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py +++ b/lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py @@ -3,38 +3,90 @@ from galaxy import model, util from galaxy.web.framework.helpers import iff, grids from galaxy.model.orm import or_ +import tool_shed.util.shed_util_common as suc from tool_shed.util import tool_dependency_util log = logging.getLogger( __name__ ) +def generate_deprecated_repository_img_str( include_mouse_over=False ): + if include_mouse_over: + deprecated_tip_str = 'class="icon-button tooltip" title="This repository is deprecated in the Tool Shed"' + else: + deprecated_tip_str = '' + return '<img src="/static/images/icon_error_sml.gif" %s/>' % deprecated_tip_str + +def generate_latest_revision_img_str( include_mouse_over=False ): + if include_mouse_over: + latest_revision_tip_str = 'class="icon-button tooltip" title="This is the latest installable revision of this repository"' + else: + latest_revision_tip_str = '' + return '<img src="/static/june_2007_style/blue/ok_small.png" %s/>' % latest_revision_tip_str + +def generate_revision_updates_img_str( include_mouse_over=False ): + if include_mouse_over: + revision_updates_tip_str = 'class="icon-button tooltip" title="Updates are available in the Tool Shed for this revision"' + else: + revision_updates_tip_str = '' + return '<img src="/static/images/icon_warning_sml.gif" %s/>' % revision_updates_tip_str + +def generate_revision_upgrades_img_str( include_mouse_over=False ): + if include_mouse_over: + revision_upgrades_tip_str = 'class="icon-button tooltip" title="A newer installable revision is available for this repository"' + else: + revision_upgrades_tip_str = '' + return '<img src="/static/images/up.gif" %s/>' % revision_upgrades_tip_str + +def generate_unknown_img_str( include_mouse_over=False ): + if include_mouse_over: + unknown_tip_str = 'class="icon-button tooltip" title="Unable to get information from the Tool Shed"' + else: + unknown_tip_str = '' + return '<img src="/static/june_2007_style/blue/question-octagon-frame.png" %s/>' % unknown_tip_str + class InstalledRepositoryGrid( grids.Grid ): + class ToolShedStatusColumn( grids.TextColumn ): + + def get_value( self, trans, grid, tool_shed_repository ): + if tool_shed_repository.tool_shed_status: + tool_shed_status_str = '' + if tool_shed_repository.is_deprecated_in_tool_shed: + tool_shed_status_str += generate_deprecated_repository_img_str( include_mouse_over=True ) + if tool_shed_repository.is_latest_installable_revision: + tool_shed_status_str += generate_latest_revision_img_str( include_mouse_over=True ) + if tool_shed_repository.revision_update_available: + tool_shed_status_str += generate_revision_updates_img_str( include_mouse_over=True ) + if tool_shed_repository.upgrade_available: + tool_shed_status_str += generate_revision_upgrades_img_str( include_mouse_over=True ) + else: + tool_shed_status_str = generate_unknown_img_str( include_mouse_over=True ) + return tool_shed_status_str + + class NameColumn( grids.TextColumn ): def get_value( self, trans, grid, tool_shed_repository ): - if tool_shed_repository.update_available: - return '<div class="count-box state-color-running">%s</div>' % tool_shed_repository.name - return tool_shed_repository.name + return str( tool_shed_repository.name ) class DescriptionColumn( grids.TextColumn ): def get_value( self, trans, grid, tool_shed_repository ): - return tool_shed_repository.description + return util.unicodify( tool_shed_repository.description ) class OwnerColumn( grids.TextColumn ): def get_value( self, trans, grid, tool_shed_repository ): - return tool_shed_repository.owner + return str( tool_shed_repository.owner ) class RevisionColumn( grids.TextColumn ): def get_value( self, trans, grid, tool_shed_repository ): - return tool_shed_repository.changeset_revision + return str( tool_shed_repository.changeset_revision ) class StatusColumn( grids.TextColumn ): @@ -91,6 +143,8 @@ template='/admin/tool_shed_repository/grid.mako' default_sort_key = "name" columns = [ + ToolShedStatusColumn( "", + attach_popup=False ), NameColumn( "Name", key="name", link=( lambda item: iff( item.status in [ model.ToolShedRepository.installation_status.CLONING ], @@ -114,10 +168,18 @@ key="free-text-search", visible=False, filterable="standard" ) ) - global_actions = [] - operations = [ grids.GridOperation( "Get updates", + global_actions = [ + grids.GridAction( "Update tool shed status", + dict( controller='admin_toolshed', action='update_tool_shed_status_for_installed_repository', all_installed_repositories=True ) ) + ] + operations = [ grids.GridOperation( "Update tool shed status", allow_multiple=False, - condition=( lambda item: not item.deleted and item.status not in \ + condition=( lambda item: not item.deleted ), + async_compatible=False, + url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='update tool shed status' ) ), + grids.GridOperation( "Get updates", + allow_multiple=False, + condition=( lambda item: not item.deleted and item.revision_update_available and item.status not in \ [ model.ToolShedRepository.installation_status.ERROR, model.ToolShedRepository.installation_status.NEW ] ), async_compatible=False, url_args=dict( controller='admin_toolshed', action='browse_repositories', operation='get updates' ) ), @@ -151,6 +213,15 @@ def build_initial_query( self, trans, **kwd ): return trans.sa_session.query( self.model_class ) + @property + def legend( self ): + legend_str = '%s Updates are available in the Tool Shed for this revision<br/>' % generate_revision_updates_img_str() + legend_str += '%s A newer installable revision is available for this repository<br/>' % generate_revision_upgrades_img_str() + legend_str += '%s This is the latest installable revision of this repository<br/>' % generate_latest_revision_img_str() + legend_str += '%s This repository is deprecated in the Tool Shed<br/>' % generate_deprecated_repository_img_str() + legend_str += '%s Unable to get information from the Tool Shed<br/>' % generate_unknown_img_str() + return legend_str + class RepositoryInstallationGrid( grids.Grid ): diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py --- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py +++ b/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py @@ -14,8 +14,15 @@ def clean_tool_shed_url( base_url ): if base_url: - protocol, base = base_url.split( '://' ) - return base.rstrip( '/' ) + if base_url.find( '://' ) > -1: + try: + protocol, base = base_url.split( '://' ) + except ValueError, e: + # The received base_url must be an invalid url. + log.debug( "Returning unchanged invalid base_url from td_common_util.clean_tool_shed_url: %s" % str( base_url ) ) + return base_url + return base.rstrip( '/' ) + return base_url.rstrip( '/' ) return base_url def create_env_var_dict( elem, tool_dependency_install_dir=None, tool_shed_repository_install_dir=None ): diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/tool_shed/galaxy_install/update_manager.py --- a/lib/tool_shed/galaxy_install/update_manager.py +++ b/lib/tool_shed/galaxy_install/update_manager.py @@ -26,29 +26,24 @@ def __restarter( self ): log.info( 'Update manager restarter starting up...' ) while self.running: - flush_needed = False + # Make a call to the tool shed for each installed repository to get the latest status information in the tool shed for the + # repository. This information includes items like newer installable repository revisions, current revision updates, whether + # the repository revision is the latest installable revision, and whether the repository has been deprecated in the tool shed. for repository in self.sa_session.query( self.app.model.ToolShedRepository ) \ - .filter( and_( self.app.model.ToolShedRepository.table.c.update_available == False, - self.app.model.ToolShedRepository.table.c.deleted == False ) ): - if self.check_for_update( repository ): - repository.update_available = True - self.sa_session.add( repository ) - flush_needed = True - if flush_needed: - self.sa_session.flush() + .filter( self.app.model.ToolShedRepository.table.c.deleted == False ): + tool_shed_status_dict = suc.get_tool_shed_status_for_installed_repository( self.app, repository ) + if tool_shed_status_dict: + if tool_shed_status_dict != repository.tool_shed_status: + repository.tool_shed_status = tool_shed_status_dict + self.sa_session.flush() + else: + # The received tool_shed_status_dict is an empty dictionary, so coerce to None. + tool_shed_status_dict = None + if tool_shed_status_dict != repository.tool_shed_status: + repository.tool_shed_status = tool_shed_status_dict + self.sa_session.flush() self.sleeper.sleep( self.seconds_to_sleep ) - log.info( 'Transfer job restarter shutting down...' ) - - def check_for_update( self, repository ): - tool_shed_url = suc.get_url_from_tool_shed( self.app, repository.tool_shed ) - url = '%s/repository/check_for_updates?name=%s&owner=%s&changeset_revision=%s&from_update_manager=True' % \ - ( tool_shed_url, repository.name, repository.owner, repository.changeset_revision ) - try: - text = common_util.tool_shed_get( self.app, tool_shed_url, url ) - except Exception, e: - # The required tool shed may be unavailable. - text = 'False' - return string_as_bool( text ) + log.info( 'Update manager restarter shutting down...' ) def shutdown( self ): self.running = False diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/tool_shed/util/repository_dependency_util.py --- a/lib/tool_shed/util/repository_dependency_util.py +++ b/lib/tool_shed/util/repository_dependency_util.py @@ -182,7 +182,7 @@ debug_msg = "Resetting tool_shed_repository '%s' for installation.\n" % str( installed_tool_shed_repository.name ) debug_msg += "The current state of the tool_shed_repository is:\n" debug_msg += "deleted: %s\n" % str( installed_tool_shed_repository.deleted ) - debug_msg += "update_available: %s\n" % str( installed_tool_shed_repository.update_available ) + debug_msg += "tool_shed_status: %s\n" % str( installed_tool_shed_repository.tool_shed_status ) debug_msg += "uninstalled: %s\n" % str( installed_tool_shed_repository.uninstalled ) debug_msg += "status: %s\n" % str( installed_tool_shed_repository.status ) debug_msg += "error_message: %s\n" % str( installed_tool_shed_repository.error_message ) diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 lib/tool_shed/util/shed_util_common.py --- a/lib/tool_shed/util/shed_util_common.py +++ b/lib/tool_shed/util/shed_util_common.py @@ -17,6 +17,7 @@ from galaxy.model.orm import or_ import sqlalchemy.orm.exc from tool_shed.util import common_util +from tool_shed.util import encoding_util from tool_shed.util import xml_util from xml.etree import ElementTree as XmlET from galaxy import eggs @@ -30,7 +31,7 @@ eggs.require( 'markupsafe' ) import markupsafe - + log = logging.getLogger( __name__ ) CHUNK_SIZE = 2**20 # 1Mb @@ -171,6 +172,20 @@ return True return False +def check_or_update_tool_shed_status_for_installed_repository( trans, repository ): + updated = False + tool_shed_status_dict = get_tool_shed_status_for_installed_repository( trans.app, repository ) + if tool_shed_status_dict: + ok = True + if tool_shed_status_dict != repository.tool_shed_status: + repository.tool_shed_status = tool_shed_status_dict + trans.sa_session.add( repository ) + trans.sa_session.flush() + updated = True + else: + ok = False + return ok, updated + def clean_repository_clone_url( repository_clone_url ): """Return a URL that can be used to clone a tool shed repository, eliminating the protocol and user if either exists.""" if repository_clone_url.find( '@' ) > 0: @@ -194,7 +209,7 @@ return tool_shed_url.split( ':' )[ 0 ] return tool_shed_url.rstrip( '/' ) -def clone_repository( repository_clone_url, repository_file_dir, ctx_rev ): +def clone_repository( repository_clone_url, repository_file_dir, ctx_rev ): """Clone the repository up to the specified changeset_revision. No subsequent revisions will be present in the cloned repository.""" try: commands.clone( get_configured_ui(), @@ -248,7 +263,7 @@ # was later uninstalled, this value should be received as the value of that change set to which the repository had been updated just prior # to it being uninstalled. current_changeset_revision = installed_changeset_revision - sa_session = app.model.context.current + sa_session = app.model.context.current tool_shed = get_tool_shed_from_clone_url( repository_clone_url ) if not owner: owner = get_repository_owner_from_clone_url( repository_clone_url ) @@ -322,7 +337,6 @@ elem = XmlET.Element( 'tool_shed_repository' ) else: elem = XmlET.SubElement( parent_elem, 'tool_shed_repository' ) - tool_shed_elem = XmlET.SubElement( elem, 'tool_shed' ) tool_shed_elem.text = tool_shed repository_name_elem = XmlET.SubElement( elem, 'repository_name' ) @@ -337,10 +351,14 @@ new_elem = XmlET.SubElement( elem, key ) new_elem.text = value return elem - + def generate_repository_info_elem_from_repository( tool_shed_repository, parent_elem=None, **kwd ): - return generate_repository_info_elem( tool_shed_repository.tool_shed, tool_shed_repository.name, tool_shed_repository.installed_changeset_revision, tool_shed_repository.owner, parent_elem=parent_elem, **kwd ) - + return generate_repository_info_elem( tool_shed_repository.tool_shed, + tool_shed_repository.name, + tool_shed_repository.installed_changeset_revision, + tool_shed_repository.owner, + parent_elem=parent_elem, + **kwd ) def generate_sharable_link_for_repository_in_tool_shed( trans, repository, changeset_revision=None ): """Generate the URL for sharing a repository that is in the tool shed.""" @@ -376,7 +394,7 @@ def generate_tool_guid( repository_clone_url, tool ): """ Generate a guid for the installed tool. It is critical that this guid matches the guid for - the tool in the Galaxy tool shed from which it is being installed. The form of the guid is + the tool in the Galaxy tool shed from which it is being installed. The form of the guid is <tool shed host>/repos/<repository owner>/<repository name>/<tool id>/<tool version> """ tmp_url = clean_repository_clone_url( repository_clone_url ) @@ -399,7 +417,7 @@ tool_config = tool_dict[ 'tool_config' ] file_name = strip_path( tool_config ) guids_and_configs[ guid ] = file_name - # Parse the shed_tool_conf file in which all of this repository's tools are defined and generate the tool_panel_dict. + # Parse the shed_tool_conf file in which all of this repository's tools are defined and generate the tool_panel_dict. tree, error_message = xml_util.parse_xml( shed_tool_conf ) if tree is None: return tool_panel_dict @@ -517,7 +535,7 @@ Send a request to the tool shed to retrieve the ctx_rev for a repository defined by the combination of a name, owner and changeset revision. """ - url = url_join( tool_shed_url, + url = url_join( tool_shed_url, 'repository/get_ctx_rev?name=%s&owner=%s&changeset_revision=%s' % ( name, owner, changeset_revision ) ) ctx_rev = common_util.tool_shed_get( app, tool_shed_url, url ) return ctx_rev @@ -1105,6 +1123,10 @@ return shed_tool_conf_dict[ 'tool_path' ] return None +def get_tool_shed_from_clone_url( repository_clone_url ): + tmp_url = clean_repository_clone_url( repository_clone_url ) + return tmp_url.split( '/repos/' )[ 0 ].rstrip( '/' ) + def get_tool_shed_repository_by_id( trans, repository_id ): """Return a tool shed repository database record defined by the id.""" # This method is used only in Galaxy, not the tool shed. @@ -1140,9 +1162,23 @@ app.model.ToolShedRepository.table.c.installed_changeset_revision == installed_changeset_revision ) ) \ .first() -def get_tool_shed_from_clone_url( repository_clone_url ): - tmp_url = clean_repository_clone_url( repository_clone_url ) - return tmp_url.split( '/repos/' )[ 0 ].rstrip( '/' ) +def get_tool_shed_status_for_installed_repository( app, repository ): + """ + Send a request to the tool shed to retrieve information about newer installable repository revisions, current revision updates, + whether the repository revision is the latest downloadable revision, and whether the repository has been deprecated in the tool shed. + The received repository is a ToolShedRepository object from Galaxy. + """ + tool_shed_url = get_url_from_tool_shed( app, repository.tool_shed ) + url = url_join( tool_shed_url, + 'repository/status_for_installed_repository?name=%s&owner=%s&changeset_revision=%s' % \ + ( repository.name, repository.owner, repository.changeset_revision ) ) + try: + encoded_tool_shed_status_dict = common_util.tool_shed_get( app, tool_shed_url, url ) + tool_shed_status_dict = encoding_util.tool_shed_decode( encoded_tool_shed_status_dict ) + except Exception, e: + log.exception( "Error attemtping to get tool shed status for installed repository %s: %s" % ( str( repository.name ), str( e ) ) ) + return {} + return tool_shed_status_dict def get_url_from_tool_shed( app, tool_shed ): """ @@ -1349,11 +1385,11 @@ def reset_previously_installed_repository( trans, repository ): """ - Reset the atrributes of a tool_shed_repository that was previsouly installed. The repository will be in some state other than with a - status of INSTALLED, so all atributes will be set to the default NEW state. This will enable the repository to be freshly installed. + Reset the atrributes of a tool_shed_repository that was previsouly installed. The repository will be in some state other than with a + status of INSTALLED, so all atributes will be set to the default (NEW( state. This will enable the repository to be freshly installed. """ repository.deleted = False - repository.update_available = False + repository.tool_shed_status = None repository.uninstalled = False repository.status = trans.model.ToolShedRepository.installation_status.NEW repository.error_message = None diff -r be3b0358acc9f251a8c8d66beb87f6b4684c1c10 -r 8f6f926f912e2aea3f11905498fd5f04f857ec97 templates/grid_base.mako --- a/templates/grid_base.mako +++ b/templates/grid_base.mako @@ -454,5 +454,12 @@ </td></tr> %endif + %if grid.legend: + <tr> + <td colspan="100"> + ${grid.legend} + </td> + </tr> + %endif </%def> 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.
participants (1)
-
commits-noreply@bitbucket.org