mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Workflow: create a new python module for 'WorkflowModules'. Tools are now another type of module, which removes a lot of special cases in the controller. Fixed some problems with validation and parameter groups.
This commit is contained in:
@@ -9,6 +9,7 @@ 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
|
||||
from galaxy.workflow.modules import *
|
||||
|
||||
class WorkflowController( BaseController ):
|
||||
beta = True
|
||||
@@ -31,7 +32,7 @@ class WorkflowController( BaseController ):
|
||||
"""
|
||||
user = trans.get_user()
|
||||
if not workflow_name:
|
||||
return error( "Must provide a name for the new workflow" )
|
||||
error( "Must provide a name for the new workflow" )
|
||||
# Create the new stored workflow
|
||||
stored_workflow = model.StoredWorkflow()
|
||||
stored_workflow.name = workflow_name
|
||||
@@ -70,13 +71,12 @@ class WorkflowController( BaseController ):
|
||||
rendered by `editor_canvas`.
|
||||
"""
|
||||
if not id:
|
||||
return trans.show_error_message( "Invalid workflow id" )
|
||||
error( "Invalid workflow id" )
|
||||
id = trans.security.decode_id( id )
|
||||
return trans.fill_template( "workflow/editor.mako",
|
||||
workflow_id=id )
|
||||
return trans.fill_template( "workflow/editor.mako", workflow_id=id )
|
||||
|
||||
@web.json
|
||||
def editor_tool_form( self, trans, tool_id=None, **incoming ):
|
||||
def editor_tool_form( self, trans, type='tool', tool_id=None, **incoming ):
|
||||
"""
|
||||
Accepts a tool state and incoming values, and generates a new tool
|
||||
form and some additional information, packed into a json dictionary.
|
||||
@@ -84,59 +84,39 @@ class WorkflowController( BaseController ):
|
||||
is selected.
|
||||
"""
|
||||
trans.workflow_building_mode = True
|
||||
tool = trans.app.toolbox.tools_by_id[tool_id]
|
||||
state = DefaultToolState()
|
||||
state.decode( incoming.pop("tool_state"), tool, trans.app )
|
||||
errors = tool.update_state( trans, tool.inputs, state.inputs, incoming )
|
||||
rval = {}
|
||||
rval['form_html'] = trans.fill_template( "workflow/editor_tool_form.mako",
|
||||
tool=tool, as_html=as_html, values=state.inputs, errors=errors )
|
||||
if errors:
|
||||
rval['tool_errors'] = errors
|
||||
else:
|
||||
rval['tool_errors'] = None
|
||||
# Updated data_inputs
|
||||
rval['data_inputs'] = get_data_inputs( tool.inputs, state.inputs )
|
||||
rval['state'] = state.encode( tool, trans.app )
|
||||
return rval
|
||||
module = module_factory.from_dict( trans, {
|
||||
'type': type,
|
||||
'tool_id': tool_id,
|
||||
'tool_state': incoming.pop("tool_state")
|
||||
} )
|
||||
module.update_state( incoming )
|
||||
return {
|
||||
'tool_state': module.get_state(),
|
||||
'data_inputs': module.get_data_inputs(),
|
||||
'data_outputs': module.get_data_outputs(),
|
||||
'form_html': module.get_config_form()
|
||||
}
|
||||
|
||||
@web.json
|
||||
def get_tool_info( self, trans, tool_id ):
|
||||
def get_new_module_info( self, trans, type, **kwargs ):
|
||||
"""
|
||||
Generate a json dictionary containing info about the tool specified by
|
||||
`tool_id`. Info includes data inputs and outputs, html representation
|
||||
Get the info for a new instance of a module initialized with default
|
||||
paramters (any keyword arguments will be passed along to the module).
|
||||
Result includes data inputs and outputs, html representation
|
||||
of the initial form, and the initial tool state (with default values).
|
||||
This is called asynchronously whenever a new node is added.
|
||||
"""
|
||||
trans.workflow_building_mode = True
|
||||
tool = trans.app.toolbox.tools_by_id[tool_id]
|
||||
rval = {}
|
||||
rval['name'] = tool.name
|
||||
rval['tool_id'] = tool.id
|
||||
data_outputs = []
|
||||
for name, ( format, metadata_source, parent ) in tool.outputs.iteritems():
|
||||
data_outputs.append( dict( name=name, extension=format ) )
|
||||
rval['data_outputs'] = data_outputs
|
||||
state = tool.new_state( trans, all_pages=True )
|
||||
rval['form_html'] = trans.fill_template( "workflow/editor_tool_form.mako",
|
||||
tool=tool, as_html=as_html, values=state.inputs, errors={} )
|
||||
rval['tool_state'] = state.encode( tool, trans.app )
|
||||
rval['data_inputs'] = get_data_inputs( tool.inputs, state.inputs )
|
||||
return rval
|
||||
|
||||
@web.json
|
||||
def get_module_info( self, trans, type ):
|
||||
module = module_types[type]()
|
||||
rval = {}
|
||||
rval['name'] = module.name
|
||||
rval['type'] = module.type
|
||||
rval['tool_state'] = state = module.get_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
|
||||
module = module_factory.new( trans, type, **kwargs )
|
||||
return {
|
||||
'type': module.type,
|
||||
'name': module.get_name(),
|
||||
'tool_id': module.get_tool_id(),
|
||||
'tool_state': module.get_state(),
|
||||
'data_inputs': module.get_data_inputs(),
|
||||
'data_outputs': module.get_data_outputs(),
|
||||
'form_html': module.get_config_form()
|
||||
}
|
||||
|
||||
@web.json
|
||||
def load_workflow( self, trans, id ):
|
||||
@@ -158,39 +138,18 @@ class WorkflowController( BaseController ):
|
||||
data['steps'] = {}
|
||||
# For each step, rebuild the form and encode the state
|
||||
for step in workflow.steps:
|
||||
step_dict = {}
|
||||
step_dict['id'] = step.order_index
|
||||
step_dict['type'] = step_type = ( step.type or "tool" )
|
||||
if step_type == 'tool':
|
||||
step_dict['tool_id'] = tool_id = step.tool_id
|
||||
# Load tool
|
||||
tool = trans.app.toolbox.tools_by_id[tool_id]
|
||||
# Build a state from the tool_inputs dict
|
||||
state = DefaultToolState()
|
||||
state.inputs = tool.params_from_strings( step.tool_inputs, trans.app, ignore_errors=True )
|
||||
step_dict['tool_state'] = state.encode( tool, trans.app )
|
||||
# Error messages for the tool
|
||||
step_dict['tool_errors'] = ( step.tool_errors or None )
|
||||
# Input and output specs
|
||||
step_dict['data_inputs'] = get_data_inputs( tool.inputs, state.inputs )
|
||||
data_outputs = []
|
||||
for name, ( format, metadata_source, parent ) in tool.outputs.iteritems():
|
||||
data_outputs.append( dict( name=name, extension=format ) )
|
||||
step_dict['data_outputs'] = data_outputs
|
||||
# Build the tool form html
|
||||
errors = step.tool_errors
|
||||
step_dict['form_html'] = trans.fill_template( "workflow/editor_tool_form.mako",
|
||||
tool=tool, as_html=as_html, values=state.inputs, errors=( step.tool_errors or {} ) )
|
||||
step_dict['name'] = tool.name
|
||||
else:
|
||||
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()
|
||||
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() )
|
||||
module = module_factory.from_workflow_step( trans, step )
|
||||
step_dict = {
|
||||
'id': step.order_index,
|
||||
'type': module.type,
|
||||
'tool_id': module.get_tool_id(),
|
||||
'name': module.get_name(),
|
||||
'tool_state': module.get_state(),
|
||||
'tool_errors': module.get_errors(),
|
||||
'data_inputs': module.get_data_inputs(),
|
||||
'data_outputs': module.get_data_outputs(),
|
||||
'form_html': module.get_config_form()
|
||||
}
|
||||
# Connections
|
||||
input_conn_dict = {}
|
||||
for conn in step.input_connections:
|
||||
@@ -229,25 +188,14 @@ class WorkflowController( BaseController ):
|
||||
for key, step_dict in data['steps'].iteritems():
|
||||
# Create the model class for the step
|
||||
step = model.WorkflowStep()
|
||||
step.type = step_type = step_dict['type']
|
||||
steps.append( step )
|
||||
steps_by_external_id[ step_dict['id' ] ] = step
|
||||
# FIXME: Position should be handled inside module
|
||||
step.position = step_dict['position']
|
||||
if step_type == 'tool':
|
||||
step.tool_id = step_dict['tool_id']
|
||||
# Decode the tool state from the step dict
|
||||
tool = trans.app.toolbox.tools_by_id[ step_dict['tool_id'] ]
|
||||
state = DefaultToolState()
|
||||
state.decode( step_dict['tool_state'], tool, trans.app )
|
||||
# Convert back to strings for database
|
||||
tool_inputs = tool.params_to_strings( state.inputs, trans.app )
|
||||
step.tool_inputs = tool_inputs
|
||||
step.tool_errors = step_dict.get( 'tool_errors', None )
|
||||
if step.tool_errors:
|
||||
workflow.has_errors = True
|
||||
else:
|
||||
module = module_types[step_type].from_state( step_dict['tool_state'] )
|
||||
module.save_to_step( step )
|
||||
module = module_factory.from_dict( trans, step_dict )
|
||||
module.save_to_step( step )
|
||||
if step.tool_errors:
|
||||
workflow.has_errors = True
|
||||
# Stick this in the step temporarily
|
||||
step.temp_input_connections = step_dict['input_connections']
|
||||
# Second pass to deal with connections between steps
|
||||
@@ -405,15 +353,12 @@ class WorkflowController( BaseController ):
|
||||
workflow = stored.latest_workflow
|
||||
# It is possible for a workflow to have 0 steps
|
||||
if len( workflow.steps ) == 0:
|
||||
return trans.show_error_message(
|
||||
"Workflow cannot be run because it does not have any steps" )
|
||||
error( "Workflow cannot be run because it does not have any steps" )
|
||||
#workflow = Workflow.from_simple( simplejson.loads( stored.encoded_value ), trans.app )
|
||||
if workflow.has_cycles:
|
||||
return trans.show_error_message(
|
||||
"Workflow cannot be run because it contains cycles" )
|
||||
error( "Workflow cannot be run because it contains cycles" )
|
||||
if workflow.has_errors:
|
||||
return trans.show_error_message(
|
||||
"Workflow cannot be run because of validation errors in some steps" )
|
||||
error( "Workflow cannot be run because of validation errors in some steps" )
|
||||
# Build the state for each step
|
||||
errors = {}
|
||||
if kwargs:
|
||||
@@ -438,7 +383,7 @@ class WorkflowController( BaseController ):
|
||||
step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args,
|
||||
update_only=True, old_errors=old_errors )
|
||||
else:
|
||||
module = step.module = WorkflowModule.from_workflow_step( step )
|
||||
module = step.module = module_factory.from_workflow_step( trans, 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:
|
||||
@@ -495,7 +440,7 @@ class WorkflowController( BaseController ):
|
||||
errors[step.id] = step.tool_errors
|
||||
else:
|
||||
## Non-tool specific stuff?
|
||||
step.module = WorkflowModule.from_workflow_step( step )
|
||||
step.module = module_factory.from_workflow_step( trans, 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 )
|
||||
@@ -505,70 +450,6 @@ class WorkflowController( BaseController ):
|
||||
steps=workflow.steps,
|
||||
workflow=stored,
|
||||
errors=errors )
|
||||
|
||||
## ---- Workflow modules (to be factored out) ---------------------------------
|
||||
|
||||
from elementtree.ElementTree import Element
|
||||
|
||||
## TODO: 'Tool' should be a module rather than a special case
|
||||
|
||||
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, step ):
|
||||
return cls()
|
||||
def get_state( self ):
|
||||
return None
|
||||
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 -------------------------------------------------------
|
||||
|
||||
@@ -589,12 +470,6 @@ def get_stored_workflow( trans, id ):
|
||||
error( "Workflow is not owned by current user" )
|
||||
# Looks good
|
||||
return stored
|
||||
|
||||
def as_html( param, value, trans, prefix ):
|
||||
if type( param ) is DataToolParameter:
|
||||
return "Data input '" + param.name + "' (" + ( " or ".join( param.extensions ) ) + ")"
|
||||
else:
|
||||
return param.get_html_field( trans, value ).get_html( prefix )
|
||||
|
||||
def attach_ordered_steps( workflow, steps ):
|
||||
ordered_steps = order_workflow_steps( steps )
|
||||
@@ -700,32 +575,4 @@ def cleanup_param_values( inputs, values ):
|
||||
prefix = "%s|" % ( key )
|
||||
cleanup( prefix, input.cases[current_case].inputs, group_values )
|
||||
cleanup( "", inputs, values )
|
||||
return associations
|
||||
|
||||
def get_data_inputs( inputs, input_values ):
|
||||
"""
|
||||
For a set of input definitions and a tool state, find all of the dataset
|
||||
inputs
|
||||
"""
|
||||
data_inputs = []
|
||||
def visitor( inputs, input_values, name_prefix, label_prefix ):
|
||||
for input in inputs.itervalues():
|
||||
if isinstance( input, Repeat ):
|
||||
for i, d in enumerate( input_values[ input.name ] ):
|
||||
index = d['__index__']
|
||||
new_name_prefix = name_prefix + "%s_%d|" % ( input.name, index )
|
||||
new_label_prefix = label_prefix + "%s %d > " % ( input.title, i + 1 )
|
||||
visitor( input.inputs, d, new_name_prefix, new_label_prefix )
|
||||
elif isinstance( input, Conditional ):
|
||||
values = input_values[ input.name ]
|
||||
current = values["__current_case__"]
|
||||
label_prefix = label_prefix
|
||||
name_prefix = name_prefix + "|" + input.name
|
||||
visitor( input.cases[current].inputs, values, name_prefix, label_prefix )
|
||||
else:
|
||||
if isinstance( input, DataToolParameter ):
|
||||
data_inputs.append( dict( name=name_prefix+input.name, label=label_prefix+input.label, extensions=input.extensions ) )
|
||||
visitor( inputs, input_values, "", "" )
|
||||
return data_inputs
|
||||
|
||||
|
||||
return associations
|
||||
@@ -0,0 +1,232 @@
|
||||
from elementtree.ElementTree import Element
|
||||
|
||||
from galaxy import web
|
||||
from galaxy.tools.parameters import DataToolParameter, check_param
|
||||
from galaxy.tools import DefaultToolState
|
||||
from galaxy.tools.parameters.grouping import Repeat, Conditional
|
||||
from galaxy.util.bunch import Bunch
|
||||
|
||||
class WorkflowModule( object ):
|
||||
|
||||
def __init__( self, trans ):
|
||||
self.trans = trans
|
||||
|
||||
## ---- Creating modules from various representations ---------------------
|
||||
|
||||
@classmethod
|
||||
def new( Class, trans, tool_id=None ):
|
||||
"""
|
||||
Create a new instance of the module with default state
|
||||
"""
|
||||
return Class( trans )
|
||||
@classmethod
|
||||
def from_dict( Class, trans, d ):
|
||||
"""
|
||||
Create a new instance of the module initialized from values in the
|
||||
dictionary `d`.
|
||||
"""
|
||||
return Class( trans )
|
||||
@classmethod
|
||||
def from_workflow_step( Class, trans, step ):
|
||||
return Class( trans )
|
||||
|
||||
## ---- Saving in various forms ------------------------------------------
|
||||
|
||||
def save_to_step( self, step ):
|
||||
step.type = self.type
|
||||
|
||||
## ---- General attributes -----------------------------------------------
|
||||
|
||||
def get_type( self ):
|
||||
return self.type
|
||||
def get_name( self ):
|
||||
return self.name
|
||||
def get_tool_id( self ):
|
||||
return None
|
||||
|
||||
## ---- Configuration time -----------------------------------------------
|
||||
|
||||
def get_state( self ):
|
||||
return None
|
||||
def get_errors( self ):
|
||||
return None
|
||||
def get_data_inputs( self ):
|
||||
return []
|
||||
def get_data_outputs( self ):
|
||||
return []
|
||||
def update_state( self ):
|
||||
pass
|
||||
def get_config_form( self ):
|
||||
raise TypeError( "Abstract method" )
|
||||
|
||||
## ---- Run time ---------------------------------------------------------
|
||||
|
||||
def get_runtime_inputs( self ):
|
||||
raise TypeError( "Abstract method" )
|
||||
def get_runtime_state( self ):
|
||||
raise TypeError( "Abstract method" )
|
||||
def encode_runtime_state( self, trans, state ):
|
||||
raise TypeError( "Abstract method" )
|
||||
def decode_runtime_state( self, trans, string ):
|
||||
raise TypeError( "Abstract method" )
|
||||
def update_runtime_state( self, trans, state, values ):
|
||||
raise TypeError( "Abstract method" )
|
||||
|
||||
def execute( self, trans, state ):
|
||||
raise TypeError( "Abstract method" )
|
||||
|
||||
class InputDataModule( WorkflowModule ):
|
||||
type = "data_input"
|
||||
name = "Input dataset"
|
||||
_runtime_inputs = {
|
||||
'input' : DataToolParameter( None, Element( "param", name="input", label="Input Dataset", type="data", format="data" ) )
|
||||
}
|
||||
|
||||
def get_data_inputs( self ):
|
||||
return []
|
||||
def get_data_outputs( self ):
|
||||
return [ dict( name='output', extension='input' ) ]
|
||||
def get_config_form( self ):
|
||||
return self.trans.fill_template( "workflow/editor_generic_form.mako",
|
||||
form = web.FormBuilder( title=self.name ) )
|
||||
|
||||
def get_runtime_inputs( self ):
|
||||
return self._runtime_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._runtime_inputs.iteritems():
|
||||
value, error = check_param( trans, param, values.get( name, None ), values )
|
||||
state.inputs[ name ] = value
|
||||
if error:
|
||||
errors[ name ] = error
|
||||
return errors
|
||||
|
||||
def execute( self, trans, state ):
|
||||
return dict( output=state.inputs['input'])
|
||||
|
||||
class ToolModule( object ):
|
||||
|
||||
type = "tool"
|
||||
|
||||
def __init__( self, trans, tool_id ):
|
||||
self.trans = trans
|
||||
self.tool_id = tool_id
|
||||
self.tool = trans.app.toolbox.tools_by_id[ tool_id ]
|
||||
self.state = None
|
||||
self.errors = None
|
||||
|
||||
@classmethod
|
||||
def new( Class, trans, tool_id=None ):
|
||||
module = Class( trans, tool_id )
|
||||
module.state = module.tool.new_state( trans, all_pages=True )
|
||||
return module
|
||||
@classmethod
|
||||
def from_dict( Class, trans, d ):
|
||||
tool_id = d['tool_id']
|
||||
module = Class( trans, tool_id )
|
||||
module.state = DefaultToolState()
|
||||
module.state.decode( d["tool_state"], module.tool, module.trans.app )
|
||||
module.errors = d.get( "tool_errors", None )
|
||||
return module
|
||||
|
||||
@classmethod
|
||||
def from_workflow_step( Class, trans, step ):
|
||||
tool_id = step.tool_id
|
||||
module = Class( trans, tool_id )
|
||||
module.state = DefaultToolState()
|
||||
print step.tool_inputs
|
||||
module.state.inputs = module.tool.params_from_strings( step.tool_inputs, trans.app )
|
||||
module.errors = step.tool_errors
|
||||
return module
|
||||
|
||||
def save_to_step( self, step ):
|
||||
step.type = self.type
|
||||
step.tool_id = self.tool_id
|
||||
step.tool_inputs = self.tool.params_to_strings( self.state.inputs, self.trans.app )
|
||||
step.tool_errors = self.errors
|
||||
|
||||
def get_name( self ):
|
||||
return self.tool.name
|
||||
def get_tool_id( self ):
|
||||
return self.tool_id
|
||||
def get_state( self ):
|
||||
return self.state.encode( self.tool, self.trans.app )
|
||||
def get_errors( self ):
|
||||
return self.errors
|
||||
|
||||
def get_data_inputs( self ):
|
||||
data_inputs = []
|
||||
def visitor( inputs, input_values, name_prefix, label_prefix ):
|
||||
for input in inputs.itervalues():
|
||||
if isinstance( input, Repeat ):
|
||||
for i, d in enumerate( input_values[ input.name ] ):
|
||||
index = d['__index__']
|
||||
new_name_prefix = name_prefix + "%s_%d|" % ( input.name, index )
|
||||
new_label_prefix = label_prefix + "%s %d > " % ( input.title, i + 1 )
|
||||
visitor( input.inputs, d, new_name_prefix, new_label_prefix )
|
||||
elif isinstance( input, Conditional ):
|
||||
values = input_values[ input.name ]
|
||||
current = values["__current_case__"]
|
||||
label_prefix = label_prefix
|
||||
name_prefix = name_prefix + "|" + input.name
|
||||
visitor( input.cases[current].inputs, values, name_prefix, label_prefix )
|
||||
else:
|
||||
if isinstance( input, DataToolParameter ):
|
||||
data_inputs.append( dict( name=name_prefix+input.name, label=label_prefix+input.label, extensions=input.extensions ) )
|
||||
visitor( self.tool.inputs, self.state.inputs, "", "" )
|
||||
return data_inputs
|
||||
def get_data_outputs( self ):
|
||||
data_outputs = []
|
||||
for name, ( format, metadata_source, parent ) in self.tool.outputs.iteritems():
|
||||
data_outputs.append( dict( name=name, extension=format ) )
|
||||
return data_outputs
|
||||
def get_config_form( self ):
|
||||
def as_html( param, value, trans, prefix ):
|
||||
if type( param ) is DataToolParameter:
|
||||
return "Data input '" + param.name + "' (" + ( " or ".join( param.extensions ) ) + ")"
|
||||
else:
|
||||
return param.get_html_field( trans, value ).get_html( prefix )
|
||||
return self.trans.fill_template( "workflow/editor_tool_form.mako",
|
||||
tool=self.tool, as_html=as_html, values=self.state.inputs, errors=( self.errors or {} ) )
|
||||
def update_state( self, incoming ):
|
||||
errors = self.tool.update_state( self.trans, self.tool.inputs, self.state.inputs, incoming )
|
||||
self.errors = errors or None
|
||||
|
||||
|
||||
class WorkflowModuleFactory( object ):
|
||||
def __init__( self, module_types ):
|
||||
self.module_types = module_types
|
||||
def new( self, trans, type, tool_id=None ):
|
||||
"""
|
||||
Return module for type and (optional) tool_id intialized with
|
||||
new / default state.
|
||||
"""
|
||||
assert type in self.module_types
|
||||
return self.module_types[type].new( trans, tool_id )
|
||||
def from_dict( self, trans, d ):
|
||||
"""
|
||||
Return module initialized from the data in dictionary `d`.
|
||||
"""
|
||||
type = d['type']
|
||||
assert type in self.module_types
|
||||
return self.module_types[type].from_dict( trans, d )
|
||||
def from_workflow_step( self, trans, step ):
|
||||
"""
|
||||
Return module initializd from the WorkflowStep object `step`.
|
||||
"""
|
||||
type = step.type
|
||||
return self.module_types[type].from_workflow_step( trans, step )
|
||||
|
||||
module_factory = WorkflowModuleFactory( dict( data_input=InputDataModule, tool=ToolModule ) )
|
||||
@@ -66,15 +66,15 @@ $.ui.plugin.add("draggable", "scrollPanel", {
|
||||
ui.position.top += amount_moved;
|
||||
moved = true;
|
||||
}
|
||||
// Update offsets
|
||||
if ( moved ) {
|
||||
$.ui.ddmanager.prepareOffsets( 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_w - element_w );
|
||||
ui.position.top = Math.min( ui.position.top, panel_h - element_h );
|
||||
// Update offsets
|
||||
if ( moved ) {
|
||||
$.ui.ddmanager.prepareOffsets( instance, e );
|
||||
}
|
||||
// Keep moving even if mouse doesn't move
|
||||
if ( moved ) {
|
||||
instance.old_e = e;
|
||||
|
||||
@@ -295,9 +295,9 @@ $.extend( Node.prototype, {
|
||||
update_field_data : function( data ) {
|
||||
var el = $(this.element),
|
||||
node = this;
|
||||
this.tool_state = data.state;
|
||||
this.tool_state = data.tool_state;
|
||||
this.form_html = data.form_html;
|
||||
this.tool_errors = data.tool_errors;
|
||||
this.tool_errors = data.tool_errors;
|
||||
if ( this.tool_errors ) {
|
||||
el.addClass( "tool-node-error" );
|
||||
} else {
|
||||
@@ -493,27 +493,28 @@ function prebuild_node( type, title_text, tool_id ) {
|
||||
scrollSpeed: 20,
|
||||
// containment: $("#shim"),
|
||||
// grow: true,
|
||||
click: function( _, ui ) {
|
||||
click: function( e, ui ) {
|
||||
var element = $(this).data("draggable").element.get(0);
|
||||
(function(p) { p.removeChild( element ); p.appendChild( element ) })(element.parentNode)
|
||||
workflow.activate_node( node );
|
||||
},
|
||||
start: function() {
|
||||
start: function( e, ui ) {
|
||||
workflow.activate_node( node );
|
||||
$(this).css( 'z-index', '1000' );
|
||||
},
|
||||
drag: function() {
|
||||
drag: function( e, ui ) {
|
||||
$(this).find( ".terminal" ).each( function() {
|
||||
this.terminal.redraw();
|
||||
})
|
||||
},
|
||||
stop: function( _, ui ) {
|
||||
stop: function( e, ui ) {
|
||||
var element = $(this).data("draggable").element.get(0);
|
||||
(function(p) { p.removeChild( element ); p.appendChild( element ) })(element.parentNode)
|
||||
$(this).css( 'z-index', '100' );
|
||||
$(this).find( ".terminal" ).each( function() {
|
||||
this.terminal.redraw();
|
||||
});
|
||||
workflow.node_changed( this );
|
||||
}
|
||||
});
|
||||
return node;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
$(".dialog-box-container" ).fadeOut( function() { $("#overlay").hide(); } );
|
||||
};
|
||||
|
||||
function show_modal( title, body, buttons ) {
|
||||
function show_modal( title, body, buttons, extra_buttons ) {
|
||||
$( ".dialog-box" ).find( ".title" ).html( title );
|
||||
var b = $( ".dialog-box" ).find( ".buttons" ).html( "" );
|
||||
if ( buttons ) {
|
||||
@@ -40,6 +40,16 @@
|
||||
} else {
|
||||
b.hide();
|
||||
}
|
||||
var b = $( ".dialog-box" ).find( ".extra_buttons" ).html( "" );
|
||||
if ( extra_buttons ) {
|
||||
$.each( extra_buttons, function( name, value ) {
|
||||
b.append( $( '<button/>' ).text( name ).click( value ) );
|
||||
b.append( " " );
|
||||
});
|
||||
b.show();
|
||||
} else {
|
||||
b.hide();
|
||||
}
|
||||
if ( body == "progress" ) {
|
||||
body = $( "<img src='${h.url_for('/static/images/yui/rel_interstitial_loading.gif')}'/>" );
|
||||
}
|
||||
@@ -100,8 +110,8 @@
|
||||
## "<b>Load</b> a stored workflow" : load_workflow
|
||||
## });
|
||||
|
||||
$("#save-button").click( save_current_workflow );
|
||||
$("#close-button").click( close_editor );
|
||||
$("#save-button").click( function() { save_current_workflow(); } );
|
||||
$("#close-button").click( function() { close_editor(); } );
|
||||
|
||||
// Unload handler
|
||||
window.onbeforeunload = function() {
|
||||
@@ -162,7 +172,7 @@
|
||||
var x = 5000, y = 5000;
|
||||
$("div.toolFormInCanvas").each( function() {
|
||||
x = Math.min( x, $(this).position().left );
|
||||
y = Math.min( x, $(this).position().left );
|
||||
y = Math.min( y, $(this).position().top );
|
||||
});
|
||||
x = Math.min( - x + 20, 0 );
|
||||
y = Math.min( - y + 20, 0 );
|
||||
@@ -176,8 +186,8 @@
|
||||
workflow.add_node( node );
|
||||
workflow.activate_node( node );
|
||||
$.ajax( {
|
||||
url: "${h.url_for( action='get_tool_info' )}",
|
||||
data: { tool_id: id, "_": "true" },
|
||||
url: "${h.url_for( action='get_new_module_info' )}",
|
||||
data: { type: "tool", tool_id: id, "_": "true" },
|
||||
global: false,
|
||||
dataType: "json",
|
||||
success: function( data ) {
|
||||
@@ -198,7 +208,7 @@
|
||||
workflow.add_node( node );
|
||||
workflow.activate_node( node );
|
||||
$.ajax( {
|
||||
url: "${h.url_for( action='get_module_info' )}",
|
||||
url: "${h.url_for( action='get_new_module_info' )}",
|
||||
data: { type: type, "_": "true" },
|
||||
dataType: "json",
|
||||
success: function( data ) {
|
||||
@@ -226,7 +236,7 @@
|
||||
$("#right-content").find( "form" ).ajaxForm( {
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function( data ) {
|
||||
success: function( data ) {
|
||||
node.update_field_data( data );
|
||||
},
|
||||
beforeSubmit: function( data ) {
|
||||
@@ -247,21 +257,26 @@
|
||||
|
||||
var close_editor = function() {
|
||||
if ( workflow && workflow.has_changes ) {
|
||||
do_close = function() {
|
||||
window.onbeforeunload = undefined;
|
||||
window.document.location = "${h.url_for( controller='root' )}"
|
||||
};
|
||||
show_modal( "Close workflow editor",
|
||||
"There are unsaved changes to your workflow which will be lost.",
|
||||
{
|
||||
"Cancel" : hide_modal,
|
||||
"Close": function() {
|
||||
window.onbeforeunload = undefined;
|
||||
window.document.location = "${h.url_for( controller='root' )}"
|
||||
"Save Changes" : function() {
|
||||
save_current_workflow( do_close );
|
||||
}
|
||||
}, {
|
||||
"Don't Save": do_close,
|
||||
} );
|
||||
} else {
|
||||
window.document.location = "${h.url_for( controller='root' )}"
|
||||
}
|
||||
}
|
||||
|
||||
var save_current_workflow = function () {
|
||||
var save_current_workflow = function ( success_callback ) {
|
||||
show_modal( "Saving workflow", "progress" );
|
||||
$.ajax( {
|
||||
url: "${h.url_for( action='save_workflow' )}",
|
||||
@@ -287,13 +302,22 @@
|
||||
workflow.name = data.name;
|
||||
workflow.has_changes = false;
|
||||
workflow.stored = true;
|
||||
show_modal( "Workflow saved", body, {
|
||||
"Ok" : function () { hide_modal(); }
|
||||
});
|
||||
if ( success_callback ) {
|
||||
success_callback();
|
||||
}
|
||||
hide_modal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
## var update_canvas_map = function() {
|
||||
## var c = $("#canvas-map-canvas").get(0).getContext("2d");
|
||||
## var cp = $("#canvas-container").position();
|
||||
## c.clearRect( 0, 0, 100, 100 );
|
||||
## c.strokeStyle = "rgb(200,0,0)";
|
||||
## c.strokeRect ( -cp.left / 50, -cp.top / 50, $("#canvas-viewport").width() / 50, $("#canvas-viewport").height() / 50 );
|
||||
## }
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
@@ -485,7 +509,11 @@
|
||||
<div class="unified-panel-header-inner"><span class='title'>Loading workflow editor...</span></div>
|
||||
</div>
|
||||
<div class="body" style="max-height: 500px; overflow: auto;"><img src="${h.url_for('/static/images/yui/rel_interstitial_loading.gif')}" /></div>
|
||||
<div class="buttons" style="display: none;"></div>
|
||||
<div>
|
||||
<div class="buttons" style="display: none; float: right;"></div>
|
||||
<div class="extra_buttons" style="display: none; padding: 5px;"></div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-box-underlay"></div>
|
||||
</div>
|
||||
@@ -576,6 +604,9 @@
|
||||
<div class="unified-panel-body">
|
||||
<div id="canvas-viewport" style="width: 100%; height: 100%; position: absolute; overflow: hidden;">
|
||||
<div id="canvas-container" style="height: 5000px; width: 5000px; background: white url(${h.url_for('/static/images/light_gray_grid.gif')}) repeat;"></div>
|
||||
## <div id="canvas-map" style="height: 100px; width: 100px; border: solid red 1px; background: white; position: absolute; right: 0; bottom: 0;">
|
||||
## <canvas width="100" height="100" style="width: 100%; height: 100%" id="canvas-map-canvas"></canvas>
|
||||
## </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -32,8 +32,9 @@ from galaxy.tools.parameters import DataToolParameter
|
||||
<% rep_errors = dict() %>
|
||||
%endif
|
||||
<div class="repeat-group-item">
|
||||
<% index = repeat_values[i]['__index__'] %>
|
||||
<div class="form-title-row"><b>${input.title} ${i + 1}</b></div>
|
||||
${do_inputs( input.inputs, repeat_values[ i ], rep_errors, prefix + input.name + "_" + str(i) + "|", step, other_values )}
|
||||
${do_inputs( input.inputs, repeat_values[ i ], rep_errors, prefix + input.name + "_" + str(index) + "|", step, other_values )}
|
||||
## <div class="form-row"><input type="submit" name="${step.id}|${prefix}${input.name}_${i}_remove" value="Remove ${input.title} ${i+1}" /></div>
|
||||
</div>
|
||||
%endfor
|
||||
|
||||
Reference in New Issue
Block a user