From 8cfa45310eadc4713ec1aa3a2c3145236819c593 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Fri, 9 May 2008 16:18:38 +0000 Subject: [PATCH] "Input dataset" module now works for running. Many bug fixes to workflows and tools. Moved some methods related to params out of the Tool class, sets of inputs can also be used in other ways (e.g. for modules). More abstraction needed here. --- lib/galaxy/tools/__init__.py | 141 ++++++----------- lib/galaxy/tools/parameters/__init__.py | 60 ++++++- lib/galaxy/util/json.py | 20 +++ lib/galaxy/web/controllers/workflow.py | 147 +++++++++++++----- .../scripts/galaxy.workflow_editor.canvas.js | 10 +- templates/workflow/editor.mako | 125 ++------------- templates/workflow/editor_tool_form.mako | 2 +- templates/workflow/run.mako | 35 +++-- 8 files changed, 282 insertions(+), 258 deletions(-) create mode 100644 lib/galaxy/util/json.py 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