expose toolbox search API, enable configuring it

make toolbox and workflowbox use it
This commit is contained in:
Martin Cech
2015-03-05 16:32:31 -05:00
parent c70f6dc041
commit 632ec4ebed
11 changed files with 117 additions and 25 deletions
+3 -2
View File
@@ -31,8 +31,9 @@ $( function() {
}
// Start a new ajax-request in X ms
$("#search-spinner").show();
var tool_search_url = galaxy_config.root + 'api/tools/search';
this.timer = setTimeout(function () {
$.get(tool_search_url, { query: q }, function (data) {
$.get(tool_search_url, { q: q }, function (data) {
// input.removeClass(config.loadingClass);
// Show live-search if results and search-term aren't empty
$("#search-no-results").hide();
@@ -72,7 +73,7 @@ $( function() {
}
$("#search-spinner").hide();
}, "json" );
}, 200 );
}, 400 );
}
this.lastValue = this.value;
});
+2 -1
View File
@@ -368,6 +368,7 @@ var ToolSearch = Backbone.Model.extend({
*/
do_search: function() {
var query = this.attributes.query;
var tool_search_url = galaxy_config.root + 'api/tools/search';
// If query is too short, do not search.
if (query.length < this.attributes.min_chars_for_search) {
@@ -388,7 +389,7 @@ var ToolSearch = Backbone.Model.extend({
this.timer = setTimeout(function () {
// log the search to analytics
ga( 'send', 'pageview', galaxy_config.root + '?q=' + q );
$.get(self.attributes.search_url, { query: q }, function (data) {
$.get( tool_search_url, { q: q }, function ( data ) {
self.set("results", data);
$("#search-spinner").hide();
$("#search-clear-btn").show();
+15
View File
@@ -717,6 +717,21 @@ use_interactive = True
# base URL of server providing search functionality using lucene
#fulltext_url = http://localhost:8081
# -- Toolbox Search
# The following boosts are used to customize this instance's toolbox search.
# The higher the boost the more importance the scoring algorithm gives to the
# given field. Section refers to the tool group in the tool panel. Rest of
# the fields are tool's attributes.
# tool_name_boost = 9
# tool_section_boost = 3
# tool_description_boost = 2
# tool_help_boost = 0.5
# Limits the number of results in toolbox search. Can be used to tweak
# how many results will appear.
# tool_search_limit = 20
# -- Users and Security
# Galaxy encodes various internal values when these values will be output in
+5
View File
@@ -276,6 +276,11 @@ class Configuration( object ):
# On can mildly speed up Galaxy startup time by disabling index of help,
# not needed on production systems but useful if running many functional tests.
self.index_tool_help = string_as_bool( kwargs.get( "index_tool_help", True ) )
self.tool_name_boost = kwargs.get( "tool_name_boost", None )
self.tool_section_boost = kwargs.get( "tool_section_boost", None )
self.tool_description_boost = kwargs.get( "tool_description_boost", None )
self.tool_help_boost = kwargs.get( "tool_help_boost", None )
self.tool_search_limit = kwargs.get( "tool_search_limit", None )
# Location for tool dependencies.
if 'tool_dependency_dir' in kwargs:
self.tool_dependency_dir = resolve_path( kwargs.get( "tool_dependency_dir" ), self.root )
+19 -15
View File
@@ -11,7 +11,7 @@ from whoosh.fields import Schema, STORED, TEXT
from whoosh.scoring import BM25F
from whoosh.qparser import MultifieldParser
schema = Schema( id=STORED,
title=TEXT,
name=TEXT,
description=TEXT,
section=TEXT,
help=TEXT )
@@ -31,41 +31,45 @@ class ToolBoxSearch( object ):
self.toolbox = toolbox
self.build_index( index_help )
def build_index( self, index_help ):
def build_index( self, index_help=True ):
log.debug( 'Starting to build toolbox index.' )
self.storage = RamStorage()
self.index = self.storage.create_index( schema )
writer = self.index.writer()
for id, tool in self.toolbox.tools():
add_doc_kwds = {
"id": id,
"title": to_unicode( tool.name ),
"name": to_unicode( tool.name ),
"description": to_unicode( tool.description ),
"section": to_unicode( tool.get_panel_section()[1] if len( tool.get_panel_section() ) == 2 else '' ),
"help": to_unicode( "" ),
"help": to_unicode( "" )
}
if index_help and tool.help:
try:
add_doc_kwds['help'] = to_unicode(tool.help.render( host_url="", static_path="" ))
add_doc_kwds['help'] = to_unicode( tool.help.render( host_url="", static_path="" ) )
except Exception:
# Don't fail to build index just because a help message
# won't render.
pass
writer.add_document( **add_doc_kwds )
writer.commit()
log.debug( 'Toolbox index finished.' )
def search( self, query, return_attribute='id' ):
def search( self, q, tool_name_boost, tool_section_boost, tool_description_boost, tool_help_boost, tool_search_limit ):
"""
Perform search on the in-memory index. Weight in the given boosts.
"""
# Change field boosts for searcher
searcher = self.index.searcher(
weighting=BM25F(
field_B={ 'title_B': 9,
'section_B': 3,
'description_B': 2,
'help_B': 0.5 }
field_B={ 'name_B': float( tool_name_boost ),
'section_B': float( tool_section_boost ),
'description_B': float( tool_description_boost ),
'help_B': float( tool_help_boost ) }
)
)
# Set query to search title, description, section, and help.
parser = MultifieldParser( [ 'title', 'description', 'section', 'help' ], schema=schema )
# Set query to search name, description, section, and help.
parser = MultifieldParser( [ 'name', 'description', 'section', 'help' ], schema=schema )
# Perform the search
hits = searcher.search( parser.parse( '*' + query + '*' ), limit=20 )
return [ hit[ return_attribute ] for hit in hits ]
hits = searcher.search( parser.parse( '*' + q + '*' ), limit=float( tool_search_limit ) )
return [ hit[ 'id' ] for hit in hits ]
+58 -2
View File
@@ -1,8 +1,10 @@
import urllib
from galaxy import exceptions
from galaxy.exceptions import RequestParameterInvalidException
from galaxy.exceptions import ObjectNotFound
from galaxy import web, util
from galaxy import managers
from galaxy.web import _future_expose_api_anonymous_and_sessionless as expose_api_anonymous_and_sessionless
from galaxy.web import _future_expose_api_anonymous
from galaxy.web import _future_expose_api
from galaxy.web.base.controller import BaseAPIController
@@ -34,6 +36,60 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
self.history_manager = managers.histories.HistoryManager( app )
self.hda_manager = managers.hdas.HDAManager( app )
@expose_api_anonymous_and_sessionless
def detect( self, trans, **kwds ):
"""
GET /api/tools/detect?full_id=toolshed.g2.bx.psu.edu/repos/blankenberg/naive_variant_caller/naive_variant_caller/0.0.1
Detect whether the tool with the given id is installed.
:param full_id: exact id of the tool
:type full_id: str
:return: flag indicating presence
"return type: bool
"""
full_id = kwds.get( 'full_id', None )
if full_id:
if self.app.toolbox.get_tool( full_id ):
return True
else:
return False
@expose_api_anonymous_and_sessionless
def search( self, trans, **kwds ):
"""
GET /api/tools/search?q=fastq
Perform the search on the given query.
Boosts and numer of results are configurable in galaxy.ini file.
:param q: the query to search with
:type q: str
:return: Dictionary containing the tools' ids of the best hits.
:return type: dict
"""
q = kwds.get( 'q', '' )
if len( q ) < 3:
raise RequestParameterInvalidException( 'The search term has to be at least 3 characters long.' )
tool_name_boost = self.app.config.get( 'tool_name_boost', 9 )
tool_section_boost = self.app.config.get( 'tool_section_boost', 3 )
tool_description_boost = self.app.config.get( 'tool_description_boost', 2 )
tool_help_boost = self.app.config.get( 'tool_help_boost', 0.5 )
tool_search_limit = self.app.config.get( 'tool_search_limit', 20 )
# log.debug(str(tool_name_boost), str(tool_section_boost), str(tool_description_boost), str(tool_help_boost), str(tool_search_limit))
results = self.app.toolbox_search.search( q=q,
tool_name_boost=tool_name_boost,
tool_section_boost=tool_section_boost,
tool_description_boost=tool_description_boost,
tool_help_boost=tool_help_boost,
tool_search_limit=tool_search_limit )
return results
@web.expose_api
def index( self, trans, **kwds ):
"""
@@ -267,7 +323,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
id = urllib.unquote_plus( id )
tool = self.app.toolbox.get_tool( id, tool_version )
if not tool or not tool.allow_user_access( user ):
raise exceptions.ObjectNotFound("Could not find tool with id '%s'" % id)
raise ObjectNotFound("Could not find tool with id '%s'" % id)
return tool
def _rerun_tool( self, trans, payload, **kwargs ):
+8
View File
@@ -208,6 +208,13 @@ def populate_api_routes( webapp, app ):
webapp.mapper.resource( 'remote_file', 'remote_files', path_prefix='/api' )
webapp.mapper.resource( 'group', 'groups', path_prefix='/api' )
webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
# =======================
# ====== TOOLS API ======
# =======================
webapp.mapper.connect( 'api_toolbox_search', '/api/tools/search', controller="tools", action='search', conditions=dict( method=[ "GET" ] ) )
webapp.mapper.connect( 'api_tool_detect', '/api/tools/detect', controller="tools", action='detect', conditions=dict( method=[ "GET" ] ) )
webapp.mapper.connect( '/api/tools/{id:.+?}/build', action='build', controller="tools" )
webapp.mapper.connect( '/api/tools/{id:.+?}/reload', action='reload', controller="tools" )
webapp.mapper.connect( '/api/tools/{id:.+?}/diagnostics', action='diagnostics', controller="tools" )
@@ -215,6 +222,7 @@ def populate_api_routes( webapp, app ):
webapp.mapper.connect( '/api/tools/{id:.+?}/download', action='download', controller="tools" )
webapp.mapper.connect( '/api/tools/{id:.+?}', action='show', controller="tools" )
webapp.mapper.resource( 'tool', 'tools', path_prefix='/api' )
webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
webapp.mapper.resource( 'genome', 'genomes', path_prefix='/api' )
webapp.mapper.resource( 'visualization', 'visualizations', path_prefix='/api' )
+3 -2
View File
@@ -31,8 +31,9 @@ $( function() {
}
// Start a new ajax-request in X ms
$("#search-spinner").show();
var tool_search_url = galaxy_config.root + 'api/tools/search';
this.timer = setTimeout(function () {
$.get(tool_search_url, { query: q }, function (data) {
$.get(tool_search_url, { q: q }, function (data) {
// input.removeClass(config.loadingClass);
// Show live-search if results and search-term aren't empty
$("#search-no-results").hide();
@@ -72,7 +73,7 @@ $( function() {
}
$("#search-spinner").hide();
}, "json" );
}, 200 );
}, 400 );
}
this.lastValue = this.value;
});
+2 -1
View File
@@ -368,6 +368,7 @@ var ToolSearch = Backbone.Model.extend({
*/
do_search: function() {
var query = this.attributes.query;
var tool_search_url = galaxy_config.root + 'api/tools/search';
// If query is too short, do not search.
if (query.length < this.attributes.min_chars_for_search) {
@@ -388,7 +389,7 @@ var ToolSearch = Backbone.Model.extend({
this.timer = setTimeout(function () {
// log the search to analytics
ga( 'send', 'pageview', galaxy_config.root + '?q=' + q );
$.get(self.attributes.search_url, { query: q }, function (data) {
$.get( tool_search_url, { q: q }, function ( data ) {
self.set("results", data);
$("#search-spinner").hide();
$("#search-clear-btn").show();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long