1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/1bb04279216e/ Changeset: 1bb04279216e User: greg Date: 2013-10-04 16:04:25 Summary: Enhance the framework that display images declared in the help section of Galaxy tool configs contained in tool shed repositories to also work in README files with a .rst extension that are contained in repositories. Remote images that are available view http requests are supported as are any relative path for locating the image if it is contained within the repository hierarchy. The previously required $PATH_TO_IMAGES is no longer required, and is ignored if used. The following image declarations are all supported: .. image:: http://www.animalphotos.me/mammal/mammal-wcat_files/wildcat7.jpg .. image:: https://raw.github.com/peterjc/picobio/769e4a77194587207f50707939db42b9f04b3... .. image:: $PATH_TO_IMAGES/count_modes.png .. image:: count_modes.png .. image:: /deep/Phyca11_example_output.png Affected #: 11 files diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac lib/galaxy/tools/__init__.py --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -11,6 +11,7 @@ import shutil import sys import tempfile +import threading import traceback import types import urllib @@ -1333,19 +1334,16 @@ help_header = "" help_footer = "" if self.help is not None: - # Handle tool help image display for tools that are contained in repositories that are in the tool shed or installed into Galaxy. - # When tool config files use the special string $PATH_TO_IMAGES, the following code will replace that string with the path on disk. - if self.repository_id and self.help.text.find( '$PATH_TO_IMAGES' ) >= 0: - if self.app.name == 'galaxy': - repository = self.sa_session.query( self.app.model.ToolShedRepository ).get( self.app.security.decode_id( self.repository_id ) ) - if repository: - path_to_images = '/tool_runner/static/images/%s' % self.repository_id - self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images ) - elif self.app.name == 'tool_shed': - repository = self.sa_session.query( self.app.model.Repository ).get( self.app.security.decode_id( self.repository_id ) ) - if repository: - path_to_images = '/repository/static/images/%s' % self.repository_id - self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images ) + if self.repository_id and self.help.text.find( '.. image:: ' ) >= 0: + # Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy. + lock = threading.Lock() + lock.acquire( True ) + try: + self.help.text = shed_util_common.set_image_paths( self.app, self.repository_id, self.help.text ) + except Exception, e: + log.exception( "Exception in parse_help, so images may not be properly displayed:\n%s" % str( e ) ) + finally: + lock.release() help_pages = self.help.findall( "page" ) help_header = self.help.text try: diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac lib/galaxy/webapps/galaxy/buildapp.py --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -46,8 +46,12 @@ atexit.register( app.shutdown ) # Create the universe WSGI application webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' ) - # The following route will handle displaying tool shelp images for tools contained in repositories installed from the tool shed. - webapp.add_route( '/tool_runner/static/images/:repository_id/:image_file', controller='tool_runner', action='display_tool_help_image_in_repository', repository_id=None, image_file=None ) + # Handle displaying tool help images and README file images contained in repositories installed from the tool shed. + webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file', + controller='admin_toolshed', + action='display_image_in_repository', + repository_id=None, + image_file=None ) webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app ) # Force /history to go to /root/history -- needed since the tests assume this webapp.add_route( '/history', controller='root', action='history' ) diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac 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 @@ -285,6 +285,28 @@ status=status ) @web.expose + def display_image_in_repository( self, trans, **kwd ): + """ + Open an image file that is contained in an installed tool shed repository or that is referenced by a URL for display. The + image can be defined in either a README.rst file contained in the repository or the help section of a Galaxy tool config that + is contained in the repository. The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer + required, and is now ignored. + .. image:: https://raw.github.com/galaxy/some_image.png + .. image:: $PATH_TO_IMAGES/some_image.png + .. image:: /static/images/some_image.gif + .. image:: some_image.jpg + .. image:: /deep/some_image.png + """ + repository_id = kwd.get( 'repository_id', None ) + relative_path_to_image_file = kwd.get( 'image_file', None ) + if repository_id and relative_path_to_image_file: + repository = suc.get_tool_shed_repository_by_id( trans, repository_id ) + repo_files_dir = repository.repo_files_directory( trans.app ) + path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file ) + return open( path_to_file, 'r' ) + return None + + @web.expose @web.require_admin def find_tools_in_tool_shed( self, trans, **kwd ): tool_shed_url = kwd[ 'tool_shed_url' ] diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac lib/galaxy/webapps/galaxy/controllers/tool_runner.py --- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py +++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py @@ -98,22 +98,6 @@ **vars ) @web.expose - def display_tool_help_image_in_repository( self, trans, **kwd ): - repository_id = kwd.get( 'repository_id', None ) - image_file = kwd.get( 'image_file', None ) - if repository_id and image_file: - repository = suc.get_tool_shed_repository_by_id( trans, repository_id ) - repo_files_dir = os.path.join( repository.repo_files_directory( trans.app ) ) - default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) ) - if os.path.exists( default_path ): - return open( default_path, 'r' ) - else: - path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file ) - if os.path.exists( path_to_file ): - return open( path_to_file, 'r' ) - return None - - @web.expose def rerun( self, trans, id=None, from_noframe=None, **kwd ): """ Given a HistoryDatasetAssociation id, find the job and that created diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac lib/galaxy/webapps/tool_shed/buildapp.py --- a/lib/galaxy/webapps/tool_shed/buildapp.py +++ b/lib/galaxy/webapps/tool_shed/buildapp.py @@ -69,8 +69,12 @@ webapp.add_route( '/view/{owner}', controller='repository', action='sharable_owner' ) webapp.add_route( '/view/{owner}/{name}', controller='repository', action='sharable_repository' ) webapp.add_route( '/view/{owner}/{name}/{changeset_revision}', controller='repository', action='sharable_repository_revision' ) - # The following route will handle displaying tool shelp images for tools contained in repositories. - webapp.add_route( '/repository/static/images/:repository_id/:image_file', controller='repository', action='display_tool_help_image_in_repository', repository_id=None, image_file=None ) + # Handle displaying tool help images and README file images for tools contained in repositories. + webapp.add_route( '/repository/static/images/:repository_id/:image_file', + controller='repository', + action='display_image_in_repository', + repository_id=None, + image_file=None ) webapp.add_route( '/:controller/:action', action='index' ) webapp.add_route( '/:action', controller='repository', action='index' ) webapp.add_route( '/repos/*path_info', controller='hg', action='handle_request', path_info='/' ) diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac 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 @@ -666,7 +666,7 @@ # Update repository files for browsing. suc.update_repository( repo ) changeset_revision = repository.tip( trans.app ) - metadata = self.get_metadata( trans, id, changeset_revision ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=True ) repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository ) return trans.fill_template( '/webapps/tool_shed/repository/browse_repository.mako', repository=repository, @@ -897,7 +897,10 @@ message = kwd.get( 'message', '' ) status = kwd.get( 'status', 'done' ) repository = suc.get_repository_in_tool_shed( trans, id ) - metadata = self.get_metadata( trans, id, repository.tip( trans.app ) ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + id, + repository.tip( trans.app ), + metadata_only=True ) if trans.user and trans.user.email: return trans.fill_template( "/webapps/tool_shed/repository/contact_owner.mako", repository=repository, @@ -1015,6 +1018,27 @@ status=status ) ) @web.expose + def display_image_in_repository( self, trans, **kwd ): + """ + Open an image file that is contained in repository or that is referenced by a URL for display. The image can be defined in + either a README.rst file contained in the repository or the help section of a Galaxy tool config that is contained in the repository. + The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer required, and is now ignored. + .. image:: https://raw.github.com/galaxy/some_image.png + .. image:: $PATH_TO_IMAGES/some_image.png + .. image:: /static/images/some_image.gif + .. image:: some_image.jpg + .. image:: /deep/some_image.png + """ + repository_id = kwd.get( 'repository_id', None ) + relative_path_to_image_file = kwd.get( 'image_file', None ) + if repository_id and relative_path_to_image_file: + repository = suc.get_repository_in_tool_shed( trans, repository_id ) + repo_files_dir = os.path.join( repository.repo_path( trans.app ), repository.name ) + path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file ) + return open( path_to_file, 'r' ) + return None + + @web.expose def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ): message = kwd.get( 'message', '' ) status = kwd.get( 'status', 'done' ) @@ -1023,7 +1047,10 @@ if message: status = 'error' tool_state = tool_util.new_state( trans, tool, invalid=False ) - metadata = self.get_metadata( trans, repository_id, changeset_revision ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + repository_id, + changeset_revision, + metadata_only=True ) try: return trans.fill_template( "/webapps/tool_shed/repository/tool_form.mako", repository=repository, @@ -1052,22 +1079,6 @@ status='error' ) ) @web.expose - def display_tool_help_image_in_repository( self, trans, **kwd ): - repository_id = kwd.get( 'repository_id', None ) - image_file = kwd.get( 'image_file', None ) - if repository_id and image_file: - repository = suc.get_repository_in_tool_shed( trans, repository_id ) - repo_files_dir = os.path.join( repository.repo_path( trans.app ), repository.name ) - default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) ) - if os.path.exists( default_path ): - return open( default_path, 'r' ) - else: - path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file ) - if os.path.exists( path_to_file ): - return open( path_to_file, 'r' ) - return None - - @web.expose def download( self, trans, repository_id, changeset_revision, file_type, **kwd ): """Download an archive of the repository files compressed as zip, gz or bz2.""" # FIXME: thgis will currently only download the repository tip, no matter which installable changeset_revision is being viewed. @@ -1571,12 +1582,6 @@ pubdate=strftime( '%a, %d %b %Y %H:%M:%S UT', gmtime() ), items=functional_test_results ) - def get_metadata( self, trans, repository_id, changeset_revision ): - repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision ) - if repository_metadata and repository_metadata.metadata: - return repository_metadata.metadata - return None - @web.json def get_readme_files( self, trans, **kwd ): """ @@ -2409,7 +2414,10 @@ avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model ) display_reviews = util.string_as_bool( kwd.get( 'display_reviews', False ) ) rra = self.get_user_item_rating( trans.sa_session, trans.user, repository, webapp_model=trans.model ) - metadata = self.get_metadata( trans, id, repository.tip( trans.app ) ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + id, + repository.tip( trans.app ), + metadata_only=True ) repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository ) return trans.fill_template( '/webapps/tool_shed/repository/rate_repository.mako', repository=repository, @@ -2806,7 +2814,10 @@ 'has_metadata' : has_metadata } # Make sure we'll view latest changeset first. changesets.insert( 0, change_dict ) - metadata = self.get_metadata( trans, id, repository.tip( trans.app ) ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + id, + repository.tip( trans.app ), + metadata_only=True ) return trans.fill_template( '/webapps/tool_shed/repository/view_changelog.mako', repository=repository, metadata=metadata, @@ -2839,7 +2850,7 @@ diffs = [] for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node() ): diffs.append( suc.to_html_string( diff ) ) - metadata = self.get_metadata( trans, id, ctx_str ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, ctx_str, metadata_only=True ) # For rendering the prev button. if ctx_parent: ctx_parent_rev = ctx_parent.rev() diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac lib/tool_shed/util/metadata_util.py --- a/lib/tool_shed/util/metadata_util.py +++ b/lib/tool_shed/util/metadata_util.py @@ -1072,13 +1072,15 @@ """Get repository metadata from the database""" return trans.sa_session.query( trans.model.RepositoryMetadata ).get( trans.security.decode_id( id ) ) -def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision ): - """Get a specified metadata record for a specified repository.""" - return trans.sa_session.query( trans.model.RepositoryMetadata ) \ - .filter( and_( trans.model.RepositoryMetadata.table.c.repository_id == trans.security.decode_id( id ), - trans.model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) ) \ - .first() - +def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=False ): + """Get a specified metadata record for a specified repository in the tool shed.""" + if metadata_only: + repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision ) + if repository_metadata and repository_metadata.metadata: + return repository_metadata.metadata + return None + return suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision ) + def get_repository_metadata_revisions_for_review( repository, reviewed=True ): repository_metadata_revisions = [] metadata_changeset_revision_hashes = [] diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac lib/tool_shed/util/readme_util.py --- a/lib/tool_shed/util/readme_util.py +++ b/lib/tool_shed/util/readme_util.py @@ -1,7 +1,11 @@ import logging import os +import threading +from mako.template import Template from galaxy import eggs +from galaxy import web from galaxy.util import json +from galaxy.util import rst_to_html from galaxy.util import unicodify import tool_shed.util.shed_util_common as suc from tool_shed.util import common_util @@ -34,13 +38,39 @@ full_path_to_readme_file = os.path.abspath( os.path.join( tool_path, relative_path_to_readme_file ) ) else: full_path_to_readme_file = os.path.abspath( relative_path_to_readme_file ) + text = None try: f = open( full_path_to_readme_file, 'r' ) text = unicodify( f.read() ) f.close() - readme_files_dict[ readme_file_name ] = suc.size_string( text ) except Exception, e: log.exception( "Error reading README file '%s' from disk: %s" % ( str( relative_path_to_readme_file ), str( e ) ) ) + text = None + if text: + text_of_reasonable_length = suc.size_string( text ) + if text_of_reasonable_length.find( '.. image:: ' ) >= 0: + # Handle image display for README files that are contained in repositories in the tool shed or installed into Galaxy. + lock = threading.Lock() + lock.acquire( True ) + try: + text_of_reasonable_length = suc.set_image_paths( trans.app, + trans.security.encode_id( repository.id ), + text_of_reasonable_length ) + except Exception, e: + log.exception( "Exception in build_readme_files_dict, so images may not be properly displayed:\n%s" % str( e ) ) + finally: + lock.release() + if readme_file_name.endswith( '.rst' ): + text_of_reasonable_length = Template( rst_to_html( text_of_reasonable_length ), + input_encoding='utf-8', + output_encoding='utf-8', + default_filters=[ 'decode.utf8' ], + encoding_errors='replace' ) + text_of_reasonable_length = text_of_reasonable_length.render( static_path=web.url_for( '/static' ), + host_url=web.url_for( '/', qualified=True ) ) + else: + text_of_reasonable_length = suc.to_html_string( text_of_reasonable_length ) + readme_files_dict[ readme_file_name ] = text_of_reasonable_length else: # We must be in the tool shed and have an old changeset_revision, so we need to retrieve the file contents from the repository manifest. ctx = suc.get_changectx_for_changeset( repo, changeset_revision ) diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac 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 @@ -1,5 +1,6 @@ import logging import os +import re import shutil import string import tempfile @@ -1504,6 +1505,28 @@ """Return a reversed list of changesets in the repository changelog up to and including the included_upper_bounds_changeset_revision.""" return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision ) +def set_image_paths( app, encoded_repository_id, text ): + """ + Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy as well as image + display in repository README files. This method will determine the location of the image file and return the path to it that will enable + the caller to open the file. + """ + if text: + if app.name == 'galaxy': + route_to_images = '/admin_toolshed/static/images/%s' % encoded_repository_id + else: + # We're in the tool shed. + route_to_images = '/repository/static/images/%s' % encoded_repository_id + # We used to require $PATH_TO_IMAGES, but we now eliminate it if it's used. + text = text.replace( '$PATH_TO_IMAGES', '' ) + # We can eliminate the default setting of /static/images since the routs will properly display images contained in that directory. + text = text.replace( '/static/images', '' ) + # Use regex to instantiate routes into the defined image paths. + for match in re.findall( '.. image:: (?!http)/?(.+)', text ): + text = text.replace( match, match.replace( '/', '%2F' ) ) + text = re.sub( '.. image:: (?!http)/?(.+)', '.. image:: %s/\\1' % route_to_images, text ) + return text + def set_only_if_compiling_contained_td( repository, required_repository ): """Return True if the received required_repository is only needed to compile a tool dependency defined for the received repository.""" # This method is called only from Galaxy when rendering repository dependencies for an installed tool shed repository. diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac templates/webapps/tool_shed/repository/common.mako --- a/templates/webapps/tool_shed/repository/common.mako +++ b/templates/webapps/tool_shed/repository/common.mako @@ -584,12 +584,7 @@ </%def><%def name="render_readme( readme, pad, parent, row_counter )"> - <% - from tool_shed.util.shed_util_common import to_html_string - from galaxy.util import rst_to_html - encoded_id = trans.security.encode_id( readme.id ) - render_rst = readme.name.endswith( '.rst' ) - %> + <% encoded_id = trans.security.encode_id( readme.id ) %><tr class="datasetRow" %if parent is not None: parent="${parent}" @@ -597,11 +592,7 @@ id="libraryItem-rr-${encoded_id}"><td style="padding-left: ${pad+20}px;"><table id="readme_files"> - %if render_rst: - <tr><td>${ rst_to_html( readme.text ) }</td></tr> - %else: - <tr><td>${ to_html_string( readme.text ) }</td></tr> - %endif + <tr><td>${ readme.text }</td></tr></table></td></tr> diff -r 288203bc6e4eac539ad2008ca088530cf2250cee -r 1bb04279216e0abf4e24b9299cccc06c10b9c0ac test/tool_shed/functional/test_1140_tool_help_images.py --- a/test/tool_shed/functional/test_1140_tool_help_images.py +++ b/test/tool_shed/functional/test_1140_tool_help_images.py @@ -109,10 +109,10 @@ In the center tool panel, look for the image string similar to the following string where the encoded_repository_id is previously determined as the installed ToolShedRepository id: - src="/tool_runner/static/images/<id>/count_modes.png" + src="/admin_toolshed/static/images/<id>/count_modes.png" ''' repository = test_db_util.get_installed_repository_by_name_owner( repository_name, common.test_user_1_name ) - image_path = 'src="/tool_runner/static/images/%s/count_modes.png"' % self.security.encode_id( repository.id ) + image_path = 'src="/admin_toolshed/static/images/%s/count_modes.png"' % self.security.encode_id( repository.id ) # The repository uploaded in this test should only define one tool, which should be the tool that contains a link to an image. repository_metadata = repository.metadata tool_id = repository_metadata[ 'tools' ][ 0 ][ 'guid' ] 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.