commit/galaxy-central: dan: Refactor Biostar interaction code. Add the ability to search for previously asked questions about a tool from the tool form. Allow logging out of biostar.
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/86ea88b98bb7/ Changeset: 86ea88b98bb7 User: dan Date: 2014-03-20 22:23:41 Summary: Refactor Biostar interaction code. Add the ability to search for previously asked questions about a tool from the tool form. Allow logging out of biostar. Affected #: 5 files diff -r f830918df15be11380a4628c9c3aad860547e092 -r 86ea88b98bb7b2f2eb1fad554307d3e5d055d9ce lib/galaxy/util/biostar.py --- /dev/null +++ b/lib/galaxy/util/biostar.py @@ -0,0 +1,121 @@ +""" +Support for integration with the Biostar application +""" + +import hmac +import urlparse +import re +from unicodedata import normalize +from galaxy.web.base.controller import url_for + +_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+') + +DEFAULT_GALAXY_TAG = 'galaxy' + +BIOSTAR_ACTIONS = { + None: { 'url': lambda x: '', 'uses_payload': False }, + 'new_post': { 'url': lambda x: 'p/new/post/', 'uses_payload': True }, + 'show_tags': { 'url': lambda x: 't/%s/' % ( "+".join( ( x.get( 'tag_val' ) or DEFAULT_GALAXY_TAG ).split( ',' ) ) ), 'uses_payload':False }, + 'log_out': { 'url': lambda x: 'site/logout/', 'uses_payload': False } +} + +DEFAULT_BIOSTAR_COOKIE_AGE = 1 + +def biostar_enabled( app ): + return bool( app.config.biostar_url ) + +# Slugifying from Armin Ronacher (http://flask.pocoo.org/snippets/5/) +def slugify(text, delim=u'-'): + """Generates an slightly worse ASCII-only slug.""" + result = [] + for word in _punct_re.split(text.lower()): + word = normalize('NFKD', word).encode('ascii', 'ignore') + if word: + result.append(word) + return unicode(delim.join(result)) + +# Default values for new posts to Biostar +DEFAULT_PAYLOAD = { + 'title': '', + 'tag_val': DEFAULT_GALAXY_TAG, + 'content': '', +} + +def get_biostar_url( app, payload=None, biostar_action=None ): + # Ensure biostar integration is enabled + if not biostar_enabled( app ): + raise Exception( "Biostar integration is not enabled" ) + if biostar_action not in BIOSTAR_ACTIONS: + raise Exception( "Invalid action specified (%s)." % ( biostar_action ) ) + biostar_action = BIOSTAR_ACTIONS[ biostar_action ] + # Start building up the payload + payload = payload or {} + payload = dict( DEFAULT_PAYLOAD, **payload ) + #generate url, can parse payload info + url = str( urlparse.urljoin( app.config.biostar_url, biostar_action.get( 'url' )( payload ) ) ) + if not biostar_action.get( 'uses_payload' ): + payload = {} + url = url_for( url, **payload ) + return url + +def tag_for_tool( tool ): + """ + Generate a reasonable biostar tag for a tool. + """ + #Biostar can now handle tags with spaces, do we want to generate tags differently now? + return slugify( unicode( tool.name ), delim='-' ) + +def populate_tag_payload( payload=None, tool=None ): + if payload is None: + payload = {} + tag_val = [ DEFAULT_GALAXY_TAG ] + if tool: + tag_val.append( tag_for_tool( tool ) ) + payload[ 'tag_val' ] = ','.join( tag_val ) + return payload + +def populate_tool_payload( payload=None, tool=None ): + payload = populate_tag_payload( payload=payload, tool=tool ) + payload[ 'title' ] = 'Need help with "%s" tool' % ( tool.name ) + payload[ 'content' ] = '<br /><hr /><p>Tool name: %s</br>Tool version: %s</br>Tool ID: %s</p>' % ( tool.name, tool.version, tool.id ) + return payload + +def determine_cookie_domain( galaxy_hostname, biostar_hostname ): + if galaxy_hostname == biostar_hostname: + return galaxy_hostname + + sub_biostar_hostname = biostar_hostname.split( '.', 1 )[-1] + if sub_biostar_hostname == galaxy_hostname: + return galaxy_hostname + + sub_galaxy_hostname = galaxy_hostname.split( '.', 1 )[-1] + if sub_biostar_hostname == sub_galaxy_hostname: + return sub_galaxy_hostname + + return galaxy_hostname + +def create_cookie( trans, key_name, key, email, age=DEFAULT_BIOSTAR_COOKIE_AGE ): + digest = hmac.new( key, email ).hexdigest() + value = "%s:%s" % (email, digest) + trans.set_cookie( value, name=key_name, path='/', age=age, version='1' ) + #We need to explicitly set the domain here, in order to allow for biostar in a subdomain to work + galaxy_hostname = urlparse.urlsplit( url_for( '/', qualified=True ) ).hostname + biostar_hostname = urlparse.urlsplit( trans.app.config.biostar_url ).hostname + trans.response.cookies[ key_name ][ 'domain' ] = determine_cookie_domain( galaxy_hostname, biostar_hostname ) + +def delete_cookie( trans, key_name ): + #Set expiration of Cookie to time in past, to cause browser to delete + if key_name in trans.request.cookies: + create_cookie( trans, trans.app.config.biostar_key_name, '', '', age=-90 ) + +def biostar_logged_in( trans ): + if biostar_enabled( trans.app ): + if trans.app.config.biostar_key_name in trans.request.cookies: + return True + return False + +def biostar_logout( trans ): + if biostar_enabled( trans.app ): + delete_cookie( trans, trans.app.config.biostar_key_name ) + return get_biostar_url( trans.app, biostar_action='log_out' ) + return None diff -r f830918df15be11380a4628c9c3aad860547e092 -r 86ea88b98bb7b2f2eb1fad554307d3e5d055d9ce lib/galaxy/webapps/galaxy/controllers/biostar.py --- a/lib/galaxy/webapps/galaxy/controllers/biostar.py +++ b/lib/galaxy/webapps/galaxy/controllers/biostar.py @@ -1,84 +1,10 @@ """ -Support for integration with the Biostar Q&A application +Controller for integration with the Biostar application """ from galaxy.web.base.controller import BaseUIController, url_for, error, web +from galaxy.util import biostar -import base64 -from galaxy.util import json -import hmac -import urlparse - -# Slugifying from Armin Ronacher (http://flask.pocoo.org/snippets/5/) - -import re -from unicodedata import normalize - -_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+') - -BIOSTAR_ACTIONS = { - None: '', - 'new': 'p/new/post/', - 'show_tag_galaxy': 't/galaxy/' -} - - -def slugify(text, delim=u'-'): - """Generates an slightly worse ASCII-only slug.""" - result = [] - for word in _punct_re.split(text.lower()): - word = normalize('NFKD', word).encode('ascii', 'ignore') - if word: - result.append(word) - return unicode(delim.join(result)) - - -# Biostar requires all keys to be present, so we start with a template -DEFAULT_PAYLOAD = { - 'title': '', - 'tag_val': 'galaxy', - 'content': '', -} - - -def encode_data( key, data ): - """ - Encode data to send a question to Biostar - """ - text = json.to_json_string(data) - text = base64.urlsafe_b64encode(text) - digest = hmac.new(key, text).hexdigest() - return text, digest - - -def tag_for_tool( tool ): - """ - Generate a reasonable biostar tag for a tool. - """ - return slugify( unicode( tool.name ) ) - -def determine_cookie_domain( galaxy_hostname, biostar_hostname ): - if galaxy_hostname == biostar_hostname: - return galaxy_hostname - - sub_biostar_hostname = biostar_hostname.split( '.', 1 )[-1] - if sub_biostar_hostname == galaxy_hostname: - return galaxy_hostname - - sub_galaxy_hostname = galaxy_hostname.split( '.', 1 )[-1] - if sub_biostar_hostname == sub_galaxy_hostname: - return sub_galaxy_hostname - - return galaxy_hostname - -def create_cookie( trans, key_name, key, email ): - digest = hmac.new( key, email ).hexdigest() - value = "%s:%s" % (email, digest) - trans.set_cookie( value, name=key_name, path='/', age=90, version='1' ) - #We need to explicitly set the domain here, in order to allow for biostar in a subdomain to work - galaxy_hostname = urlparse.urlsplit( url_for( '/', qualified=True ) ).hostname - biostar_hostname = urlparse.urlsplit( trans.app.config.biostar_url ).hostname - trans.response.cookies[ key_name ][ 'domain' ] = determine_cookie_domain( galaxy_hostname, biostar_hostname ) class BiostarController( BaseUIController ): """ @@ -89,24 +15,35 @@ def biostar_redirect( self, trans, payload=None, biostar_action=None ): """ Generate a redirect to a Biostar site using external authentication to - pass Galaxy user information and information about a specific tool. + pass Galaxy user information and optional information about a specific tool. """ - # Ensure biostar integration is enabled - if not trans.app.config.biostar_url: - return error( "Biostar integration is not enabled" ) - if biostar_action not in BIOSTAR_ACTIONS: - return error( "Invalid action specified (%s)." % ( biostar_action ) ) - - # Start building up the payload - payload = payload or {} - payload = dict( DEFAULT_PAYLOAD, **payload ) - # Do the best we can of providing user information for the payload + try: + url = biostar.get_biostar_url( trans.app, payload=payload, biostar_action=biostar_action ) + except Exception, e: + return error( str( e ) ) + # Only create/log in biostar user if is registered Galaxy user if trans.user: - email = trans.user.email - else: - email = "anon-%s" % ( trans.security.encode_id( trans.galaxy_session.id ) ) - create_cookie( trans, trans.app.config.biostar_key_name, trans.app.config.biostar_key, email ) - return trans.response.send_redirect( url_for( urlparse.urljoin( trans.app.config.biostar_url, BIOSTAR_ACTIONS[ biostar_action ] ), **payload ) ) + biostar.create_cookie( trans, trans.app.config.biostar_key_name, trans.app.config.biostar_key, trans.user.email ) + return trans.response.send_redirect( url ) + + @web.expose + def biostar_tool_tag_redirect( self, trans, tool_id=None ): + """ + Generate a redirect to a Biostar site using tag for tool. + """ + # tool_id is required + if tool_id is None: + return error( "No tool_id provided" ) + # Load the tool + tool_version_select_field, tools, tool = \ + self.app.toolbox.get_tool_components( tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=True ) + # No matching tool, unlikely + if not tool: + return error( "No tool found matching '%s'" % tool_id ) + # Tool specific information for payload + payload = biostar.populate_tag_payload( tool=tool ) + # Pass on to standard redirect method + return self.biostar_redirect( trans, payload=payload, biostar_action='show_tags' ) @web.expose def biostar_question_redirect( self, trans, payload=None ): @@ -114,8 +51,8 @@ Generate a redirect to a Biostar site using external authentication to pass Galaxy user information and information about a specific tool. """ - payload = payload or {} - return self.biostar_redirect( trans, payload=payload, biostar_action='new' ) + # Pass on to standard redirect method + return self.biostar_redirect( trans, payload=payload, biostar_action='new_post' ) @web.expose def biostar_tool_question_redirect( self, trans, tool_id=None ): @@ -133,8 +70,19 @@ if not tool: return error( "No tool found matching '%s'" % tool_id ) # Tool specific information for payload - payload = { 'title':'Need help with "%s" tool' % ( tool.name ), - 'content': '<br /><hr /><p>Tool name: %s</br>Tool version: %s</br>Tool ID: %s</p>' % ( tool.name, tool.version, tool.id ), - 'tag_val': ','.join( [ 'galaxy', tag_for_tool( tool ) ] ) } + payload = biostar.populate_tool_payload( tool=tool ) # Pass on to regular question method return self.biostar_question_redirect( trans, payload ) + + @web.expose + def biostar_logout( self, trans ): + """ + Log out of biostar + """ + try: + url = biostar.biostar_log_out( trans ) + except Exception, e: + return error( str( e ) ) + if url: + return trans.response.send_redirect( url ) + return error( "Could not determine Biostar logout URL." ) diff -r f830918df15be11380a4628c9c3aad860547e092 -r 86ea88b98bb7b2f2eb1fad554307d3e5d055d9ce lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -30,7 +30,7 @@ from galaxy.web.form_builder import build_select_field from galaxy.web.framework.helpers import time_ago, grids from datetime import datetime, timedelta -from galaxy.util import hash_util +from galaxy.util import hash_util, biostar log = logging.getLogger( __name__ ) @@ -600,6 +600,11 @@ trans.handle_user_logout( logout_all=logout_all ) message = 'You have been logged out.<br>You can log in again, <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \ ( trans.request.referer, url_for( '/' ) ) + if biostar.biostar_logged_in( trans ): + biostar_url = biostar.biostar_logout( trans ) + if biostar_url: + #TODO: It would be better if we automatically logged this user out of biostar + message += '<br>To logout of Biostar, please click <a href="%s" target="_blank">here</a>.' % ( biostar_url ) return trans.fill_template( '/user/logout.mako', refresh_frames=refresh_frames, message=message, diff -r f830918df15be11380a4628c9c3aad860547e092 -r 86ea88b98bb7b2f2eb1fad554307d3e5d055d9ce templates/webapps/galaxy/galaxy.masthead.mako --- a/templates/webapps/galaxy/galaxy.masthead.mako +++ b/templates/webapps/galaxy/galaxy.masthead.mako @@ -40,7 +40,7 @@ 'enable_cloud_launch' : app.config.get_bool('enable_cloud_launch', False), 'lims_doc_url' : app.config.get("lims_doc_url", "http://main.g2.bx.psu.edu/u/rkchak/p/sts"), 'biostar_url' : app.config.biostar_url, - 'biostar_url_redirect' : h.url_for( controller='biostar', action='biostar_redirect', biostar_action='show_tag_galaxy', qualified=True ), + 'biostar_url_redirect' : h.url_for( controller='biostar', action='biostar_redirect', biostar_action='show_tags', qualified=True ), 'support_url' : app.config.get("support_url", "http://wiki.galaxyproject.org/Support"), 'search_url' : app.config.get("search_url", "http://galaxyproject.org/search/usegalaxy/"), 'mailing_lists' : app.config.get("mailing_lists", "http://wiki.galaxyproject.org/MailingLists"), diff -r f830918df15be11380a4628c9c3aad860547e092 -r 86ea88b98bb7b2f2eb1fad554307d3e5d055d9ce templates/webapps/galaxy/tool_form.mako --- a/templates/webapps/galaxy/tool_form.mako +++ b/templates/webapps/galaxy/tool_form.mako @@ -324,8 +324,13 @@ %if trans.app.config.biostar_url: ## BioStar links <span class="pull-right"> - <span class="fa fa-question-circle"> </span><a href="${h.url_for( controller='biostar', action='biostar_tool_question_redirect', tool_id=tool.id )}" - target="_blank">Ask a question about this tool</a> <span class="fa fa-external-link"></span> + Help from Biostar + <div class="icon-btn-group"> + <a href="${h.url_for( controller='biostar', action='biostar_tool_tag_redirect', tool_id=tool.id )}" + target="_blank" class="icon-btn" title="Search for this tool"><span class="fa fa-search"></span></a> + <a href="${h.url_for( controller='biostar', action='biostar_tool_question_redirect', tool_id=tool.id )}" + target="_blank" class="icon-btn" title="Ask a question about this tool"><span class="fa fa-question-circle"></a> + </div></span> %endif </div> 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