mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
expose toolbox search API, enable configuring it
make toolbox and workflowbox use it
This commit is contained in:
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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 ):
|
||||
|
||||
@@ -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' )
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user