diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index c29008de9fb..00696f28f12 100644 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -142,7 +142,7 @@ class DefaultToolState( object ): """ # Convert parameters to a dictionary of strings, and save curent # page in that dict - value = tool.params_to_strings( self.inputs, app ) + value = params_to_strings( tool.inputs, self.inputs, app ) value["__page__"] = self.page value = simplejson.dumps( value ) # Make it secure @@ -161,7 +161,7 @@ class DefaultToolState( object ): # Restore from string values = json_fix( simplejson.loads( value ) ) self.page = values.pop( "__page__" ) - self.inputs = tool.params_from_strings( values, app, ignore_errors=True ) + self.inputs = params_from_strings( tool.inputs, values, app, ignore_errors=True ) class Tool: """ @@ -740,6 +740,7 @@ class Tool: prefix = "%s|" % ( key ) # Deal with the 'test' element and see if it's value changed test_param_key = prefix + input.test_param.name + test_param_error = None if 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 @@ -751,7 +752,7 @@ class Tool: # Get value of test param and determine current case test_incoming = incoming.get( prefix + input.test_param.name, None ) value, test_param_error = \ - self.check_param( trans, input.test_param, test_incoming, context ) + check_param( trans, input.test_param, test_incoming, context ) current_case = input.get_current_case( value, trans ) if current_case != old_current_case: # Current case has changed, throw away old state @@ -786,74 +787,48 @@ class Tool: # preserve the old error message. if input.name in old_errors: errors[ input.name ] = old_errors[ input.name ] - # SelectToolParameters and DataToolParameters whose options are dynamically - # generated based on the current value of a dependency parameter require special - # handling. When the dependency parameter's value is changed, the form is - # submitted ( due to the refresh_on_change behavior ). When this occurs, the - # "dependent" parameter's value has not been reset ( dynamically generated based - # on the new value of its dependency ) prior to reaching this point, so we need - # to regenerate it before it is validated in check_param(). - incoming_value_generated = False - if not( 'runtool_btn' in incoming or 'URL' in incoming ): - # Form must have been refreshed, probably due to a refresh_on_change - try: - if input.is_dynamic: - dependencies = input.get_dependencies() - for dependency_name in dependencies: - dependency_value = changed_dependencies.get( dependency_name, None ) - if dependency_value: - # We need to dynamically generate the current input based on - # the changed dependency parameter - changed_params = {} - changed_params[dependency_name] = dependency_value - changed_params[input.name] = input - incoming_value = input.get_initial_value( trans, changed_params ) - incoming_value_generated = True - # Delete the dependency_param from chagned_dependencies since its - # dependent param has been generated based its new value. - del changed_dependencies[dependency_name] - break - except: - pass - if not incoming_value_generated: - incoming_value = incoming.get( key, None ) - value, error = self.check_param( trans, input, incoming_value, context ) - if input.dependent_params and state[ input.name ] != value: - # We need to keep track of changed dependency parametrs ( parameters - # that have dependent parameters whose options are dynamically generated ) - changed_dependencies[ input.name ] = value - if error: - errors[ input.name ] = error - state[ input.name ] = value + else: + # SelectToolParameters and DataToolParameters whose options are dynamically + # generated based on the current value of a dependency parameter require special + # handling. When the dependency parameter's value is changed, the form is + # submitted ( due to the refresh_on_change behavior ). When this occurs, the + # "dependent" parameter's value has not been reset ( dynamically generated based + # on the new value of its dependency ) prior to reaching this point, so we need + # to regenerate it before it is validated in check_param(). + incoming_value_generated = False + if not( 'runtool_btn' in incoming or 'URL' in incoming ): + # Form must have been refreshed, probably due to a refresh_on_change + try: + if input.is_dynamic: + dependencies = input.get_dependencies() + for dependency_name in dependencies: + dependency_value = changed_dependencies.get( dependency_name, None ) + if dependency_value: + # We need to dynamically generate the current input based on + # the changed dependency parameter + changed_params = {} + changed_params[dependency_name] = dependency_value + changed_params[input.name] = input + incoming_value = input.get_initial_value( trans, changed_params ) + incoming_value_generated = True + # Delete the dependency_param from chagned_dependencies since its + # dependent param has been generated based its new value. + del changed_dependencies[dependency_name] + break + except: + pass + if not incoming_value_generated: + incoming_value = incoming.get( key, None ) + value, error = check_param( trans, input, incoming_value, context ) + if input.dependent_params and state[ input.name ] != value: + # We need to keep track of changed dependency parametrs ( parameters + # that have dependent parameters whose options are dynamically generated ) + changed_dependencies[ input.name ] = value + if error: + errors[ input.name ] = error + state[ input.name ] = value return errors - def check_param( self, trans, param, incoming_value, param_values ): - """ - Check the value of a single parameter `param`. The value in - `incoming_value` is converted from its HTML encoding and validated. - The `param_values` argument contains the processed values of - previous parameters (this may actually be an ExpressionContext - when dealing with grouping scenarios). - """ - value = incoming_value - error = None - try: - if param.name == 'file_data': - pass - elif value is not None or isinstance(param, DataToolParameter): - # Convert value from HTML representation - value = param.from_html( value, trans, param_values ) - # Allow the value to be converted if neccesary - filtered_value = param.filter_value( value, trans, param_values ) - # Then do any further validation on the value - param.validate( filtered_value, trans.history ) - elif value is None and isinstance( param, SelectToolParameter ): - # An empty select list or column list - param.validate( value, trans.history ) - except ValueError, e: - error = str( e ) - return value, error - def get_static_param_values( self, trans ): """ Returns a map of parameter names and values if the tool does not @@ -880,34 +855,10 @@ class Tool: return self.tool_action.execute( self, trans, incoming ) def params_to_strings( self, params, app ): - """ - Convert a dictionary of parameter values to a dictionary of strings - suitable for persisting. The `value_to_basic` method of each parameter - is called to convert its value to basic types, the result of which - is then json encoded (this allowing complex nested parameters and - such). - """ - rval = dict() - for key, value in params.iteritems(): - if key in self.inputs: - value = self.inputs[ key ].value_to_basic( value, app ) - rval[ key ] = str( simplejson.dumps( value ) ) - return rval + return params_to_strings( self.inputs, params, app ) def params_from_strings( self, params, app, ignore_errors=False ): - """ - Convert a dictionary of strings as produced by `params_to_strings` - back into parameter values (decode the json representation and then - allow each parameter to convert the basic types into the parameters - preferred form). - """ - rval = dict() - for key, value in params.iteritems(): - value = json_fix( simplejson.loads( value ) ) - if key in self.inputs: - value = self.inputs[key].value_from_basic( value, app, ignore_errors ) - rval[ key ] = value - return rval + return params_from_strings( self.inputs, params, app, ignore_errors ) def handle_unvalidated_param_values( self, input_values, app ): """ diff --git a/lib/galaxy/tools/parameters/__init__.py b/lib/galaxy/tools/parameters/__init__.py index 2ffcbe37971..2aa3495ac71 100644 --- a/lib/galaxy/tools/parameters/__init__.py +++ b/lib/galaxy/tools/parameters/__init__.py @@ -2,4 +2,62 @@ Classes encapsulating Galaxy tool parameters. """ -from basic import * \ No newline at end of file +from basic import * +from galaxy.util.json import * + +def check_param( trans, param, incoming_value, param_values ): + """ + Check the value of a single parameter `param`. The value in + `incoming_value` is converted from its HTML encoding and validated. + The `param_values` argument contains the processed values of + previous parameters (this may actually be an ExpressionContext + when dealing with grouping scenarios). + """ + value = incoming_value + error = None + try: + if param.name == 'file_data': + pass + elif value is not None or isinstance(param, DataToolParameter): + # Convert value from HTML representation + value = param.from_html( value, trans, param_values ) + # Allow the value to be converted if neccesary + filtered_value = param.filter_value( value, trans, param_values ) + # Then do any further validation on the value + param.validate( filtered_value, trans.history ) + elif value is None and isinstance( param, SelectToolParameter ): + # An empty select list or column list + param.validate( value, trans.history ) + except ValueError, e: + error = str( e ) + return value, error + +def params_to_strings( params, param_values, app ): + """ + Convert a dictionary of parameter values to a dictionary of strings + suitable for persisting. The `value_to_basic` method of each parameter + is called to convert its value to basic types, the result of which + is then json encoded (this allowing complex nested parameters and + such). + """ + rval = dict() + for key, value in param_values.iteritems(): + if key in params: + value = params[ key ].value_to_basic( value, app ) + rval[ key ] = str( to_json_string( value ) ) + return rval + +def params_from_strings( params, param_values, app, ignore_errors=False ): + """ + Convert a dictionary of strings as produced by `params_to_strings` + back into parameter values (decode the json representation and then + allow each parameter to convert the basic types into the parameters + preferred form). + """ + rval = dict() + for key, value in param_values.iteritems(): + value = json_fix( from_json_string( value ) ) + if key in params: + value = params[key].value_from_basic( value, app, ignore_errors ) + rval[ key ] = value + return rval \ No newline at end of file diff --git a/lib/galaxy/util/json.py b/lib/galaxy/util/json.py new file mode 100644 index 00000000000..8e69713bc19 --- /dev/null +++ b/lib/galaxy/util/json.py @@ -0,0 +1,20 @@ + +__all__ = [ "to_json_string", "from_json_string", "json_fix"] + +import pkg_resources +pkg_resources.require( "simplejson" ) + +import simplejson + +to_json_string = simplejson.dumps +from_json_string = simplejson.loads + +def json_fix( val ): + if isinstance( val, list ): + return [ json_fix( v ) for v in val ] + elif isinstance( val, dict ): + return dict( [ ( json_fix( k ), json_fix( v ) ) for ( k, v ) in val.iteritems() ] ) + elif isinstance( val, unicode ): + return val.encode( "utf8" ) + else: + return val \ No newline at end of file diff --git a/lib/galaxy/web/controllers/workflow.py b/lib/galaxy/web/controllers/workflow.py index 06567148db0..7a49dab1297 100644 --- a/lib/galaxy/web/controllers/workflow.py +++ b/lib/galaxy/web/controllers/workflow.py @@ -2,11 +2,12 @@ from galaxy.web.base.controller import * import simplejson -from galaxy.tools.parameters import DataToolParameter +from galaxy.tools.parameters import DataToolParameter, check_param from galaxy.tools import DefaultToolState from galaxy.tools.parameters.grouping import Repeat, Conditional from galaxy.datatypes.data import Data from galaxy.util.odict import odict +from galaxy.util.bunch import Bunch from galaxy.util.topsort import topsort, topsort_levels, CycleError class WorkflowController( BaseController ): @@ -60,7 +61,6 @@ class WorkflowController( BaseController ): # Display the management page return self.index( trans ) - @web.expose @web.require_login( "edit workflows" ) def editor( self, trans, id=None ): @@ -131,9 +131,11 @@ class WorkflowController( BaseController ): rval['name'] = module.name rval['type'] = module.type rval['tool_state'] = state = module.get_state() - rval['data_inputs'] = module.get_data_inputs( state ) - rval['data_outputs'] = module.get_data_outputs( state ) - rval['form_html'] = module.get_config_form_html( trans, state ) + rval['data_inputs'] = module.get_data_inputs() + rval['data_outputs'] = module.get_data_outputs() + rval['form_html'] = trans.fill_template( + "workflow/editor_generic_form.mako", + form = module.get_config_form() ) return rval @web.json @@ -184,9 +186,11 @@ class WorkflowController( BaseController ): module = module_types[step.type].from_workflow_step( step ) step_dict['name'] = module.name step_dict['tool_state'] = state = module.get_state() - step_dict['data_inputs'] = module.get_data_inputs( state ) - step_dict['data_outputs'] = module.get_data_outputs( state ) - step_dict['form_html'] = module.get_config_form_html( trans, state ) + step_dict['data_inputs'] = module.get_data_inputs() + step_dict['data_outputs'] = module.get_data_outputs() + step_dict['form_html'] = trans.fill_template( + "workflow/editor_generic_form.mako", + form = module.get_config_form() ) # Connections input_conn_dict = {} for conn in step.input_connections: @@ -319,9 +323,12 @@ class WorkflowController( BaseController ): if type( job_ids ) == str: job_ids = [ job_ids ] job_ids = [ int( id ) for id in job_ids ] - if type( dataset_ids ) == str: - dataset_ids = [ job_ids ] - dataset_ids = [ int( id ) for id in dataset_ids ] + if dataset_ids: + if type( dataset_ids ) == str: + dataset_ids = [ job_ids ] + dataset_ids = [ int( id ) for id in dataset_ids ] + else: + dataset_ids = [] # Find each job, for security we (implicately) check that they are # associated witha job in the current history. jobs, warnings = get_job_dict( trans ) @@ -335,7 +342,6 @@ class WorkflowController( BaseController ): step.type = 'data_input' hid_to_output_pair[ hid ] = ( step, 'output' ) steps.append( step ) - print hid_to_output_pair # Tool steps for job_id in job_ids: assert job_id in jobs_by_id, "Attempt to create workflow with job not connected to current history" @@ -344,6 +350,7 @@ class WorkflowController( BaseController ): param_values = job.get_param_values( trans.app ) associations = cleanup_param_values( tool.inputs, param_values ) step = model.WorkflowStep() + step.type = 'tool' step.tool_id = job.tool_id step.tool_inputs = tool.params_to_strings( param_values, trans.app ) # NOTE: We shouldn't need to do two passes here since only @@ -363,7 +370,6 @@ class WorkflowController( BaseController ): # Store created dataset hids for assoc in job.output_datasets: hid_to_output_pair[ assoc.dataset.hid ] = ( step, assoc.name ) - print hid_to_output_pair # Workflow to populate workflow = model.Workflow() workflow.name = workflow_name @@ -418,54 +424,82 @@ class WorkflowController( BaseController ): p = "%s|" % step.id l = len(p) step_args = dict( ( k[l:], v ) for ( k, v ) in kwargs.iteritems() if k.startswith( p ) ) - if step.type == 'tool': + step_errors = None + if step.type == 'tool' or step.type is None: # Get the tool tool = trans.app.toolbox.tools_by_id[ step.tool_id ] # Get the state state = DefaultToolState() state.decode( step_args.pop("tool_state"), tool, trans.app ) step.state = state - # Connections by input name - step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) # Get old errors old_errors = state.inputs.pop( "__errors__", {} ) # Update the state step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args, update_only=True, old_errors=old_errors ) - if step_errors: - errors[step.id] = state.inputs["__errors__"] = step_errors else: - return error( "Modules not yet supported for running" ) + module = step.module = WorkflowModule.from_workflow_step( step ) + state = step.state = module.decode_runtime_state( trans, step_args.pop( "tool_state" ) ) + step_errors = module.update_runtime_state( trans, state, step_args ) + if step_errors: + errors[step.id] = state.inputs["__errors__"] = step_errors + # Connections by input name + step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) if not errors: # Run each step, connecting outputs to inputs outputs = {} for step in workflow.steps: - tool = trans.app.toolbox.tools_by_id[ step.tool_id ] - inputs = step.state.inputs - # Connect up - for conn in step.input_connections: - inputs[ conn.input_name ] = outputs[ conn.output_step.id ][ conn.output_name ] - outputs[ step.id ] = tool.execute( trans, step.state.inputs ) + if step.type == 'tool' or step.type is None: + tool = trans.app.toolbox.tools_by_id[ step.tool_id ] + input_values = step.state.inputs + # Connect up + # TODO: Generalize out visitor + def visitor( inputs, input_values, prefix ): + for input in inputs.itervalues(): + if isinstance( input, Repeat ): + for i, d in enumerate( input_values[ input.name ] ): + index = d['__index__'] + new_prefix = prefix + "%s_%d|" % ( input.name, index ) + visitor( input.inputs, d, new_prefix) + elif isinstance( input, Conditional ): + values = input_values[ input.name ] + current = values["__current_case__"] + label_prefix = label_prefix + prefix = prefix + "|" + input.name + visitor( input.cases[current].inputs, values, prefix ) + else: + if isinstance( input, DataToolParameter ): + prefixed_name = prefix + input.name + if prefixed_name in step.input_connections_by_name: + conn = step.input_connections_by_name[ prefixed_name ] + input_values[ input.name ] = outputs[ conn.output_step.id ][ conn.output_name ] + visitor( tool.inputs, input_values, "" ) + # Execute it + outputs[ step.id ] = tool.execute( trans, step.state.inputs ) + else: + outputs[ step.id ] = step.module.execute( trans, step.state ) return trans.fill_template( "workflow/run_complete.mako", workflow=stored, outputs=outputs ) else: for step in workflow.steps: - if step.type == 'tool': + if step.type == 'tool' or step.type is None: # Build a new tool state for the step tool = trans.app.toolbox.tools_by_id[ step.tool_id ] state = DefaultToolState() state.inputs = tool.params_from_strings( step.tool_inputs, trans.app ) # Store state with the step step.state = state - # Connections by input name - step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) # This should never actually happen since we don't allow # running workflows with errors (yet?) if step.tool_errors: errors[step.id] = step.tool_errors else: - return error( "Modules not yet supported for running" ) + ## Non-tool specific stuff? + step.module = WorkflowModule.from_workflow_step( step ) + step.state = step.module.get_runtime_state() + # Connections by input name + step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) # Render the form return trans.fill_template( "workflow/run.mako", @@ -475,29 +509,66 @@ class WorkflowController( BaseController ): ## ---- Workflow modules (to be factored out) --------------------------------- +from elementtree.ElementTree import Element + ## TODO: 'Tool' should be a module rather than a special case -class InputDataModule( object ): +class WorkflowModule( object ): + @classmethod + def from_workflow_step( cls, step ): + assert step.type in module_types, "Unknown module type" + return module_types[ step.type ].from_workflow_step( step ) + +class InputDataModule( WorkflowModule ): type = "data_input" name = "Input dataset" + _inputs = { + 'input' : DataToolParameter( None, Element( "param", name="input", label="Input Dataset", type="data", format="data" ) ) + } @classmethod def from_state( cls, state ): return cls() @classmethod - def from_workflow_step( cls, state ): + def from_workflow_step( cls, step ): return cls() def get_state( self ): return None - def get_data_inputs( self, state ): - return [] - def get_data_outputs( self, state ): - return [ dict( name='output', extension='input' ) ] - def get_config_form_html( self, trans, state ): - form = web.FormBuilder( title=self.name ) - return trans.fill_template( "workflow/editor_generic_form.mako", form=form ) def save_to_step( self, step ): pass + def get_data_inputs( self ): + return [] + def get_data_outputs( self ): + return [ dict( name='output', extension='input' ) ] + def get_config_form( self ): + return web.FormBuilder( title=self.name ) + + def get_runtime_inputs( self ): + return self._inputs + def get_runtime_state( self ): + state = DefaultToolState() + state.inputs = dict( input=None ) + return state + def encode_runtime_state( self, trans, state ): + fake_tool = Bunch( inputs = self.get_runtime_inputs() ) + return state.encode( fake_tool, trans.app ) + def decode_runtime_state( self, trans, string ): + fake_tool = Bunch( inputs = self.get_runtime_inputs() ) + state = DefaultToolState() + state.decode( string, fake_tool, trans.app ) + return state + def update_runtime_state( self, trans, state, values ): + errors = {} + for name, param in self._inputs.iteritems(): + value, error = check_param( trans, param, values[name], values ) + state.inputs[ name ] = value + if error: + errors[ name ] = error + return errors + + def execute( self, trans, state ): + return dict( output=state.inputs['input']) + module_types = dict( data_input=InputDataModule ) ## ---- Utility methods ------------------------------------------------------- diff --git a/static/scripts/galaxy.workflow_editor.canvas.js b/static/scripts/galaxy.workflow_editor.canvas.js index 02f454ba421..57ae792cb62 100644 --- a/static/scripts/galaxy.workflow_editor.canvas.js +++ b/static/scripts/galaxy.workflow_editor.canvas.js @@ -257,7 +257,9 @@ $.extend( Node.prototype, { }, init_field_data : function ( data ) { var f = this.element; - this.type = data.type + if ( data.type ) { + this.type = data.type; + } this.form_html = data.form_html; this.tool_state = data.tool_state; this.tool_errors = data.tool_errors; @@ -281,7 +283,11 @@ $.extend( Node.prototype, { $.each( data.data_outputs, function( i, output ) { var t = $( "
" ); node.enable_output_terminal( t, output.name, output.extension ); - b.append( $("
" + output.name + "
" ).append( t ) ); + var label = output.name + if ( output.extension != 'input' ) { + label = label + " (" + output.extension + ")"; + } + b.append( $("
" + label + "
" ).append( t ) ); }); workflow.node_changed( this ); }, diff --git a/templates/workflow/editor.mako b/templates/workflow/editor.mako index 12bb6391028..9c8c5d0f693 100644 --- a/templates/workflow/editor.mako +++ b/templates/workflow/editor.mako @@ -7,8 +7,8 @@ @@ -20,6 +20,7 @@ + @@ -73,7 +74,7 @@ // Load workflow definition $.ajax( { url: "${h.url_for( action='load_workflow' )}", - data: { id: "${trans.security.encode_id( workflow_id )}" }, + data: { id: "${trans.security.encode_id( workflow_id )}", "_": "true" }, dataType: 'json', success: function( data ) { reset(); @@ -147,66 +148,6 @@ } }); }); - - // Scrolling - $.ui.plugin.add("draggable", "scrollPanel", { - drag: function(e, ui) { - var o = ui.options, - i = ui.instance, - panel = o.panel, - panel_pos = panel.position(), - viewport = panel.parent(); - moved = false; - nudge = 23; - // Legal panel range - var p_min_x = - ( panel.width() - viewport.width() ), - p_min_y = - ( panel.height() - viewport.height() ), - p_max_x = 0, - p_max_y = 0; - var min_vis_x = - panel_pos.left, - max_vis_x = min_vis_x + viewport.width(), - min_vis_y = - panel_pos.top, - max_vis_y = min_vis_y + viewport.height(); - // Move it - if ( ( panel_pos.left < p_max_x ) && ( ui.position.left < min_vis_x ) ) { - var t = Math.min( nudge, p_max_x - panel_pos.left ); - panel.css( "left", panel_pos.left + t ); - moved = true; - ui.instance.offset.left += t; - ui.position.left = min_vis_x - t - } - if ( ( panel_pos.left > p_min_x ) && ( ( ui.position.left + ui.element.width() ) > max_vis_x ) ) { - var t = Math.min( nudge, panel_pos.left - p_min_x ); - panel.css( "left", panel_pos.left - t ); - moved = true; - ui.instance.offset.left -= t - ui.position.left = max_vis_x + t - ui.element.width(); - } - if ( ( panel_pos.top < p_max_y ) && ( ui.position.top < min_vis_y ) ) { - var t = Math.min( nudge, p_max_y - panel_pos.top ); - panel.css( "top", panel_pos.top + t ); - moved = true; - ui.instance.offset.top += t; - ui.position.top = min_vis_y - t; - } - if ( ( panel_pos.top > p_min_y ) && ( ( ui.position.top + ui.element.height() ) > max_vis_y ) ) { - var t = Math.min( nudge, panel_pos.top - p_min_x ); - panel.css( "top", panel_pos.top - t ); - moved = true; - ui.instance.offset.top -= t - ui.position.top = max_vis_y + t - ui.element.height(); - } - // Update offsets - if ( moved ) { - $.ui.ddmanager.prepareOffsets( ui.instance, e ); - } - // Still contain in panel - ui.position.left = Math.max( ui.position.left, 0 ); - ui.position.top = Math.max( ui.position.top, 0 ); - ui.position.left = Math.min( ui.position.left, panel.width() - ui.element.width() ); - ui.position.top = Math.min( ui.position.top, panel.height() - ui.element.height() ); - } - }); }); var workflow = null; @@ -226,8 +167,10 @@ $("div.toolFormInCanvas").each( function() { x = Math.min( x, $(this).position().left ); y = Math.min( x, $(this).position().left ); - }); - $("#canvas-container").css( { left: - x + 20, top: - y + 20 } ); + }); + x = Math.min( - x + 20, 0 ); + y = Math.min( - y + 20, 0 ); + $("#canvas-container").css( { left: x, top: y } ); } } @@ -238,7 +181,8 @@ workflow.activate_node( node ); $.ajax( { url: "${h.url_for( action='get_tool_info' )}", - data: { tool_id: id, "_": "true" }, + data: { tool_id: id, "_": "true" }, + global: false, dataType: "json", success: function( data ) { node.init_field_data( data ); @@ -292,6 +236,10 @@ beforeSubmit: function( data ) { data.push( { name: 'tool_state', value: node.tool_state } ); data.push( { name: '_', value: "true" } ); + $("#tool-form-save-button").each( function() { + this.value = "Saving..."; + this.disabled = true; + }); } }).each( function() { form = this; @@ -324,7 +272,8 @@ type: "POST", data: { id: "${trans.security.encode_id( workflow_id )}", - workflow_data: $.toJSON( workflow.to_simple() ) + workflow_data: $.toJSON( workflow.to_simple() ), + "_": "true" }, dataType: 'json', success: function( data ) { @@ -349,48 +298,6 @@ }); } - var load_workflow = function () { - var body = $("#load-dialog-form").clone(); - var form = body.find( "form" ).ajaxForm( { - dataType: 'json', - success: function( data ) { - reset(); - workflow.from_simple( data ); - workflow.has_changes = false; - show_modal( "Workflow loaded", "Workflow loaded.", { - "Ok" : function () { hide_modal(); } - }); - }, - beforeSubmit: function( data ) { - show_modal( "Loading workflow", "progress" ); - } - }); - if ( workflow.has_changes ) { - body.prepend( "
Your unsaved changes will be lost!
" ); - } - show_modal( "Load workflow", body, { - "Cancel" : hide_modal, - "Load": function() { form.submit() } - } ); - } - - var clear_workflow = function () { - reset(); - hide_modal(); - } - - var create_new_workflow_dialog = function () { - if ( workflow.has_changes ) { - show_modal( "Create new workflow", - "Your workflow has unsaved changes which will be lost", { - "Cancel" : hide_modal, - "Continue": clear_workflow - }); - } else { - clear_workflow(); - } - } - diff --git a/templates/workflow/editor_tool_form.mako b/templates/workflow/editor_tool_form.mako index da75062e777..9bdf20552a6 100644 --- a/templates/workflow/editor_tool_form.mako +++ b/templates/workflow/editor_tool_form.mako @@ -65,7 +65,7 @@ ${do_inputs( inputs, values, errors, "" )} %endfor
- +
diff --git a/templates/workflow/run.mako b/templates/workflow/run.mako index 8b7bd9f8f22..d8afb240ad7 100644 --- a/templates/workflow/run.mako +++ b/templates/workflow/run.mako @@ -31,10 +31,10 @@ from galaxy.tools.parameters import DataToolParameter
${input.title} ${i + 1}
${do_inputs( input.inputs, repeat_values[ i ], rep_errors, prefix + input.name + "_" + str(i) + "|", step )} -
-
+ ##
+ %endfor -
+ ##
%elif input.type == "conditional": <% group_values = values[input.name] %> @@ -50,6 +50,7 @@ from galaxy.tools.parameters import DataToolParameter <%def name="row_for_param( param, value, error_dict, prefix, step )"> + ## -- ${param.name} -- ${step.state.inputs} -- %if error_dict.has_key( param.name ): <% cls = "form-row form-row-error" %> %else: @@ -86,15 +87,25 @@ from galaxy.tools.parameters import DataToolParameter ## %for i, step in enumerate( steps ): - <% tool = app.toolbox.tools_by_id[step.tool_id] %> - -
-
Step ${int(step.order_index)+1}: ${tool.name}
-
- ${do_inputs( tool.inputs, step.state.inputs, errors.get( step.id, dict() ), "", step )} -
-
- + %if step.type == 'tool' or step.type is None: + <% tool = app.toolbox.tools_by_id[step.tool_id] %> + +
+
Step ${int(step.order_index)+1}: ${tool.name}
+
+ ${do_inputs( tool.inputs, step.state.inputs, errors.get( step.id, dict() ), "", step )} +
+
+ %else: + <% module = step.module %> + +
+
Step ${int(step.order_index)+1}: ${module.name}
+
+ ${do_inputs( module.get_runtime_inputs(), step.state.inputs, errors.get( step.id, dict() ), "", step )} +
+
+ %endif %endfor \ No newline at end of file