Merge pull request #829 from guerler/tool_execution_000

Reduce handle inputs and remove update state from tools
This commit is contained in:
John Chilton
2015-10-07 16:24:49 +01:00
20 changed files with 186 additions and 1132 deletions
@@ -91,13 +91,6 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
}
},
/** Shows the final message (usually upon successful job submission)
*/
reciept: function($el) {
this.$el.empty();
this.$el.append($el);
},
/** Highlight and scroll to input element (currently only used for error notifications)
*/
highlight: function (input_id, message, silent) {
@@ -118,7 +118,7 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
console.debug(response);
// show error
var error_message = response.error || response.err_msg || 'Uncaught error.';
var error_message = ( response && response.error ) || ( response && response.err_msg ) || 'Uncaught error.';
if ( self.$el.is(':empty') ) {
self.$el.prepend((new Ui.Message({
message : error_message,
@@ -34,24 +34,23 @@ return {
data : job_def,
success : function(response) {
callback && callback();
form.reciept(ToolTemplate.success(response));
form.$el.replaceWith( ToolTemplate.success( response ) );
// begin checking the history for updates
self._refreshHdas();
},
error : function(response, response_full) {
error : function(response) {
callback && callback();
if (response && response.message && response.message.data) {
var error_messages = form.data.matchResponse(response.message.data);
console.debug(response);
if (response && response.err_data) {
var error_messages = form.data.matchResponse(response.err_data);
for (var input_id in error_messages) {
form.highlight(input_id, error_messages[input_id]);
break;
}
} else {
// show error message with details
console.debug(response);
response && form.modal.show({
form.modal.show({
title : 'Job submission failed',
body : ToolTemplate.error(job_def),
body : ( response && response.err_msg ) || ToolTemplate.error( job_def ),
buttons : {
'Close' : function() {
form.modal.hide();
+39 -447
View File
@@ -45,7 +45,7 @@ from galaxy.tools.test import parse_tests
from galaxy.tools.parser import get_tool_source
from galaxy.tools.parser.xml import XmlPageSource
from galaxy.tools.toolbox import AbstractToolBox
from galaxy.util import rst_to_html, string_as_bool, string_to_object
from galaxy.util import rst_to_html, string_as_bool
from galaxy.tools.parameters.meta import expand_meta_parameters
from galaxy.util.bunch import Bunch
from galaxy.util.expressions import ExpressionContext
@@ -451,7 +451,6 @@ class Tool( object, Dictifiable ):
requires_setting_metadata = True
default_tool_action = DefaultToolAction
dict_collection_visible_keys = ( 'id', 'name', 'version', 'description' )
default_template = ''
def __init__( self, config_file, tool_source, app, guid=None, repository_id=None, allow_code_files=True ):
"""Load a tool from the config named by `config_file`"""
@@ -1195,21 +1194,14 @@ class Tool( object, Dictifiable ):
# outputs?
return True
def new_state( self, trans, all_pages=False, history=None ):
def new_state( self, trans, history=None ):
"""
Create a new `DefaultToolState` for this tool. It will be initialized
with default values for inputs.
Only inputs on the first page will be initialized unless `all_pages` is
True, in which case all inputs regardless of page are initialized.
"""
state = DefaultToolState()
state.inputs = {}
if all_pages:
inputs = self.inputs
else:
inputs = self.inputs_by_page[ 0 ]
self.fill_in_new_state( trans, inputs, state.inputs, history=history )
self.fill_in_new_state( trans, self.inputs, state.inputs, history=history )
return state
def fill_in_new_state( self, trans, inputs, state, context=None, history=None ):
@@ -1274,101 +1266,70 @@ class Tool( object, Dictifiable ):
else:
input.visit_inputs( "", value[input.name], callback )
def handle_input( self, trans, incoming, history=None, old_errors=None, process_state='update', source='html' ):
def handle_input( self, trans, incoming, history=None, source='html' ):
"""
Process incoming parameters for this tool from the dict `incoming`,
update the tool state (or create if none existed), and either return
to the form or execute the tool (only if 'execute' was clicked and
there were no errors).
process_state can be either 'update' (to incrementally build up the state
over several calls - one repeat per handle for instance) or 'populate'
force a complete build of the state and submission all at once (like
from API). May want an incremental version of the API also at some point,
that is why this is not just called for_api.
"""
all_pages = ( process_state == "populate" ) # If process_state = update, handle all pages at once.
rerun_remap_job_id = None
if 'rerun_remap_job_id' in incoming:
try:
rerun_remap_job_id = trans.app.security.decode_id( incoming[ 'rerun_remap_job_id' ] )
except Exception, exception:
log.error( str( exception ) )
message = 'Failure executing tool (attempting to rerun invalid job).'
return 'message.mako', dict( status='error', message=message, refresh_frames=[] )
raise exceptions.MessageException( 'Failure executing tool (attempting to rerun invalid job).' )
# Fixed set of input parameters may correspond to any number of jobs.
# Expand these out to individual parameters for given jobs (tool
# executions).
# Expand these out to individual parameters for given jobs (tool executions).
expanded_incomings, collection_info = expand_meta_parameters( trans, self, incoming )
if not expanded_incomings:
raise exceptions.MessageException( "Tool execution failed, trying to run a tool over an empty collection." )
raise exceptions.MessageException( 'Tool execution failed, trying to run a tool over an empty collection.' )
# Remapping a single job to many jobs doesn't make sense, so disable
# remap if multi-runs of tools are being used.
if rerun_remap_job_id and len( expanded_incomings ) > 1:
message = 'Failure executing tool (cannot create multiple jobs when remapping existing job).'
return 'message.mako', dict( status='error', message=message, refresh_frames=[] )
all_states = []
for expanded_incoming in expanded_incomings:
state, state_new = self.__fetch_state( trans, expanded_incoming, history, all_pages=all_pages )
all_states.append( state )
if state_new:
# This feels a bit like a hack. It allows forcing full processing
# of inputs even when there is no state in the incoming dictionary
# by providing either 'runtool_btn' (the name of the submit button
# on the standard run form) or "URL" (a parameter provided by
# external data source tools).
if "runtool_btn" not in incoming and "URL" not in incoming:
if not self.display_interface:
return self.__no_display_interface_response()
if len(incoming):
self.update_state( trans, self.inputs_by_page[state.page], state.inputs, incoming, old_errors=old_errors or {}, source=source )
return self.default_template, dict( errors={}, tool_state=state, param_values={}, incoming={} )
raise exceptions.MessageException( 'Failure executing tool (cannot create multiple jobs when remapping existing job).' )
all_errors = []
all_params = []
for expanded_incoming, expanded_state in zip(expanded_incomings, all_states):
errors, params = self.__check_param_values( trans, expanded_incoming, expanded_state, old_errors, process_state, history=history, source=source )
for expanded_incoming in expanded_incomings:
expanded_state = self.new_state( trans, history=history )
# Process incoming data
if not self.check_values:
# If `self.check_values` is false we don't do any checking or
# processing on input This is used to pass raw values
# through to/from external sites.
errors = {}
params = expanded_incoming
else:
# Update state for all inputs on the current page taking new
# values from `incoming`.
errors = self.populate_state( trans, self.inputs, expanded_state.inputs, expanded_incoming, history, source=source )
# If the tool provides a `validate_input` hook, call it.
validate_input = self.get_hook( 'validate_input' )
if validate_input:
validate_input( trans, errors, expanded_state.inputs, self.inputs )
params = expanded_state.inputs
all_errors.append( errors )
all_params.append( params )
if self.__should_refresh_state( incoming ):
template, template_vars = self.__handle_state_refresh( trans, state, errors )
# If there were errors, we stay on the same page and display
# error messages
if any( all_errors ):
raise exceptions.MessageException( err_data=all_errors[ 0 ] )
else:
# User actually clicked next or execute.
# If there were errors, we stay on the same page and display
# error messages
if any( all_errors ):
error_message = "One or more errors were found in the input you provided. The specific errors are marked below."
template = self.default_template
template_vars = dict( errors=errors, tool_state=state, incoming=incoming, error_message=error_message )
# If we've completed the last page we can execute the tool
elif all_pages or state.page == self.last_page:
execution_tracker = execute_job( trans, self, all_params, history=history, rerun_remap_job_id=rerun_remap_job_id, collection_info=collection_info )
if execution_tracker.successful_jobs:
template = 'tool_executed.mako'
template_vars = dict(
out_data=execution_tracker.output_datasets,
num_jobs=len( execution_tracker.successful_jobs ),
job_errors=execution_tracker.execution_errors,
jobs=execution_tracker.successful_jobs,
output_collections=execution_tracker.output_collections,
implicit_collections=execution_tracker.implicit_collections,
)
else:
template = 'message.mako'
template_vars = dict( status='error', message=execution_tracker.execution_errors[0], refresh_frames=[] )
# Otherwise move on to the next page
execution_tracker = execute_job( trans, self, all_params, history=history, rerun_remap_job_id=rerun_remap_job_id, collection_info=collection_info )
if execution_tracker.successful_jobs:
return dict( out_data=execution_tracker.output_datasets,
num_jobs=len( execution_tracker.successful_jobs ),
job_errors=execution_tracker.execution_errors,
jobs=execution_tracker.successful_jobs,
output_collections=execution_tracker.output_collections,
implicit_collections=execution_tracker.implicit_collections )
else:
template, template_vars = self.__handle_page_advance( trans, state, errors )
return template, template_vars
def __should_refresh_state( self, incoming ):
return not( 'runtool_btn' in incoming or 'URL' in incoming or 'ajax_upload' in incoming )
raise exceptions.MessageException( execution_tracker.execution_errors[ 0 ] )
def handle_single_execution( self, trans, rerun_remap_job_id, params, history, mapping_over_collection ):
"""
@@ -1394,72 +1355,6 @@ class Tool( object, Dictifiable ):
message = 'Failure executing tool (invalid data returned from tool execution)'
return False, message
def __handle_state_refresh( self, trans, state, errors ):
try:
self.find_fieldstorage( state.inputs )
except InterruptedUpload:
# If inputs contain a file it won't persist. Most likely this
# is an interrupted upload. We should probably find a more
# standard method of determining an incomplete POST.
return self.handle_interrupted( trans, state.inputs )
except:
pass
# Just a refresh, render the form with updated state and errors.
if not self.display_interface:
return self.__no_display_interface_response()
return self.default_template, dict( errors=errors, tool_state=state )
def __handle_page_advance( self, trans, state, errors ):
state.page += 1
# Fill in the default values for the next page
self.fill_in_new_state( trans, self.inputs_by_page[ state.page ], state.inputs )
if not self.display_interface:
return self.__no_display_interface_response()
return self.default_template, dict( errors=errors, tool_state=state )
def __no_display_interface_response( self ):
return 'message.mako', dict( status='info', message="The interface for this tool cannot be displayed", refresh_frames=['everything'] )
def __fetch_state( self, trans, incoming, history, all_pages ):
# Get the state or create if not found
if "tool_state" in incoming:
encoded_state = string_to_object( incoming["tool_state"] )
state = DefaultToolState()
state.decode( encoded_state, self, trans.app )
new = False
else:
state = self.new_state( trans, history=history, all_pages=all_pages )
new = True
return state, new
def __check_param_values( self, trans, incoming, state, old_errors, process_state, history, source ):
# Process incoming data
if not self.check_values:
# If `self.check_values` is false we don't do any checking or
# processing on input This is used to pass raw values
# through to/from external sites. FIXME: This should be handled
# more cleanly, there is no reason why external sites need to
# post back to the same URL that the tool interface uses.
errors = {}
params = incoming
else:
# Update state for all inputs on the current page taking new
# values from `incoming`.
if process_state == "update":
inputs = self.inputs_by_page[state.page]
errors = self.update_state( trans, inputs, state.inputs, incoming, old_errors=old_errors or {}, source=source )
elif process_state == "populate":
inputs = self.inputs
errors = self.populate_state( trans, inputs, state.inputs, incoming, history, source=source )
else:
raise Exception("Unknown process_state type %s" % process_state)
# If the tool provides a `validate_input` hook, call it.
validate_input = self.get_hook( 'validate_input' )
if validate_input:
validate_input( trans, errors, state.inputs, inputs )
params = state.inputs
return errors, params
def find_fieldstorage( self, x ):
if isinstance( x, FieldStorage ):
raise InterruptedUpload( None )
@@ -1468,37 +1363,6 @@ class Tool( object, Dictifiable ):
elif isinstance(x, list):
[ self.find_fieldstorage( y ) for y in x ]
def handle_interrupted( self, trans, inputs ):
"""
Upon handling inputs, if it appears that we have received an incomplete
form, do some cleanup or anything else deemed necessary. Currently
this is only likely during file uploads, but this method could be
generalized and a method standardized for handling other tools.
"""
# If the async upload tool has uploading datasets, we need to error them.
if 'async_datasets' in inputs and inputs['async_datasets'] not in [ 'None', '', None ]:
for id in inputs['async_datasets'].split(','):
try:
data = self.sa_session.query( trans.model.HistoryDatasetAssociation ).get( int( id ) )
except:
log.exception( 'Unable to load precreated dataset (%s) sent in upload form' % id )
continue
if trans.user is None and trans.galaxy_session.current_history != data.history:
log.error( 'Got a precreated dataset (%s) but it does not belong to anonymous user\'s current session (%s)'
% ( data.id, trans.galaxy_session.id ) )
elif data.history.user != trans.user:
log.error( 'Got a precreated dataset (%s) but it does not belong to current user (%s)'
% ( data.id, trans.user.id ) )
else:
data.state = data.states.ERROR
data.info = 'Upload of this dataset was interrupted. Please try uploading again or'
self.sa_session.add( data )
self.sa_session.flush()
# It's unlikely the user will ever see this.
return 'message.mako', dict( status='error',
message='Your upload was interrupted. If this was uninentional, please retry it.',
refresh_frames=[], cont=None )
def populate_state( self, trans, inputs, state, incoming, history=None, source="html", prefix="", context=None ):
errors = dict()
# Push this level onto the context stack
@@ -1643,260 +1507,6 @@ class Tool( object, Dictifiable ):
state[ input.name ] = value
return errors
def update_state( self, trans, inputs, state, incoming, source='html', prefix="", context=None,
update_only=False, old_errors={}, item_callback=None ):
"""
Update the tool state in `state` using the user input in `incoming`.
This is designed to be called recursively: `inputs` contains the
set of inputs being processed, and `prefix` specifies a prefix to
add to the name of each input to extract its value from `incoming`.
If `update_only` is True, values that are not in `incoming` will
not be modified. In this case `old_errors` can be provided, and any
errors for parameters which were *not* updated will be preserved.
"""
errors = dict()
# Push this level onto the context stack
context = ExpressionContext( state, context )
# Iterate inputs and update (recursively)
for input in inputs.itervalues():
key = prefix + input.name
if isinstance( input, Repeat ):
group_state = state[input.name]
# Create list of empty errors for each previously existing state
group_errors = [ {} for i in range( len( group_state ) ) ]
group_old_errors = old_errors.get( input.name, None )
any_group_errors = False
# Check any removals before updating state -- only one
# removal can be performed, others will be ignored
for i, rep_state in enumerate( group_state ):
rep_index = rep_state['__index__']
if key + "_" + str(rep_index) + "_remove" in incoming:
if len( group_state ) > input.min:
del group_state[i]
del group_errors[i]
if group_old_errors:
del group_old_errors[i]
break
else:
group_errors[i] = { '__index__': 'Cannot remove repeat (min size=%i).' % input.min }
any_group_errors = True
# Only need to find one that can't be removed due to size, since only
# one removal is processed at # a time anyway
break
elif group_old_errors and group_old_errors[i]:
group_errors[i] = group_old_errors[i]
any_group_errors = True
# Update state
max_index = -1
for i, rep_state in enumerate( group_state ):
rep_index = rep_state['__index__']
max_index = max( max_index, rep_index )
rep_prefix = "%s_%d|" % ( key, rep_index )
if group_old_errors:
rep_old_errors = group_old_errors[i]
else:
rep_old_errors = {}
rep_errors = self.update_state( trans,
input.inputs,
rep_state,
incoming,
source=source,
prefix=rep_prefix,
context=context,
update_only=update_only,
old_errors=rep_old_errors,
item_callback=item_callback )
if rep_errors:
any_group_errors = True
group_errors[i].update( rep_errors )
# Check for addition
if key + "_add" in incoming:
if len( group_state ) < input.max:
new_state = {}
new_state['__index__'] = max_index + 1
self.fill_in_new_state( trans, input.inputs, new_state, context )
group_state.append( new_state )
group_errors.append( {} )
else:
group_errors[-1] = { '__index__': 'Cannot add repeat (max size=%i).' % input.max }
any_group_errors = True
# Were there *any* errors for any repetition?
if any_group_errors:
errors[input.name] = group_errors
elif isinstance( input, Conditional ):
group_state = state[input.name]
group_old_errors = old_errors.get( input.name, {} )
old_current_case = group_state['__current_case__']
group_prefix = "%s|" % ( key )
# Deal with the 'test' element and see if its value changed
if input.value_ref and not input.value_ref_in_group:
# We are referencing an existent parameter, which is not
# part of this group
test_param_key = prefix + input.test_param.name
else:
test_param_key = group_prefix + input.test_param.name
test_param_error = None
test_incoming = get_incoming_value( incoming, test_param_key, None )
if test_param_key not in incoming \
and "__force_update__" + test_param_key not in incoming \
and update_only:
# Update only, keep previous value and state, but still
# recurse in case there are nested changes
value = group_state[ input.test_param.name ]
current_case = old_current_case
if input.test_param.name in old_errors:
errors[ input.test_param.name ] = old_errors[ input.test_param.name ]
else:
# Get value of test param and determine current case
value, test_param_error = \
check_param( trans, input.test_param, test_incoming, context, source=source )
try:
current_case = input.get_current_case( value, trans )
except ValueError, e:
if input.is_job_resource_conditional:
# Unless explicitly given job resource parameters
# (e.g. from the run tool form) don't populate the
# state. Along with other hacks prevents workflow
# saving from populating resource defaults - which
# are meant to be much more transient than the rest
# of tool state.
continue
# load default initial value
if not test_param_error:
test_param_error = str( e )
if trans is not None:
history = trans.get_history()
else:
history = None
value = input.test_param.get_initial_value( trans, context, history=history )
current_case = input.get_current_case( value, trans )
case_changed = current_case != old_current_case
if case_changed:
# Current case has changed, throw away old state
group_state = state[input.name] = {}
# TODO: we should try to preserve values if we can
self.fill_in_new_state( trans, input.cases[current_case].inputs, group_state, context )
group_errors = dict()
group_old_errors = dict()
# If we didn't just change the current case and are coming from HTML - the values
# in incoming represent the old values and should not be replaced. If being updated
# from the API (json) instead of HTML - form values below the current case
# may also be supplied and incoming should be preferred to case defaults.
if (not case_changed) or (source != "html"):
# Current case has not changed, update children
group_errors = self.update_state( trans,
input.cases[current_case].inputs,
group_state,
incoming,
prefix=group_prefix,
context=context,
source=source,
update_only=update_only,
old_errors=group_old_errors,
item_callback=item_callback )
if input.test_param.name in group_old_errors and not test_param_error:
test_param_error = group_old_errors[ input.test_param.name ]
if test_param_error:
group_errors[ input.test_param.name ] = test_param_error
if group_errors:
errors[ input.name ] = group_errors
# Store the current case in a special value
group_state['__current_case__'] = current_case
# Store the value of the test element
group_state[ input.test_param.name ] = value
elif isinstance( input, Section ):
group_state = state[input.name]
group_old_errors = old_errors.get( input.name, {} )
group_prefix = "%s|" % ( key )
group_errors = self.update_state( trans,
input.inputs,
group_state,
incoming,
prefix=group_prefix,
context=context,
source=source,
update_only=update_only,
old_errors=group_old_errors,
item_callback=item_callback )
if group_errors:
errors[ input.name ] = group_errors
elif isinstance( input, UploadDataset ):
group_state = state[input.name]
group_errors = []
group_old_errors = old_errors.get( input.name, None )
any_group_errors = False
d_type = input.get_datatype( trans, context )
writable_files = d_type.writable_files
# remove extra files
while len( group_state ) > len( writable_files ):
del group_state[-1]
if group_old_errors:
del group_old_errors[-1]
# Update state
max_index = -1
for i, rep_state in enumerate( group_state ):
rep_index = rep_state['__index__']
max_index = max( max_index, rep_index )
rep_prefix = "%s_%d|" % ( key, rep_index )
if group_old_errors:
rep_old_errors = group_old_errors[i]
else:
rep_old_errors = {}
rep_errors = self.update_state( trans,
input.inputs,
rep_state,
incoming,
prefix=rep_prefix,
context=context,
source=source,
update_only=update_only,
old_errors=rep_old_errors,
item_callback=item_callback )
if rep_errors:
any_group_errors = True
group_errors.append( rep_errors )
else:
group_errors.append( {} )
# Add new fileupload as needed
offset = 1
while len( writable_files ) > len( group_state ):
new_state = {}
new_state['__index__'] = max_index + offset
offset += 1
self.fill_in_new_state( trans, input.inputs, new_state, context )
group_state.append( new_state )
if any_group_errors:
group_errors.append( {} )
# Were there *any* errors for any repetition?
if any_group_errors:
errors[input.name] = group_errors
else:
if key not in incoming \
and "__force_update__" + key not in incoming \
and update_only:
# No new value provided, and we are only updating, so keep
# the old value (which should already be in the state) and
# preserve the old error message.
if input.name in old_errors:
errors[ input.name ] = old_errors[ input.name ]
else:
incoming_value = get_incoming_value( incoming, key, None )
value, error = check_param( trans, input, incoming_value, context, source=source )
# If a callback was provided, allow it to process the value
input_name = input.name
if item_callback:
old_value = state.get( input_name, None )
value, error = item_callback( trans, key, input, value, error, old_value, context )
if error:
errors[ input_name ] = error
state[ input_name ] = value
meta_properties = self.__meta_properties_for_state( key, incoming, incoming_value, value, input_name )
state.update( meta_properties )
return errors
def __remove_meta_properties( self, incoming ):
result = incoming.copy()
meta_property_suffixes = [
@@ -1908,19 +1518,6 @@ class Tool( object, Dictifiable ):
del result[ key ]
return result
def __meta_properties_for_state( self, key, incoming, incoming_val, state_val, input_name ):
meta_properties = {}
meta_property_suffixes = [
"__multirun__",
"__collection_multirun__",
]
for meta_property_suffix in meta_property_suffixes:
multirun_key = "%s|%s" % ( key, meta_property_suffix )
if multirun_key in incoming:
multi_value = incoming[ multirun_key ]
meta_properties[ "%s|%s" % ( input_name, meta_property_suffix ) ] = multi_value
return meta_properties
@property
def params_with_missing_data_table_entry( self ):
"""
@@ -2762,11 +2359,6 @@ class Tool( object, Dictifiable ):
except Exception, error:
trans.response.status = 500
return { 'error': str(error) }
# can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now
# if not self.is_workflow_compatible:
# trans.response.status = 500
# return { 'error': 'The \'%s\' tool does currently not support re-running.' % self.name }
return message
def get_default_history_by_trans( self, trans, create=False ):
@@ -477,7 +477,7 @@ class LibraryDatasetsController( BaseAPIController, UsesVisualizationMixin ):
tool_id = 'upload1'
tool = trans.app.toolbox.get_tool( tool_id )
state = tool.new_state( trans )
tool.update_state( trans, tool.inputs_by_page[ 0 ], state.inputs, kwd )
tool.populate_state( trans, tool.inputs, state.inputs, kwd )
tool_params = state.inputs
dataset_upload_inputs = []
for input in tool.inputs.itervalues():
+11 -34
View File
@@ -1,7 +1,6 @@
import urllib
from galaxy.exceptions import ObjectNotFound
from galaxy.exceptions import InternalServerError
from galaxy import exceptions
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
@@ -72,7 +71,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
try:
return self.app.toolbox.to_dict( trans, in_panel=in_panel, trackster=trackster)
except Exception:
raise InternalServerError( "Error: Could not convert toolbox to dictionary" )
raise exceptions.InternalServerError( "Error: Could not convert toolbox to dictionary" )
@expose_api_anonymous_and_sessionless
def show( self, trans, id, **kwd ):
@@ -206,7 +205,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.tgz"' % ( id )
return download_file
@web.expose_api_anonymous
@expose_api_anonymous
def create( self, trans, payload, **kwd ):
"""
POST /api/tools
@@ -223,13 +222,12 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
tool_version = payload.get( 'tool_version', None )
tool = trans.app.toolbox.get_tool( payload[ 'tool_id' ] , tool_version ) if 'tool_id' in payload else None
if not tool or not tool.allow_user_access( trans.user ):
trans.response.status = 404
return { "message": { "type": "error", "text" : trans.app.model.Dataset.conversion_messages.NO_TOOL } }
raise exceptions.MessageException( 'Tool not found or not accessible.' )
# Set running history from payload parameters.
# History not set correctly as part of this API call for
# dataset upload.
history_id = payload.get("history_id", None)
history_id = payload.get('history_id', None)
if history_id:
decoded_id = self.decode_id( history_id )
target_history = self.history_manager.get_owned( decoded_id, trans.user, current_history=trans.history )
@@ -240,7 +238,7 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
inputs = payload.get( 'inputs', {} )
# Find files coming in as multipart file data and add to inputs.
for k, v in payload.iteritems():
if k.startswith("files_") or k.startswith("__files_"):
if k.startswith('files_') or k.startswith('__files_'):
inputs[k] = v
# for inputs that are coming from the Library, copy them into the history
@@ -254,43 +252,22 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
for k, v in input_patch.iteritems():
inputs[k] = v
# HACK: add run button so that tool.handle_input will run tool.
inputs['runtool_btn'] = 'Execute'
# TODO: encode data ids and decode ids.
# TODO: handle dbkeys
params = util.Params( inputs, sanitize=False )
# process_state will be 'populate' or 'update'. When no tool
# state is specified in input - it will be 'populate', and
# tool will fully expand repeat and conditionals when building
# up state. If tool state is found in input
# parameters,process_state will be 'update' and complex
# submissions (with repeats and conditionals) must be built up
# over several iterative calls to the API - mimicing behavior
# of web controller (though frankly API never returns
# tool_state so this "legacy" behavior is probably impossible
# through API currently).
incoming = params.__dict__
process_state = "update" if "tool_state" in incoming else "populate"
template, vars = tool.handle_input( trans, incoming, history=target_history, process_state=process_state, source="json" )
if 'errors' in vars:
trans.response.status = 400
return { "message": { "type": "error", "data" : vars[ 'errors' ] } }
vars = tool.handle_input( trans, incoming, history=target_history, source='json' )
# TODO: check for errors and ensure that output dataset(s) are available.
output_datasets = vars.get( 'out_data', [] )
rval = {
"outputs": [],
"output_collections": [],
"jobs": [],
"implicit_collections": [],
}
rval = { 'outputs': [], 'output_collections': [], 'jobs': [], 'implicit_collections': [] }
job_errors = vars.get( 'job_errors', [] )
if job_errors:
# If we are here - some jobs were successfully executed but some failed.
rval[ "errors" ] = job_errors
rval[ 'errors' ] = job_errors
outputs = rval[ "outputs" ]
outputs = rval[ 'outputs' ]
# TODO:?? poss. only return ids?
for output_name, output in output_datasets:
output_dict = output.to_dict()
@@ -324,7 +301,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 ObjectNotFound("Could not find tool with id '%s'" % id)
raise exceptions.ObjectNotFound("Could not find tool with id '%s'" % id)
return tool
def _rerun_tool( self, trans, payload, **kwargs ):
@@ -5,7 +5,7 @@ Upload class
import logging
import urllib
from galaxy import jobs, util, web
from galaxy import jobs, web
from galaxy.util import Params
from galaxy.util.hash_util import hmac_new
from galaxy.web.base.controller import BaseUIController
@@ -32,7 +32,6 @@ class ASync( BaseUIController ):
if not kwd:
return trans.response.send_redirect( "/index" )
history = trans.get_history( create=True )
params = Params(kwd, sanitize=False)
STATUS = params.STATUS
URL = params.URL
@@ -143,4 +142,4 @@ class ASync( BaseUIController ):
trans.sa_session.flush()
return trans.fill_template( 'tool_executed.mako', history=history, toolbox=toolbox, tool=tool, util=util, out_data={}, num_jobs=1, job_errors=[] )
return trans.fill_template( 'root/tool_runner.mako', out_data={}, num_jobs=1, job_errors=[] )
@@ -1044,7 +1044,7 @@ class LibraryCommon( BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMet
tool_id = 'upload1'
tool = trans.app.toolbox.get_tool( tool_id )
state = tool.new_state( trans )
tool.update_state( trans, tool.inputs_by_page[0], state.inputs, kwd )
tool.populate_state( trans, tool.inputs, state.inputs, kwd )
tool_params = state.inputs
dataset_upload_inputs = []
for input_name, input in tool.inputs.iteritems():
@@ -1,5 +1,5 @@
"""
Upload class
Controller handles external tool related requests
"""
import logging
@@ -9,23 +9,13 @@ from markupsafe import escape
import galaxy.util
from galaxy import web
from galaxy.tools import DefaultToolState
from galaxy.tools import DataSourceTool
from galaxy.tools.actions import upload_common
from galaxy.util.bunch import Bunch
from galaxy.web import error, url_for
from galaxy.web.base.controller import BaseUIController
log = logging.getLogger( __name__ )
class AddFrameData:
def __init__( self ):
self.wiki_url = None
self.debug = None
self.from_noframe = None
class ToolRunner( BaseUIController ):
# Hack to get biomart to work, ideally, we could pass tool_id to biomart and receive it back
@@ -45,71 +35,48 @@ class ToolRunner( BaseUIController ):
"""Catches the tool id and redirects as needed"""
return self.index(trans, tool_id=tool_id, **kwd)
def __get_tool_components( self, tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=False ):
return self.get_toolbox().get_tool_components( tool_id, tool_version, get_loaded_tools_by_lineage, set_selected )
def __get_tool( self, tool_id, tool_version=None, get_loaded_tools_by_lineage=False, set_selected=False ):
tool_version_select_field, tools, tool = self.get_toolbox().get_tool_components( tool_id, tool_version, get_loaded_tools_by_lineage, set_selected )
return tool
@web.expose
def index( self, trans, tool_id=None, from_noframe=None, **kwd ):
# No tool id passed, redirect to main page
# tool id not available, redirect to main page
if tool_id is None:
return trans.response.send_redirect( url_for( controller="root", action="welcome" ) )
tool_version_select_field, tools, tool = self.__get_tool_components( tool_id )
# No tool matching the tool id, display an error (shouldn't happen)
return trans.response.send_redirect( url_for( controller='root', action='welcome' ) )
tool = self.__get_tool( tool_id )
# tool id is not matching, display an error
if not tool or not tool.allow_user_access( trans.user ):
log.error( "index called with tool id '%s' but no such tool exists", tool_id )
trans.log_event( "Tool id '%s' does not exist" % tool_id )
log.error( 'index called with tool id \'%s\' but no such tool exists', tool_id )
trans.log_event( 'Tool id \'%s\' does not exist' % tool_id )
trans.response.status = 404
return trans.show_error_message("Tool '%s' does not exist." % ( escape(tool_id) ))
return trans.show_error_message('Tool \'%s\' does not exist.' % ( escape(tool_id) ))
if tool.require_login and not trans.user:
message = "You must be logged in to use this tool."
status = "info"
redirect = url_for( controller='tool_runner', action='index', tool_id=tool_id, **kwd )
return trans.response.send_redirect( url_for( controller='user',
action='login',
cntrller='user',
message=message,
status=status,
status='info',
message='You must be logged in to use this tool.',
redirect=redirect ) )
if tool.tool_type == 'default':
return trans.response.send_redirect( url_for( controller="root", tool_id=tool_id ) )
return trans.response.send_redirect( url_for( controller='root', tool_id=tool_id ) )
def _validated_params_for( kwd ):
params = galaxy.util.Params( kwd, sanitize=False ) # Sanitize parameters when substituting into command line via input wrappers
# do param translation here, used by datasource tools
if tool.input_translator:
tool.input_translator.translate( params )
return params
params = _validated_params_for( kwd )
# execute tool without displaying form (used for datasource tools)
params = galaxy.util.Params( kwd, sanitize=False )
# do param translation here, used by datasource tools
if tool.input_translator:
tool.input_translator.translate( params )
# We may be visiting Galaxy for the first time ( e.g., sending data from UCSC ),
# so make sure to create a new history if we've never had one before.
history = tool.get_default_history_by_trans( trans, create=True )
try:
template, vars = tool.handle_input( trans, params.__dict__ )
except KeyError:
# This error indicates (or at least can indicate) there was a
# problem with the stored tool_state - it is incompatible with
# this variant of the tool - possibly because the tool changed
# or because the tool version changed.
del kwd[ "tool_state" ]
params = _validated_params_for( kwd )
template, vars = tool.handle_input( trans, params.__dict__ )
vars = tool.handle_input( trans, params.__dict__, history=history )
except Exception, e:
error( str( e ) )
if len( params ) > 0:
trans.log_event( "Tool params: %s" % ( str( params ) ), tool_id=tool_id )
add_frame = AddFrameData()
add_frame.debug = trans.debug
if from_noframe is not None:
add_frame.wiki_url = trans.app.config.wiki_url
add_frame.from_noframe = True
return trans.fill_template( template,
history=history,
toolbox=self.get_toolbox(),
tool_version_select_field=tool_version_select_field,
tool=tool,
util=galaxy.util,
add_frame=add_frame,
form_input_auto_focus=True,
**vars )
trans.log_event( 'Tool params: %s' % ( str( params ) ), tool_id=tool_id )
return trans.fill_template( 'root/tool_runner.mako', **vars )
@web.expose
def rerun( self, trans, id=None, job_id=None, **kwd ):
@@ -118,7 +85,6 @@ class ToolRunner( BaseUIController ):
the dataset, extract the parameters, and display the appropriate tool
form with parameters already filled in.
"""
if job_id is None:
if not id:
error( "'id' parameter is required" )
@@ -155,10 +121,7 @@ class ToolRunner( BaseUIController ):
"""
if tool_id is None:
return trans.response.send_redirect( url_for( controller="root", action="welcome" ) )
tool_version_select_field, tools, tool = self.__get_tool_components( tool_id,
tool_version=None,
get_loaded_tools_by_lineage=False,
set_selected=False )
tool = self.__get_tool( tool_id )
# No tool matching the tool id, display an error (shouldn't happen)
if not tool:
log.error( "data_source_redirect called with tool id '%s' but no such tool exists", tool_id )
@@ -178,95 +141,3 @@ class ToolRunner( BaseUIController ):
return trans.show_error_message( "Required URL for redirection missing" )
trans.log_event( "Redirecting to: %s" % redirect_url )
return trans.fill_template( 'root/redirect.mako', redirect_url=redirect_url )
@web.json
def upload_async_create( self, trans, tool_id=None, **kwd ):
"""
Precreate datasets for asynchronous uploading.
"""
cntrller = kwd.get( 'cntrller', '' )
roles = kwd.get( 'roles', False )
if roles:
# The user associated the DATASET_ACCESS permission on the uploaded datasets with 1 or more roles.
# We need to ensure that the roles are legitimately derived from the roles associated with the LIBRARY_ACCESS
# permission if the library is not public ( this should always be the case since any ill-legitimate roles
# were filtered out of the roles displayed on the upload form. In addition, we need to ensure that the user
# did not associated roles that would make the dataset in-accessible by everyone.
library_id = trans.app.security.decode_id( kwd.get( 'library_id', '' ) )
vars = dict( DATASET_ACCESS_in=roles )
permissions, in_roles, error, msg = trans.app.security_agent.derive_roles_from_access( trans, library_id, cntrller, library=True, **vars )
if error:
return [ 'error', msg ]
def create_dataset( name ):
ud = Bunch( name=name, file_type=None, dbkey=None )
if nonfile_params.get( 'folder_id', False ):
replace_id = nonfile_params.get( 'replace_id', None )
if replace_id not in [ None, 'None' ]:
replace_dataset = trans.sa_session.query( trans.app.model.LibraryDataset ).get( trans.security.decode_id( replace_id ) )
else:
replace_dataset = None
# FIXME: instead of passing params here ( chiech have been process by util.Params(), the original kwd
# should be passed so that complex objects that may have been included in the initial request remain.
library_bunch = upload_common.handle_library_params( trans, nonfile_params, nonfile_params.folder_id, replace_dataset )
else:
library_bunch = None
return upload_common.new_upload( trans, cntrller, ud, library_bunch=library_bunch, state=trans.app.model.HistoryDatasetAssociation.states.UPLOAD )
tool = self.get_toolbox().get_tool( tool_id )
if not tool:
return False # bad tool_id
nonfile_params = galaxy.util.Params( kwd, sanitize=False )
if kwd.get( 'tool_state', None ) not in ( None, 'None' ):
encoded_state = galaxy.util.string_to_object( kwd["tool_state"] )
tool_state = DefaultToolState()
tool_state.decode( encoded_state, tool, trans.app )
else:
tool_state = tool.new_state( trans )
tool.update_state( trans, tool.inputs, tool_state.inputs, kwd, update_only=True )
datasets = []
dataset_upload_inputs = []
for input_name, input in tool.inputs.iteritems():
if input.type == "upload_dataset":
dataset_upload_inputs.append( input )
assert dataset_upload_inputs, Exception( "No dataset upload groups were found." )
for dataset_upload_input in dataset_upload_inputs:
d_type = dataset_upload_input.get_datatype( trans, kwd )
if d_type.composite_type is not None:
datasets.append( create_dataset( dataset_upload_input.get_composite_dataset_name( kwd ) ) )
else:
params = Bunch( ** tool_state.inputs[dataset_upload_input.name][0] )
if params.file_data not in [ None, "" ]:
name = params.file_data
if name.count('/'):
name = name.rsplit('/', 1)[1]
if name.count('\\'):
name = name.rsplit('\\', 1)[1]
datasets.append( create_dataset( name ) )
if params.url_paste not in [ None, "" ]:
url_paste = params.url_paste.replace( '\r', '' ).split( '\n' )
url = False
for line in url_paste:
line = line.rstrip( '\r\n' ).strip()
if not line:
continue
elif line.lower().startswith( 'http://' ) or line.lower().startswith( 'ftp://' ) or line.lower().startswith( 'https://' ):
url = True
datasets.append( create_dataset( line ) )
else:
if url:
continue # non-url when we've already processed some urls
else:
# pasted data
datasets.append( create_dataset( 'Pasted Entry' ) )
break
return [ d.id for d in datasets ]
@web.expose
def upload_async_message( self, trans, **kwd ):
# might be more appropriate in a different controller
msg = """<p>Your upload has been queued. History entries that are still uploading will be blue, and turn green upon completion.</p>
<p><b>Please do not use your browser\'s "stop" or "reload" buttons until the upload is complete, or it may be interrupted.</b></p>
<p>You may safely continue to use Galaxy while the upload is in progress. Using "stop" and "reload" on pages other than Galaxy is also safe.</p>
"""
# return trans.show_message( msg, refresh_frames=[ 'history' ] )
return trans.show_message( msg )
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"form-view.js","sources":["../../../src/mvc/form/form-view.js"],"names":["define","Utils","Portlet","Ui","FormSection","FormData","Backbone","View","extend","initialize","options","this","optionsDefault","is_workflow","initial_errors","cls","merge","console","debug","galaxy","parent","Galaxy","modal","Modal","setElement","_build","update","new_model","self","data","matchModel","input_id","node","input","input_list","_","isEqual","field","field_list","new_options","indexOf","type","i","opt","length","push","label","value","trigger","wait","active","is_dynamic","unwait","reciept","$el","empty","append","highlight","message","silent","input_element","element_list","error","top","$panel","parents","filter","$","css","first","animate","scrollTop","offset","errors","error_messages","matchResponse","off","_renderForm","create","current_check","checksum","on","new_check","onchange","reset","Message","section","inputs","remove","portlet","icon","title","operations","buttons","addClass","persistent","status"],"mappings":"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"}
{"version":3,"file":"form-view.js","sources":["../../../src/mvc/form/form-view.js"],"names":["define","Utils","Portlet","Ui","FormSection","FormData","Backbone","View","extend","initialize","options","this","optionsDefault","is_workflow","initial_errors","cls","merge","console","debug","galaxy","parent","Galaxy","modal","Modal","setElement","_build","update","new_model","self","data","matchModel","input_id","node","input","input_list","_","isEqual","field","field_list","new_options","indexOf","type","i","opt","length","push","label","value","trigger","wait","active","is_dynamic","unwait","highlight","message","silent","input_element","element_list","error","top","$panel","$el","parents","filter","$","css","first","animate","scrollTop","offset","errors","error_messages","matchResponse","off","_renderForm","create","current_check","checksum","on","new_check","onchange","reset","Message","section","inputs","remove","portlet","icon","title","operations","buttons","append","addClass","empty","persistent","status"],"mappings":"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"}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"tools-jobs.js","sources":["../../../src/mvc/tools/tools-jobs.js"],"names":["define","Utils","ToolTemplate","submit","form","options","callback","self","this","job_def","tool_id","id","tool_version","version","inputs","data","create","trigger","_validation","console","debug","request","type","url","galaxy_config","root","success","response","reciept","_refreshHdas","error","message","error_messages","matchResponse","input_id","highlight","modal","show","title","body","buttons","Close","hide","job_inputs","batch_n","batch_src","job_input_id","input_value","match","input_field","field_list","input_def","input_list","optional","batch","n","values","length","src","detailIds","parent","Galaxy","currHistoryPanel","refreshContents"],"mappings":"AAGAA,QAAQ,cAAe,4BAA6B,SAASC,EAAOC,GACpE,OACIC,OAAQ,SAASC,EAAMC,EAASC,GAE5B,GAAIC,GAAOC,KAGPC,GACAC,QAAkBL,EAAQM,GAC1BC,aAAkBP,EAAQQ,QAC1BC,OAAkBV,EAAKW,KAAKC,SAOhC,OAHAZ,GAAKa,QAAQ,SAGRT,KAAKU,YAAYd,EAAMK,IAO5BU,QAAQC,MAAMX,OAGdR,GAAMoB,SACFC,KAAU,OACVC,IAAUC,cAAcC,KAAO,YAC/BV,KAAUN,EACViB,QAAU,SAASC,GACfrB,GAAYA,IACZF,EAAKwB,QAAQ1B,EAAawB,QAAQC,IAElCpB,EAAKsB,gBAETC,MAAU,SAASH,GAEf,GADArB,GAAYA,IACRqB,GAAYA,EAASI,SAAWJ,EAASI,QAAQhB,KAAM,CACvD,GAAIiB,GAAiB5B,EAAKW,KAAKkB,cAAcN,EAASI,QAAQhB,KAC9D,KAAK,GAAImB,KAAYF,GAAgB,CACjC5B,EAAK+B,UAAUD,EAAUF,EAAeE,GACxC,YAIJf,SAAQC,MAAMO,GACdA,GAAYvB,EAAKgC,MAAMC,MACnBC,MAAU,wBACVC,KAAUrC,EAAa4B,MAAMrB,GAC7B+B,SACIC,MAAU,WACNrC,EAAKgC,MAAMM,gBAnC/BvB,QAAQC,MAAM,qEACdd,GAAYA,OA6CpBY,YAAa,SAASd,EAAMK,GAExB,GAAIkC,GAAalC,EAAQK,OAGrB8B,EAAU,GACVC,EAAY,IAGhB,KAAK,GAAIC,KAAgBH,GAAY,CAEjC,GAAII,GAAcJ,EAAWG,GAGzBZ,EAAW9B,EAAKW,KAAKiC,MAAMF,GAC3BG,EAAc7C,EAAK8C,WAAWhB,GAC9BiB,EAAY/C,EAAKgD,WAAWlB,EAGhC,IAAKA,GAAaiB,GAAcF,EAAhC,CAMA,IAAKE,EAAUE,UAA2B,MAAfN,EAEvB,MADA3C,GAAK+B,UAAUD,IACR,CAIX,IAAIa,GAAeA,EAAYO,MAAO,CAElC,GAAIC,GAAIR,EAAYS,OAAOC,OAGvBC,EAAM,IAMV,IALIH,EAAI,IACJG,EAAMX,EAAYS,OAAO,IAAMT,EAAYS,OAAO,GAAGE,KAIrDA,EACA,GAAkB,OAAdb,EACAA,EAAYa,MAEZ,IAAIb,IAAca,EAEd,MADAtD,GAAK+B,UAAUD,EAAU,mFAClB,CAMnB,IAAgB,KAAZU,EACAA,EAAUW,MAEV,IAAIX,IAAYW,EAEZ,MADAnD,GAAK+B,UAAUD,EAAU,gHAAkHqB,EAAI,wDAA0DX,EAAU,UAC5M,OAvCfzB,SAAQC,MAAM,8DA8CtB,OAAO,GAKXS,aAAc,SAAS8B,EAAWtD,GAC1BuD,OAAOC,QAAUD,OAAOC,OAAOC,kBAC/BF,OAAOC,OAAOC,iBAAiBC,gBAAgBJ,EAAWtD"}
{"version":3,"file":"tools-jobs.js","sources":["../../../src/mvc/tools/tools-jobs.js"],"names":["define","Utils","ToolTemplate","submit","form","options","callback","self","this","job_def","tool_id","id","tool_version","version","inputs","data","create","trigger","_validation","console","debug","request","type","url","galaxy_config","root","success","response","$el","replaceWith","_refreshHdas","error","err_data","error_messages","matchResponse","input_id","highlight","modal","show","title","body","err_msg","buttons","Close","hide","job_inputs","batch_n","batch_src","job_input_id","input_value","match","input_field","field_list","input_def","input_list","optional","batch","n","values","length","src","detailIds","parent","Galaxy","currHistoryPanel","refreshContents"],"mappings":"AAGAA,QAAQ,cAAe,4BAA6B,SAASC,EAAOC,GACpE,OACIC,OAAQ,SAASC,EAAMC,EAASC,GAE5B,GAAIC,GAAOC,KAGPC,GACAC,QAAkBL,EAAQM,GAC1BC,aAAkBP,EAAQQ,QAC1BC,OAAkBV,EAAKW,KAAKC,SAOhC,OAHAZ,GAAKa,QAAQ,SAGRT,KAAKU,YAAYd,EAAMK,IAO5BU,QAAQC,MAAMX,OAGdR,GAAMoB,SACFC,KAAU,OACVC,IAAUC,cAAcC,KAAO,YAC/BV,KAAUN,EACViB,QAAU,SAASC,GACfrB,GAAYA,IACZF,EAAKwB,IAAIC,YAAa3B,EAAawB,QAASC,IAE5CpB,EAAKuB,gBAETC,MAAU,SAASJ,GAGf,GAFArB,GAAYA,IACZa,QAAQC,MAAMO,GACVA,GAAYA,EAASK,SAAU,CAC/B,GAAIC,GAAiB7B,EAAKW,KAAKmB,cAAcP,EAASK,SACtD,KAAK,GAAIG,KAAYF,GAAgB,CACjC7B,EAAKgC,UAAUD,EAAUF,EAAeE,GACxC,YAGJ/B,GAAKiC,MAAMC,MACPC,MAAU,wBACVC,KAAYb,GAAYA,EAASc,SAAavC,EAAa6B,MAAOtB,GAClEiC,SACIC,MAAU,WACNvC,EAAKiC,MAAMO,gBAlC/BzB,QAAQC,MAAM,qEACdd,GAAYA,OA4CpBY,YAAa,SAASd,EAAMK,GAExB,GAAIoC,GAAapC,EAAQK,OAGrBgC,EAAU,GACVC,EAAY,IAGhB,KAAK,GAAIC,KAAgBH,GAAY,CAEjC,GAAII,GAAcJ,EAAWG,GAGzBb,EAAW/B,EAAKW,KAAKmC,MAAMF,GAC3BG,EAAc/C,EAAKgD,WAAWjB,GAC9BkB,EAAYjD,EAAKkD,WAAWnB,EAGhC,IAAKA,GAAakB,GAAcF,EAAhC,CAMA,IAAKE,EAAUE,UAA2B,MAAfN,EAEvB,MADA7C,GAAKgC,UAAUD,IACR,CAIX,IAAIc,GAAeA,EAAYO,MAAO,CAElC,GAAIC,GAAIR,EAAYS,OAAOC,OAGvBC,EAAM,IAMV,IALIH,EAAI,IACJG,EAAMX,EAAYS,OAAO,IAAMT,EAAYS,OAAO,GAAGE,KAIrDA,EACA,GAAkB,OAAdb,EACAA,EAAYa,MAEZ,IAAIb,IAAca,EAEd,MADAxD,GAAKgC,UAAUD,EAAU,mFAClB,CAMnB,IAAgB,KAAZW,EACAA,EAAUW,MAEV,IAAIX,IAAYW,EAEZ,MADArD,GAAKgC,UAAUD,EAAU,gHAAkHsB,EAAI,wDAA0DX,EAAU,UAC5M,OAvCf3B,SAAQC,MAAM,8DA8CtB,OAAO,GAKXU,aAAc,SAAS+B,EAAWxD,GAC1ByD,OAAOC,QAAUD,OAAOC,OAAOC,kBAC/BF,OAAOC,OAAOC,iBAAiBC,gBAAgBJ,EAAWxD"}
+1 -1
View File
@@ -1,2 +1,2 @@
define(["utils/utils","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/form/form-section","mvc/form/form-data"],function(a,b,c,d,e){return Backbone.View.extend({initialize:function(b){this.optionsDefault={is_workflow:!1,initial_errors:!1,cls:"ui-portlet-limited"},this.options=a.merge(b,this.optionsDefault),console.debug(this.options);var d=parent.Galaxy;this.modal=d&&d.modal?d.modal:new c.Modal.View,this.setElement("<div/>"),this._build()},update:function(a){var b=this;this.data.matchModel(a,function(a,c){var d=b.input_list[a];if(d&&d.options&&!_.isEqual(d.options,c.options)){d.options=c.options;var e=b.field_list[a];if(e.update){var f=[];if(-1!=["data","data_collection","drill_down"].indexOf(d.type))f=d.options;else for(var g in c.options){var h=c.options[g];h.length>2&&f.push({label:h[0],value:h[1]})}e.update(f),e.trigger("change"),console.debug("Updating options for "+a)}}})},wait:function(a){for(var b in this.input_list){var c=this.field_list[b],d=this.input_list[b];d.is_dynamic&&c.wait&&c.unwait&&(a?c.wait():c.unwait())}},reciept:function(a){this.$el.empty(),this.$el.append(a)},highlight:function(a,b,c){var d=this.element_list[a];if(d&&(d.error(b||"Please verify this parameter."),this.trigger("expand",a),!c))if(self==top){var e=this.$el.parents().filter(function(){return"auto"==$(this).css("overflow")}).first();e.animate({scrollTop:e.scrollTop()+d.$el.offset().top-50},500)}else $("html, body").animate({scrollTop:d.$el.offset().top-20},500)},errors:function(a){if(this.trigger("reset"),a&&a.errors){var b=this.data.matchResponse(a.errors);for(var c in this.element_list){{this.element_list[c]}b[c]&&this.highlight(c,b[c],!0)}}},_build:function(){var a=this;this.off("change"),this.off("reset"),this.field_list={},this.input_list={},this.element_list={},this.data=new e(this),this._renderForm(),this.data.create(),this.options.initial_errors&&this.errors(this.options);var b=this.data.checksum();this.on("change",function(){var c=a.data.checksum();c!=b&&(b=c,a.options.onchange&&a.options.onchange())}),this.on("reset",function(){for(var a in this.element_list)this.element_list[a].reset()})},_renderForm:function(){this.message=new c.Message,this.section=new d.View(this,{inputs:this.options.inputs}),$(".tooltip").remove(),this.portlet=new b.View({icon:"fa-wrench",title:this.options.title,cls:this.options.cls,operations:this.options.operations,buttons:this.options.buttons}),this.portlet.append(this.message.$el.addClass("ui-margin-top")),this.portlet.append(this.section.$el),this.$el.empty(),this.$el.append(this.portlet.$el),this.options.message&&this.message.update({persistent:!0,status:"warning",message:this.options.message}),console.debug("tools-form-base::initialize() - Completed.")}})});
define(["utils/utils","mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/form/form-section","mvc/form/form-data"],function(a,b,c,d,e){return Backbone.View.extend({initialize:function(b){this.optionsDefault={is_workflow:!1,initial_errors:!1,cls:"ui-portlet-limited"},this.options=a.merge(b,this.optionsDefault),console.debug(this.options);var d=parent.Galaxy;this.modal=d&&d.modal?d.modal:new c.Modal.View,this.setElement("<div/>"),this._build()},update:function(a){var b=this;this.data.matchModel(a,function(a,c){var d=b.input_list[a];if(d&&d.options&&!_.isEqual(d.options,c.options)){d.options=c.options;var e=b.field_list[a];if(e.update){var f=[];if(-1!=["data","data_collection","drill_down"].indexOf(d.type))f=d.options;else for(var g in c.options){var h=c.options[g];h.length>2&&f.push({label:h[0],value:h[1]})}e.update(f),e.trigger("change"),console.debug("Updating options for "+a)}}})},wait:function(a){for(var b in this.input_list){var c=this.field_list[b],d=this.input_list[b];d.is_dynamic&&c.wait&&c.unwait&&(a?c.wait():c.unwait())}},highlight:function(a,b,c){var d=this.element_list[a];if(d&&(d.error(b||"Please verify this parameter."),this.trigger("expand",a),!c))if(self==top){var e=this.$el.parents().filter(function(){return"auto"==$(this).css("overflow")}).first();e.animate({scrollTop:e.scrollTop()+d.$el.offset().top-50},500)}else $("html, body").animate({scrollTop:d.$el.offset().top-20},500)},errors:function(a){if(this.trigger("reset"),a&&a.errors){var b=this.data.matchResponse(a.errors);for(var c in this.element_list){{this.element_list[c]}b[c]&&this.highlight(c,b[c],!0)}}},_build:function(){var a=this;this.off("change"),this.off("reset"),this.field_list={},this.input_list={},this.element_list={},this.data=new e(this),this._renderForm(),this.data.create(),this.options.initial_errors&&this.errors(this.options);var b=this.data.checksum();this.on("change",function(){var c=a.data.checksum();c!=b&&(b=c,a.options.onchange&&a.options.onchange())}),this.on("reset",function(){for(var a in this.element_list)this.element_list[a].reset()})},_renderForm:function(){this.message=new c.Message,this.section=new d.View(this,{inputs:this.options.inputs}),$(".tooltip").remove(),this.portlet=new b.View({icon:"fa-wrench",title:this.options.title,cls:this.options.cls,operations:this.options.operations,buttons:this.options.buttons}),this.portlet.append(this.message.$el.addClass("ui-margin-top")),this.portlet.append(this.section.$el),this.$el.empty(),this.$el.append(this.portlet.$el),this.options.message&&this.message.update({persistent:!0,status:"warning",message:this.options.message}),console.debug("tools-form-base::initialize() - Completed.")}})});
//# sourceMappingURL=../../../maps/mvc/form/form-view.js.map
+1 -1
View File
@@ -1,2 +1,2 @@
define(["utils/utils","utils/deferred","mvc/ui/ui-misc","mvc/form/form-view","mvc/tools/tools-template","mvc/citation/citation-model","mvc/citation/citation-view"],function(a,b,c,d,e,f,g){return Backbone.View.extend({initialize:function(c){this.options=a.merge(c,{}),this.setElement("<div/>"),this.deferred=new b,c.inputs?this._buildForm(c):this._buildModel(c,!0)},_buildForm:function(b){var c=this;this.options=a.merge(b,this.options),this.options=a.merge({icon:"fa-wrench",title:"<b>"+b.name+"</b> "+b.description+" (Galaxy Tool Version "+b.version+")",operations:this._operations(),onchange:function(){c.deferred.reset(),c.deferred.execute(function(){c._updateModel()})}},this.options),this.options.customize&&this.options.customize(this.options),this.form=new d(this.options),this._footer(),this.$el.empty(),this.$el.append(this.form.$el)},_buildModel:function(b,d){var e=this;if(this.options.id=b.id,this.options.version=b.version,b.job_id)f=Galaxy.root+"api/jobs/"+b.job_id+"/build_for_rerun";else{var f=Galaxy.root+"api/tools/"+b.id+"/build?";b.version&&(f+="tool_version="+b.version+"&"),Galaxy.params&&Galaxy.params.tool_id==b.id&&_.each(Galaxy.params,function(a,b){-1==["tool_version","tool_id"].indexOf(b)&&(f+=b+"="+a+"&")})}var g=this.deferred.register();a.request({type:"GET",url:f,success:function(a){e._buildForm(a.tool_model||a),!d&&e.form.message.update({status:"success",message:"Now you are using '"+e.options.name+"' version "+e.options.version+".",persistent:!1}),e.deferred.done(g),console.debug("tools-form::initialize() - Initial tool model ready."),console.debug(a)},error:function(a){e.deferred.done(g),console.debug("tools-form::initialize() - Initial tool model request failed."),console.debug(a);var b=a.error||a.err_msg||"Uncaught error.";e.$el.is(":empty")?e.$el.prepend(new c.Message({message:b,status:"danger",persistent:!0,large:!0}).$el):Galaxy.modal.show({title:"Tool request failed",body:b,buttons:{Close:function(){Galaxy.modal.hide()}}})}})},_updateModel:function(){var b=this.options.update_url||Galaxy.root+"api/tools/"+this.options.id+"/build",c=this,d=this.form,e={tool_id:this.options.id,tool_version:this.options.version,inputs:$.extend(!0,{},c.form.data.create())};d.wait(!0);var f=this.deferred.register();console.debug("tools-form-base::_updateModel() - Sending current state (see below)."),console.debug(e),a.request({type:"POST",url:b,data:e,success:function(a){c.form.update(a.tool_model||a),c.options.update&&c.options.update(a),d.wait(!1),console.debug("tools-form-base::_updateModel() - Received new model (see below)."),console.debug(a),c.deferred.done(f)},error:function(a){c.deferred.done(f),console.debug("tools-form-base::_updateModel() - Refresh request failed."),console.debug(a)}})},_operations:function(){var a=this,b=this.options,d=new c.ButtonMenu({icon:"fa-cubes",title:!b.narrow&&"Versions"||null,tooltip:"Select another tool version"});if(!b.is_workflow&&b.versions&&b.versions.length>1)for(var f in b.versions){var g=b.versions[f];g!=b.version&&d.addMenu({title:"Switch to "+g,version:g,icon:"fa-cube",onclick:function(){var c=b.id.replace(b.version,this.version),d=this.version;a.deferred.reset(),a.deferred.execute(function(){a._buildModel({id:c,version:d})})}})}else d.$el.hide();var h=new c.ButtonMenu({icon:"fa-caret-down",title:!b.narrow&&"Options"||null,tooltip:"View available options"});return b.biostar_url&&(h.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(b.biostar_url+"/p/new/post/")}}),h.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(b.biostar_url+"/local/search/page/?q="+b.name)}})),h.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+Galaxy.root+"root?tool_id="+b.id)}}),Galaxy.user&&Galaxy.user.get("is_admin")&&h.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=Galaxy.root+"api/tools/"+b.id+"/download"}}),b.requirements&&b.requirements.length>0&&h.addMenu({icon:"fa-info-circle",title:"Requirements",tooltip:"Display tool requirements",onclick:function(){this.visible?(this.visible=!1,a.form.message.update({message:""})):(this.visible=!0,a.form.message.update({persistent:!0,message:e.requirements(b),status:"info"}))}}),b.sharable_url&&h.addMenu({icon:"fa-external-link",title:"See in Tool Shed",tooltip:"Access the repository",onclick:function(){window.open(b.sharable_url)}}),{menu:h,versions:d}},_footer:function(){var a=this.options;if(""!=a.help&&this.form.$el.append(e.help(a)),a.citations){var b=$("<div/>"),c=new f.ToolCitationCollection;c.tool_id=a.id;var d=new g.CitationListView({el:b,collection:c});d.render(),c.fetch(),this.form.$el.append(b)}}})});
define(["utils/utils","utils/deferred","mvc/ui/ui-misc","mvc/form/form-view","mvc/tools/tools-template","mvc/citation/citation-model","mvc/citation/citation-view"],function(a,b,c,d,e,f,g){return Backbone.View.extend({initialize:function(c){this.options=a.merge(c,{}),this.setElement("<div/>"),this.deferred=new b,c.inputs?this._buildForm(c):this._buildModel(c,!0)},_buildForm:function(b){var c=this;this.options=a.merge(b,this.options),this.options=a.merge({icon:"fa-wrench",title:"<b>"+b.name+"</b> "+b.description+" (Galaxy Tool Version "+b.version+")",operations:this._operations(),onchange:function(){c.deferred.reset(),c.deferred.execute(function(){c._updateModel()})}},this.options),this.options.customize&&this.options.customize(this.options),this.form=new d(this.options),this._footer(),this.$el.empty(),this.$el.append(this.form.$el)},_buildModel:function(b,d){var e=this;if(this.options.id=b.id,this.options.version=b.version,b.job_id)f=Galaxy.root+"api/jobs/"+b.job_id+"/build_for_rerun";else{var f=Galaxy.root+"api/tools/"+b.id+"/build?";b.version&&(f+="tool_version="+b.version+"&"),Galaxy.params&&Galaxy.params.tool_id==b.id&&_.each(Galaxy.params,function(a,b){-1==["tool_version","tool_id"].indexOf(b)&&(f+=b+"="+a+"&")})}var g=this.deferred.register();a.request({type:"GET",url:f,success:function(a){e._buildForm(a.tool_model||a),!d&&e.form.message.update({status:"success",message:"Now you are using '"+e.options.name+"' version "+e.options.version+".",persistent:!1}),e.deferred.done(g),console.debug("tools-form::initialize() - Initial tool model ready."),console.debug(a)},error:function(a){e.deferred.done(g),console.debug("tools-form::initialize() - Initial tool model request failed."),console.debug(a);var b=a&&a.error||a&&a.err_msg||"Uncaught error.";e.$el.is(":empty")?e.$el.prepend(new c.Message({message:b,status:"danger",persistent:!0,large:!0}).$el):Galaxy.modal.show({title:"Tool request failed",body:b,buttons:{Close:function(){Galaxy.modal.hide()}}})}})},_updateModel:function(){var b=this.options.update_url||Galaxy.root+"api/tools/"+this.options.id+"/build",c=this,d=this.form,e={tool_id:this.options.id,tool_version:this.options.version,inputs:$.extend(!0,{},c.form.data.create())};d.wait(!0);var f=this.deferred.register();console.debug("tools-form-base::_updateModel() - Sending current state (see below)."),console.debug(e),a.request({type:"POST",url:b,data:e,success:function(a){c.form.update(a.tool_model||a),c.options.update&&c.options.update(a),d.wait(!1),console.debug("tools-form-base::_updateModel() - Received new model (see below)."),console.debug(a),c.deferred.done(f)},error:function(a){c.deferred.done(f),console.debug("tools-form-base::_updateModel() - Refresh request failed."),console.debug(a)}})},_operations:function(){var a=this,b=this.options,d=new c.ButtonMenu({icon:"fa-cubes",title:!b.narrow&&"Versions"||null,tooltip:"Select another tool version"});if(!b.is_workflow&&b.versions&&b.versions.length>1)for(var f in b.versions){var g=b.versions[f];g!=b.version&&d.addMenu({title:"Switch to "+g,version:g,icon:"fa-cube",onclick:function(){var c=b.id.replace(b.version,this.version),d=this.version;a.deferred.reset(),a.deferred.execute(function(){a._buildModel({id:c,version:d})})}})}else d.$el.hide();var h=new c.ButtonMenu({icon:"fa-caret-down",title:!b.narrow&&"Options"||null,tooltip:"View available options"});return b.biostar_url&&(h.addMenu({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(b.biostar_url+"/p/new/post/")}}),h.addMenu({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(b.biostar_url+"/local/search/page/?q="+b.name)}})),h.addMenu({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",window.location.origin+Galaxy.root+"root?tool_id="+b.id)}}),Galaxy.user&&Galaxy.user.get("is_admin")&&h.addMenu({icon:"fa-download",title:"Download",tooltip:"Download this tool",onclick:function(){window.location.href=Galaxy.root+"api/tools/"+b.id+"/download"}}),b.requirements&&b.requirements.length>0&&h.addMenu({icon:"fa-info-circle",title:"Requirements",tooltip:"Display tool requirements",onclick:function(){this.visible?(this.visible=!1,a.form.message.update({message:""})):(this.visible=!0,a.form.message.update({persistent:!0,message:e.requirements(b),status:"info"}))}}),b.sharable_url&&h.addMenu({icon:"fa-external-link",title:"See in Tool Shed",tooltip:"Access the repository",onclick:function(){window.open(b.sharable_url)}}),{menu:h,versions:d}},_footer:function(){var a=this.options;if(""!=a.help&&this.form.$el.append(e.help(a)),a.citations){var b=$("<div/>"),c=new f.ToolCitationCollection;c.tool_id=a.id;var d=new g.CitationListView({el:b,collection:c});d.render(),c.fetch(),this.form.$el.append(b)}}})});
//# sourceMappingURL=../../../maps/mvc/tools/tools-form-base.js.map
+1 -1
View File
@@ -1,2 +1,2 @@
define(["utils/utils","mvc/tools/tools-template"],function(a,b){return{submit:function(c,d,e){var f=this,g={tool_id:d.id,tool_version:d.version,inputs:c.data.create()};return c.trigger("reset"),this._validation(c,g)?(console.debug(g),void a.request({type:"POST",url:galaxy_config.root+"api/tools",data:g,success:function(a){e&&e(),c.reciept(b.success(a)),f._refreshHdas()},error:function(a){if(e&&e(),a&&a.message&&a.message.data){var d=c.data.matchResponse(a.message.data);for(var f in d){c.highlight(f,d[f]);break}}else console.debug(a),a&&c.modal.show({title:"Job submission failed",body:b.error(g),buttons:{Close:function(){c.modal.hide()}}})}})):(console.debug("tools-jobs::submit - Submission canceled. Validation failed."),void(e&&e()))},_validation:function(a,b){var c=b.inputs,d=-1,e=null;for(var f in c){var g=c[f],h=a.data.match(f),i=a.field_list[h],j=a.input_list[h];if(h&&j&&i){if(!j.optional&&null==g)return a.highlight(h),!1;if(g&&g.batch){var k=g.values.length,l=null;if(k>0&&(l=g.values[0]&&g.values[0].src),l)if(null===e)e=l;else if(e!==l)return a.highlight(h,"Please select either dataset or dataset list fields for all batch mode fields."),!1;if(-1===d)d=k;else if(d!==k)return a.highlight(h,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+k+"</b> selection(s) while a previous field contains <b>"+d+"</b>."),!1}}else console.debug("tools-jobs::_validation - Retrieving input objects failed.")}return!0},_refreshHdas:function(a,b){parent.Galaxy&&parent.Galaxy.currHistoryPanel&&parent.Galaxy.currHistoryPanel.refreshContents(a,b)}}});
define(["utils/utils","mvc/tools/tools-template"],function(a,b){return{submit:function(c,d,e){var f=this,g={tool_id:d.id,tool_version:d.version,inputs:c.data.create()};return c.trigger("reset"),this._validation(c,g)?(console.debug(g),void a.request({type:"POST",url:galaxy_config.root+"api/tools",data:g,success:function(a){e&&e(),c.$el.replaceWith(b.success(a)),f._refreshHdas()},error:function(a){if(e&&e(),console.debug(a),a&&a.err_data){var d=c.data.matchResponse(a.err_data);for(var f in d){c.highlight(f,d[f]);break}}else c.modal.show({title:"Job submission failed",body:a&&a.err_msg||b.error(g),buttons:{Close:function(){c.modal.hide()}}})}})):(console.debug("tools-jobs::submit - Submission canceled. Validation failed."),void(e&&e()))},_validation:function(a,b){var c=b.inputs,d=-1,e=null;for(var f in c){var g=c[f],h=a.data.match(f),i=a.field_list[h],j=a.input_list[h];if(h&&j&&i){if(!j.optional&&null==g)return a.highlight(h),!1;if(g&&g.batch){var k=g.values.length,l=null;if(k>0&&(l=g.values[0]&&g.values[0].src),l)if(null===e)e=l;else if(e!==l)return a.highlight(h,"Please select either dataset or dataset list fields for all batch mode fields."),!1;if(-1===d)d=k;else if(d!==k)return a.highlight(h,"Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>"+k+"</b> selection(s) while a previous field contains <b>"+d+"</b>."),!1}}else console.debug("tools-jobs::_validation - Retrieving input objects failed.")}return!0},_refreshHdas:function(a,b){parent.Galaxy&&parent.Galaxy.currHistoryPanel&&parent.Galaxy.currHistoryPanel.refreshContents(a,b)}}});
//# sourceMappingURL=../../../maps/mvc/tools/tools-jobs.js.map
-122
View File
@@ -136,128 +136,6 @@
%endif
</script>
## Handle AJAX (actually hidden iframe) upload tool
<script type="text/javascript">
var upload_form_error = function( msg ) {
var $galaxy_mainBody = $("iframe#galaxy_main").contents().find("body"),
$errMsg = $galaxy_mainBody.find( 'div.errormessage' );
if ( !$errMsg.size() ){
$errMsg = $( '<div/>' ).addClass( 'errormessage' ).prependTo( $galaxy_mainBody );
}
$errMsg.text( msg );
}
var uploads_in_progress = 0;
function decrementUploadsInProgress(){
uploads_in_progress -= 1;
if( uploads_in_progress === 0 ){
window.onbeforeunload = null;
}
}
jQuery( function() {
$("iframe#galaxy_main").load( function() {
$(this).contents().find("form").each( function() {
if ( $(this).find("input[galaxy-ajax-upload]").length > 0 ){
var $form = $( this );
$(this).submit( function( event ) {
// Only bother using a hidden iframe if there's a file (e.g. big data) upload
var file_upload = false;
$(this).find("input[galaxy-ajax-upload]").each( function() {
if ( $(this).val() != '' ) {
file_upload = true;
}
});
if ( ! file_upload ) {
return true;
}
// Make a synchronous request to create the datasets first
var async_datasets;
var upload_error = false;
//NOTE: in order for upload.py to match the datasets created below, we'll move the js File
// object's name into the file_data field (not in the form only for what we send to
// upload_async_create)
var formData = $( this ).serializeArray();
var name = function(){
var $fileInput = $form.find( 'input[name="files_0|file_data"]' );
if( /msie/.test( navigator.userAgent.toLowerCase() ) ){
return $fileInput.val().replace( 'C:\\fakepath\\', '' );
} else {
return $fileInput.get( 0 ).files[0].name;
}
}
formData.push({ name: "files_0|file_data", value: name });
$.ajax( {
async: false,
type: "POST",
url: "${h.url_for(controller='/tool_runner', action='upload_async_create')}",
data: formData,
dataType: "json",
success: function(array_obj, status) {
if (array_obj.length > 0) {
if (array_obj[0] == 'error') {
upload_error = true;
upload_form_error(array_obj[1]);
} else {
async_datasets = array_obj.join();
}
} else {
// ( gvk 1/22/10 ) FIXME: this block is never entered, so there may be a bug somewhere
// I've done some debugging like checking to see if array_obj is undefined, but have not
// tracked down the behavior that will result in this block being entered. I believe the
// intent was to have this block entered if the upload button is clicked on the upload
// form but no file was selected.
upload_error = true;
upload_form_error( 'No data was entered in the upload form. You may choose to upload a file, paste some data directly in the data box, or enter URL(s) to fetch data.' );
}
}
} );
// show the dataset we created above in the history panel
Galaxy && Galaxy.currHistoryPanel && Galaxy.currHistoryPanel.refreshContents();
if (upload_error == true) {
return false;
} else {
$(this).find("input[name=async_datasets]").val( async_datasets );
$(this).append("<input type='hidden' name='ajax_upload' value='true'>");
}
// iframe submit is required for nginx (otherwise the encoding is wrong)
$(this).ajaxSubmit({
//iframe: true,
error: function( xhr, msg, status ){
decrementUploadsInProgress();
},
success: function ( response, x, y, z ) {
decrementUploadsInProgress();
}
});
uploads_in_progress++;
window.onbeforeunload = function() {
return "Navigating away from the Galaxy analysis interface will interrupt the "
+ "file upload(s) currently in progress. Do you really want to do this?";
}
if ( $(this).find("input[name='folder_id']").val() != undefined ) {
var library_id = $(this).find("input[name='library_id']").val();
var show_deleted = $(this).find("input[name='show_deleted']").val();
if ( location.pathname.indexOf( 'admin' ) != -1 ) {
$("iframe#galaxy_main").attr("src","${h.url_for( controller='library_common', action='browse_library' )}?cntrller=library_admin&id=" + library_id + "&created_ldda_ids=" + async_datasets + "&show_deleted=" + show_deleted);
} else {
$("iframe#galaxy_main").attr("src","${h.url_for( controller='library_common', action='browse_library' )}?cntrller=library&id=" + library_id + "&created_ldda_ids=" + async_datasets + "&show_deleted=" + show_deleted);
}
} else {
$("iframe#galaxy_main").attr("src","${h.url_for(controller='tool_runner', action='upload_async_message')}");
}
event.preventDefault();
return false;
});
}
});
});
});
</script>
</%def>
## Masthead
@@ -0,0 +1,42 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Galaxy</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
<script type="text/javascript">
setTimeout( function() { top.location.href = '${h.url_for( "/" )}'; }, 1000 );
</script>
</head>
<body>
<div class="donemessagelarge">
%if num_jobs > 1:
<% jobs_str = "%d jobs have" % num_jobs %>
%else:
<% jobs_str = "A job has" %>
%endif
%if len(out_data) == 1:
<% datasets_str = "dataset" %>
%else:
<% datasets_str = "datasets" %>
%endif
<p>
${jobs_str} been successfully added to the queue - resulting in the following ${datasets_str}:
</p>
%for _, data in out_data:
<div style="padding: 10px"><b> ${data.hid}: ${data.name | h}</b></div>
%endfor
<p> You can check the status of queued jobs and view the resulting data by refreshing the <b>History</b> pane. When the job has been run the status will change from 'running' to 'finished' if completed successfully or 'error' if problems were encountered. You are now being redirected back to <a href="${h.url_for( '/' )}">Galaxy</a>.</p>
</div>
%if job_errors:
<div class="errormessagelarge">
There were errors setting up ${len(job_errors)} submitted job(s):
<ul>
%for job_error in job_errors:
<li><b>${job_error | h}</b></li>
%endfor
</ul>
</div>
%endif
</body>
</html>
@@ -1,94 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Galaxy</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var inside_galaxy_frameset = false;
// refresh the history panel to include any new datasets created by the tool
if( top.Galaxy && top.Galaxy.currHistoryPanel ){
top.Galaxy.currHistoryPanel.refreshContents();
}
%if trans.user:
if (inside_galaxy_frameset)
{
//parent.frames.galaxy_tools.update_recently_used();
}
%endif
if ( parent.handle_minwidth_hint ) {
parent.handle_minwidth_hint( -1 );
}
function main() {
// If called from outside the galaxy frameset, redirect there
%if tool.options.refresh:
if ( ! inside_galaxy_frameset ) {
setTimeout( "refresh()", 1000 );
document.getElementById( "refresh_message" ).style.display = "block";
}
%endif
}
function refresh() {
top.location.href = '${h.url_for( "/" )}';
}
</script>
</head>
<body onLoad="main()">
<div class="donemessagelarge">
%if num_jobs > 1:
<% jobs_str = "%d jobs have" % num_jobs %>
%else:
<% jobs_str = "A job has" %>
%endif
%if len(out_data) == 1:
<% datasets_str = "dataset" %>
%else:
<% datasets_str = "datasets" %>
%endif
<p>
${jobs_str} been successfully added to the queue - resulting in the following ${datasets_str}:
</p>
%for _, data in out_data:
<div style="padding: 10px"><b> ${data.hid}: ${data.name | h}</b></div>
%endfor
<p>
You can check the status of queued jobs and view the resulting
data by refreshing the <b>History</b> pane. When the job has been run
the status will change from 'running' to 'finished' if completed
successfully or 'error' if problems were encountered.
</p>
%if tool.options.refresh:
<p id="refresh_message" style="display: none;">You are now being redirected back to <a href="${h.url_for( '/' )}">Galaxy</a></div>
%endif
</div>
%if job_errors:
<div class="errormessagelarge">
There were errors setting up ${len(job_errors)} submitted job(s):
<ul>
<!-- Styling on this list is a little flat. Consider identing these error messages. -->
%for job_error in job_errors:
<li><b>${job_error | h}</b></li>
%endfor
</ul>
</div>
%endif
</body>
</html>
+1 -1
View File
@@ -219,7 +219,7 @@ class GalaxyInteractorApi( object ):
jobs=submit_response_object[ 'jobs' ],
)
except KeyError:
message = "Error creating a job for these tool inputs - %s" % submit_response_object[ 'message' ]
message = "Error creating a job for these tool inputs - %s" % submit_response_object[ 'err_msg' ]
raise RunToolException( message, inputs_tree )
def _create_collection( self, history_id, collection_def ):
+50 -253
View File
@@ -4,11 +4,8 @@
from unittest import TestCase
import galaxy.model
from galaxy.tools import DefaultToolState
from galaxy.tools.parameters import params_to_incoming
from galaxy.util.bunch import Bunch
from galaxy.util import string_to_object
from galaxy.util import object_to_string
from galaxy.util.odict import odict
import tools_support
@@ -49,118 +46,62 @@ class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.Uses
def test_state_new( self ):
self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS )
template, template_vars = self.__handle_with_incoming(
param1="moo",
# no runtool_btn, just rerenders the form mako with tool
# state populated.
)
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert state.inputs[ "param1" ] == "moo"
vars = self.__handle_with_incoming( param1="moo" )
state = self.__assert_rerenders_tool_without_errors( vars )
assert state[ "param1" ] == "moo"
def test_execute( self ):
self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS )
template, template_vars = self.__handle_with_incoming(
param1="moo",
runtool_btn="dummy",
)
self.__assert_exeuted( template, template_vars )
vars = self.__handle_with_incoming( param1="moo" )
self.__assert_executed( vars )
# Didn't specify a rerun_remap_id so this should be None
assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] is None
def test_execute_exception( self ):
self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS )
self.tool_action.raise_exception( )
template, template_vars = self.__handle_with_incoming(
param1="moo",
runtool_btn="dummy",
)
assert template == "message.mako"
assert template_vars[ "status" ] == "error"
assert "Error executing tool" in template_vars[ "message" ]
try:
self.__handle_with_incoming( param1="moo" )
except Exception, e:
assert 'Error executing tool' in str( e )
def test_execute_errors( self ):
self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS )
self.tool_action.return_error( )
template, template_vars = self.__handle_with_incoming(
param1="moo",
runtool_btn="dummy",
)
assert template == "message.mako"
assert template_vars[ "status" ] == "error"
assert "Test Error Message" in template_vars[ "message" ], template_vars
try:
self.__handle_with_incoming( param1="moo" )
except Exception, e:
assert 'Test Error Message' in str( e )
def test_redirect( self ):
self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS )
self.tool_action.expect_redirect = True
redirect_raised = False
try:
template, template_vars = self.__handle_with_incoming(
param1="moo",
runtool_btn="dummy",
)
self.__handle_with_incoming( param1="moo" )
except httpexceptions.HTTPFound:
redirect_raised = True
assert redirect_raised
def test_remap_job( self ):
self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS )
template, template_vars = self.__handle_with_incoming(
param1="moo",
rerun_remap_job_id=self.app.security.encode_id(123),
runtool_btn="dummy",
)
self.__assert_exeuted( template, template_vars )
vars = self.__handle_with_incoming( param1="moo", rerun_remap_job_id=self.app.security.encode_id(123) )
self.__assert_executed( vars )
assert self.tool_action.execution_call_args[ 0 ][ "rerun_remap_job_id" ] == 123
def test_invalid_remap_job( self ):
self._init_tool( tools_support.SIMPLE_TOOL_CONTENTS )
template, template_vars = self.__handle_with_incoming(
param1="moo",
rerun_remap_job_id='123', # Not encoded
runtool_btn="dummy",
)
assert template == "message.mako"
assert template_vars[ "status" ] == "error"
assert "invalid job" in template_vars[ "message" ]
def test_repeat_state_updates( self ):
self._init_tool( REPEAT_TOOL_CONTENTS )
# Fresh state contains no repeat elements
template, template_vars = self.__handle_with_incoming()
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 0
# Hitting add button adds repeat element
template, template_vars = self.__handle_with_incoming(
repeat1_add="dummy",
)
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 1
# Hitting add button again adds another repeat element
template, template_vars = self.__handle_with_incoming( state, **{
"repeat1_add": "dummy",
"repeat1_0|param2": "moo2",
} )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 2
assert state.inputs[ "repeat1" ][ 0 ][ "param2" ] == "moo2"
# Hitting remove drops a repeat element
template, template_vars = self.__handle_with_incoming( state, repeat1_1_remove="dummy" )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 1
try:
self.__handle_with_incoming( param1="moo", rerun_remap_job_id='123' )
except Exception, e:
assert 'invalid job' in str( e )
def test_data_param_execute( self ):
self._init_tool( tools_support.SIMPLE_CAT_TOOL_CONTENTS )
hda = self.__add_dataset(1)
hda = self.__add_dataset( 1 )
# Execute tool action
template, template_vars = self.__handle_with_incoming(
param1=1,
runtool_btn="dummy",
)
self.__assert_exeuted( template, template_vars )
vars = self.__handle_with_incoming( param1=1 )
self.__assert_executed( vars )
# Tool 'executed' once, with hda as param1
assert len( self.tool_action.execution_call_args ) == 1
assert self.tool_action.execution_call_args[ 0 ][ "incoming" ][ "param1" ] == hda
@@ -169,113 +110,27 @@ class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.Uses
self._init_tool( tools_support.SIMPLE_CAT_TOOL_CONTENTS )
hda = self.__add_dataset( 1 )
# Update state
template, template_vars = self.__handle_with_incoming(
param1=1,
)
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert hda == state.inputs[ "param1" ]
vars = self.__handle_with_incoming( param1=1 )
state = self.__assert_rerenders_tool_without_errors( vars )
assert hda == state[ "param1" ]
def test_simple_multirun_state_update( self ):
hda1, hda2 = self.__setup_multirun_job()
template, template_vars = self.__handle_with_incoming( **{
"param1|__multirun__": [ 1, 2 ],
vars = self.__handle_with_incoming( **{
"param1|__multirun__": [ 1, 2 ]
} )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
self.__assert_state_serializable( state )
self.assertEquals( state.inputs[ "param1|__multirun__" ], [ 1, 2 ] )
def test_simple_collection_multirun_state_update( self ):
hdca = self.__setup_collection_multirun_job()
encoded_id = self.app.security.encode_id(hdca.id)
template, template_vars = self.__handle_with_incoming( **{
"param1|__collection_multirun__": encoded_id,
} )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
self.__assert_state_serializable( state )
self.assertEquals( state.inputs[ "param1|__collection_multirun__" ], encoded_id )
def test_repeat_multirun_state_updates( self ):
self._init_tool( REPEAT_TOOL_CONTENTS )
# Fresh state contains no repeat elements
self.__handle_with_incoming()
# Hitting add button adds repeat element
template, template_vars = self.__handle_with_incoming(**{
"param1|__multirun__": [ 1, 2 ],
"repeat1_add": "dummy",
})
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
self.assertEquals( state.inputs[ "param1|__multirun__" ], [ 1, 2 ] )
assert len( state.inputs[ "repeat1" ] ) == 1
# Hitting add button again adds another repeat element
template, template_vars = self.__handle_with_incoming( state, **{
"repeat1_0|param2|__multirun__": [ 1, 2 ],
"repeat1_add": "dummy",
} )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
self.assertEquals( state.inputs[ "param1|__multirun__" ], [ 1, 2 ] )
self.assertEquals( state.inputs[ "repeat1" ][0][ "param2|__multirun__" ], [ 1, 2 ] )
def test_simple_multirun_execution( self ):
hda1, hda2 = self.__setup_multirun_job()
template, template_vars = self.__handle_with_incoming( **{
"param1|__multirun__": [ 1, 2 ],
"runtool_btn": "dummy",
} )
self.__assert_exeuted( template, template_vars )
# Tool 'executed' twice, with param1 as hda1 and hda2 respectively.
assert len( self.tool_action.execution_call_args ) == 2
self.assertEquals( self.tool_action.execution_call_args[ 0 ][ "incoming" ][ "param1" ], hda1 )
self.assertEquals( self.tool_action.execution_call_args[ 1 ][ "incoming" ][ "param1" ], hda2 )
self.assertEquals( len( template_vars[ "jobs" ] ), 2 )
self.__assert_rerenders_tool_without_errors( vars )
assert vars[ 'num_jobs' ] == 2
def test_cannot_multirun_and_remap( self ):
hda1, hda2 = self.__setup_multirun_job()
template, template_vars = self.__handle_with_incoming( **{
"param1|__multirun__": [ 1, 2 ],
"rerun_remap_job_id": self.app.security.encode_id(123), # Not encoded
"runtool_btn": "dummy",
} )
self.assertEquals( template, "message.mako" )
assert template_vars[ "status" ] == "error"
assert "multiple jobs" in template_vars[ "message" ]
def test_multirun_with_state_updates( self ):
hda1, hda2 = self.__setup_multirun_job()
# Fresh state contains no repeat elements
template, template_vars = self.__handle_with_incoming()
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 0
self.__assert_state_serializable( state )
# Hitting add button adds repeat element
template, template_vars = self.__handle_with_incoming( **{
"param1|__multirun__": [ 1, 2 ],
"repeat1_add": "dummy",
} )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 1
self.assertEquals( state.inputs[ "param1|__multirun__" ], [ 1, 2 ] )
self.__assert_state_serializable( state )
# Hitting add button again adds another repeat element
template, template_vars = self.__handle_with_incoming( state, **{
"repeat1_add": "dummy",
"repeat1_0|param2": 1,
} )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
self.assertEquals( state.inputs[ "param1|__multirun__" ], [ 1, 2 ] )
assert len( state.inputs[ "repeat1" ] ) == 2
assert state.inputs[ "repeat1" ][ 0 ][ "param2" ] == hda1
self.__assert_state_serializable( state )
# Hitting remove drops a repeat element
template, template_vars = self.__handle_with_incoming( state, repeat1_1_remove="dummy" )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 1
self.__assert_state_serializable( state )
try:
self.__handle_with_incoming( **{
"param1|__multirun__": [ 1, 2 ],
"rerun_remap_job_id": self.app.security.encode_id( 123 )
} )
except Exception, e:
assert 'multiple jobs' in str( e )
def test_collection_multirun_with_state_updates( self ):
hda1, hda2 = self.__setup_multirun_job()
@@ -284,31 +139,10 @@ class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.Uses
self.app.dataset_collections_service = Bunch(
match_collections=lambda collections: None
)
template, template_vars = self.__handle_with_incoming( **{
"param1|__collection_multirun__": collection_id,
"runtool_btn": "dummy",
vars = self.__handle_with_incoming( **{
"param1|__collection_multirun__": collection_id
} )
self.__assert_exeuted( template, template_vars )
def test_subcollection_multirun_with_state_updates( self ):
self._init_tool( REPEAT_COLLECTION_PARAM_CONTENTS )
hda1, hda2 = self.__add_dataset( 1 ), self.__add_dataset( 2 )
collection = self.__history_dataset_collection_for( [ hda1, hda2 ], collection_type="list:paired" )
collection_id = self.app.security.encode_id( collection.id )
self.app.dataset_collections_service = Bunch(
match_collections=lambda collections: None
)
template, template_vars = self.__handle_with_incoming(
repeat1_add="dummy",
)
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert len( state.inputs[ "repeat1" ] ) == 1
template, template_vars = self.__handle_with_incoming( state, **{
"repeat1_0|param2|__collection_multirun__": "%s|paired" % collection_id,
"repeat1_add": "dummy",
} )
state = self.__assert_rerenders_tool_without_errors( template, template_vars )
assert state.inputs[ "repeat1" ][ 0 ][ "param2|__collection_multirun__" ] == "%s|paired" % collection_id
self.__assert_executed( vars )
def __history_dataset_collection_for( self, hdas, collection_type="list", id=1234 ):
collection = galaxy.model.DatasetCollection(
@@ -340,19 +174,11 @@ class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.Uses
hdcas[ id ] = history_dataset_collection_association
return history_dataset_collection_association
def __assert_state_serializable( self, state ):
self.__state_to_string( state ) # Will thrown exception if there is a problem...
def __setup_multirun_job( self ):
self._init_tool( tools_support.SIMPLE_CAT_TOOL_CONTENTS )
hda1, hda2 = self.__add_dataset( 1 ), self.__add_dataset( 2 )
return hda1, hda2
def __setup_collection_multirun_job( self ):
self._init_tool( tools_support.SIMPLE_CAT_TOOL_CONTENTS )
hdca = self.__add_collection_dataset( 1 )
return hdca
def __handle_with_incoming( self, previous_state=None, **kwds ):
""" Execute tool.handle_input with incoming specified by kwds
(optionally extending a previous state).
@@ -361,10 +187,7 @@ class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.Uses
incoming = self.__to_incoming( previous_state, **kwds)
else:
incoming = kwds
return self.tool.handle_input(
trans=self.trans,
incoming=incoming,
)
return self.tool.handle_input( trans=self.trans, incoming=incoming )
def __to_incoming( self, state, **kwds ):
new_incoming = {}
@@ -398,43 +221,17 @@ class ToolExecutionTestCase( TestCase, tools_support.UsesApp, tools_support.Uses
self.history.dataset_collections.append( hdca )
return hdca
def __assert_rerenders_tool_without_errors( self, template, template_vars ):
assert template == ""
self.__assert_no_errors( template_vars )
state = template_vars[ "tool_state" ]
return state
def __assert_rerenders_tool_without_errors( self, vars ):
self.__assert_no_errors( vars )
return self.tool_action.execution_call_args[ 0 ][ "incoming" ]
def __assert_exeuted( self, template, template_vars ):
if template == "":
self.__assert_no_errors( template_vars )
self.assertEquals(
template,
"tool_executed.mako",
"Expected tools_execute template - got template %s with vars %s" % ( template, template_vars)
)
def __assert_executed( self, vars ):
self.__assert_no_errors( vars )
assert len( vars[ 'jobs' ] ) > 0
def __assert_no_errors( self, template_vars ):
assert "errors" in template_vars, "Tool form rendered without error definition."
errors = template_vars[ "errors" ]
assert not errors, "Template rendered unexpected errors - %s" % errors
def __string_to_state( self, state_string ):
encoded_state = string_to_object( state_string )
state = DefaultToolState()
state.decode( encoded_state, self.tool, self.app )
return state
def __inputs_to_state( self, inputs ):
tool_state = DefaultToolState()
tool_state.inputs = inputs
return tool_state
def __state_to_string( self, tool_state ):
return object_to_string( tool_state.encode( self.tool, self.app ) )
def __inputs_to_state_string( self, inputs ):
tool_state = self.__inputs_to_state( inputs )
return self.__state_to_string( tool_state )
def __assert_no_errors( self, vars ):
assert "job_errors" in vars
assert not vars[ "job_errors" ]
class MockAction( object ):