1 | from elementtree.ElementTree import Element |
---|
2 | |
---|
3 | from galaxy import web |
---|
4 | from galaxy.tools.parameters import DataToolParameter, DummyDataset, RuntimeValue, check_param, visit_input_values |
---|
5 | from galaxy.tools import DefaultToolState |
---|
6 | from galaxy.tools.parameters.grouping import Repeat, Conditional |
---|
7 | from galaxy.util.bunch import Bunch |
---|
8 | from galaxy.util.json import from_json_string, to_json_string |
---|
9 | from galaxy.jobs.actions.post import ActionBox |
---|
10 | from galaxy.model import PostJobAction |
---|
11 | |
---|
12 | class WorkflowModule( object ): |
---|
13 | |
---|
14 | def __init__( self, trans ): |
---|
15 | self.trans = trans |
---|
16 | |
---|
17 | ## ---- Creating modules from various representations --------------------- |
---|
18 | |
---|
19 | @classmethod |
---|
20 | def new( Class, trans, tool_id=None ): |
---|
21 | """ |
---|
22 | Create a new instance of the module with default state |
---|
23 | """ |
---|
24 | return Class( trans ) |
---|
25 | @classmethod |
---|
26 | def from_dict( Class, trans, d ): |
---|
27 | """ |
---|
28 | Create a new instance of the module initialized from values in the |
---|
29 | dictionary `d`. |
---|
30 | """ |
---|
31 | return Class( trans ) |
---|
32 | @classmethod |
---|
33 | def from_workflow_step( Class, trans, step ): |
---|
34 | return Class( trans ) |
---|
35 | |
---|
36 | ## ---- Saving in various forms ------------------------------------------ |
---|
37 | |
---|
38 | def save_to_step( self, step ): |
---|
39 | step.type = self.type |
---|
40 | |
---|
41 | ## ---- General attributes ----------------------------------------------- |
---|
42 | |
---|
43 | def get_type( self ): |
---|
44 | return self.type |
---|
45 | def get_name( self ): |
---|
46 | return self.name |
---|
47 | def get_tool_id( self ): |
---|
48 | return None |
---|
49 | def get_tooltip( self ): |
---|
50 | return None |
---|
51 | |
---|
52 | ## ---- Configuration time ----------------------------------------------- |
---|
53 | |
---|
54 | def get_state( self ): |
---|
55 | return None |
---|
56 | def get_errors( self ): |
---|
57 | return None |
---|
58 | def get_data_inputs( self ): |
---|
59 | return [] |
---|
60 | def get_data_outputs( self ): |
---|
61 | return [] |
---|
62 | def update_state( self ): |
---|
63 | pass |
---|
64 | def get_config_form( self ): |
---|
65 | raise TypeError( "Abstract method" ) |
---|
66 | |
---|
67 | def check_and_update_state( self ): |
---|
68 | """ |
---|
69 | If the state is not in sync with the current implementation of the |
---|
70 | module, try to update. Returns a list of messages to be displayed |
---|
71 | """ |
---|
72 | pass |
---|
73 | |
---|
74 | ## ---- Run time --------------------------------------------------------- |
---|
75 | |
---|
76 | def get_runtime_inputs( self ): |
---|
77 | raise TypeError( "Abstract method" ) |
---|
78 | def get_runtime_state( self ): |
---|
79 | raise TypeError( "Abstract method" ) |
---|
80 | def encode_runtime_state( self, trans, state ): |
---|
81 | raise TypeError( "Abstract method" ) |
---|
82 | def decode_runtime_state( self, trans, string ): |
---|
83 | raise TypeError( "Abstract method" ) |
---|
84 | def update_runtime_state( self, trans, state, values ): |
---|
85 | raise TypeError( "Abstract method" ) |
---|
86 | |
---|
87 | def execute( self, trans, state ): |
---|
88 | raise TypeError( "Abstract method" ) |
---|
89 | |
---|
90 | class InputDataModule( WorkflowModule ): |
---|
91 | type = "data_input" |
---|
92 | name = "Input dataset" |
---|
93 | |
---|
94 | @classmethod |
---|
95 | def new( Class, trans, tool_id=None ): |
---|
96 | module = Class( trans ) |
---|
97 | module.state = dict( name="Input Dataset" ) |
---|
98 | return module |
---|
99 | @classmethod |
---|
100 | def from_dict( Class, trans, d, secure=True ): |
---|
101 | module = Class( trans ) |
---|
102 | state = from_json_string( d["tool_state"] ) |
---|
103 | module.state = dict( name=state.get( "name", "Input Dataset" ) ) |
---|
104 | return module |
---|
105 | @classmethod |
---|
106 | def from_workflow_step( Class, trans, step ): |
---|
107 | module = Class( trans ) |
---|
108 | module.state = dict( name="Input Dataset" ) |
---|
109 | if step.tool_inputs and "name" in step.tool_inputs: |
---|
110 | module.state['name'] = step.tool_inputs[ 'name' ] |
---|
111 | return module |
---|
112 | def save_to_step( self, step ): |
---|
113 | step.type = self.type |
---|
114 | step.tool_id = None |
---|
115 | step.tool_inputs = self.state |
---|
116 | |
---|
117 | def get_data_inputs( self ): |
---|
118 | return [] |
---|
119 | def get_data_outputs( self ): |
---|
120 | return [ dict( name='output', extensions=['input'] ) ] |
---|
121 | def get_config_form( self ): |
---|
122 | form = web.FormBuilder( title=self.name ) \ |
---|
123 | .add_text( "name", "Name", value=self.state['name'] ) |
---|
124 | return self.trans.fill_template( "workflow/editor_generic_form.mako", |
---|
125 | module=self, form=form ) |
---|
126 | def get_state( self, secure=True ): |
---|
127 | return to_json_string( self.state ) |
---|
128 | |
---|
129 | def update_state( self, incoming ): |
---|
130 | self.state['name'] = incoming.get( 'name', 'Input Dataset' ) |
---|
131 | |
---|
132 | def get_runtime_inputs( self ): |
---|
133 | label = self.state.get( "name", "Input Dataset" ) |
---|
134 | return dict( input=DataToolParameter( None, Element( "param", name="input", label=label, type="data", format="data" ) ) ) |
---|
135 | def get_runtime_state( self ): |
---|
136 | state = DefaultToolState() |
---|
137 | state.inputs = dict( input=None ) |
---|
138 | return state |
---|
139 | def encode_runtime_state( self, trans, state ): |
---|
140 | fake_tool = Bunch( inputs = self.get_runtime_inputs() ) |
---|
141 | return state.encode( fake_tool, trans.app ) |
---|
142 | def decode_runtime_state( self, trans, string ): |
---|
143 | fake_tool = Bunch( inputs = self.get_runtime_inputs() ) |
---|
144 | state = DefaultToolState() |
---|
145 | state.decode( string, fake_tool, trans.app ) |
---|
146 | return state |
---|
147 | def update_runtime_state( self, trans, state, values ): |
---|
148 | errors = {} |
---|
149 | for name, param in self.get_runtime_inputs().iteritems(): |
---|
150 | value, error = check_param( trans, param, values.get( name, None ), values ) |
---|
151 | state.inputs[ name ] = value |
---|
152 | if error: |
---|
153 | errors[ name ] = error |
---|
154 | return errors |
---|
155 | |
---|
156 | def execute( self, trans, state ): |
---|
157 | return None, dict( output=state.inputs['input']) |
---|
158 | |
---|
159 | class ToolModule( WorkflowModule ): |
---|
160 | |
---|
161 | type = "tool" |
---|
162 | |
---|
163 | def __init__( self, trans, tool_id ): |
---|
164 | self.trans = trans |
---|
165 | self.tool_id = tool_id |
---|
166 | self.tool = trans.app.toolbox.tools_by_id[ tool_id ] |
---|
167 | self.post_job_actions = {} |
---|
168 | self.workflow_outputs = [] |
---|
169 | self.state = None |
---|
170 | self.errors = None |
---|
171 | |
---|
172 | @classmethod |
---|
173 | def new( Class, trans, tool_id=None ): |
---|
174 | module = Class( trans, tool_id ) |
---|
175 | module.state = module.tool.new_state( trans, all_pages=True ) |
---|
176 | return module |
---|
177 | |
---|
178 | @classmethod |
---|
179 | def from_dict( Class, trans, d, secure=True ): |
---|
180 | tool_id = d['tool_id'] |
---|
181 | module = Class( trans, tool_id ) |
---|
182 | module.state = DefaultToolState() |
---|
183 | module.state.decode( d["tool_state"], module.tool, module.trans.app, secure=secure ) |
---|
184 | module.errors = d.get( "tool_errors", None ) |
---|
185 | module.post_job_actions = d.get("post_job_actions", {}) |
---|
186 | module.workflow_outputs = d.get("workflow_outputs", []) |
---|
187 | return module |
---|
188 | |
---|
189 | @classmethod |
---|
190 | def from_workflow_step( Class, trans, step ): |
---|
191 | tool_id = step.tool_id |
---|
192 | module = Class( trans, tool_id ) |
---|
193 | module.state = DefaultToolState() |
---|
194 | module.state.inputs = module.tool.params_from_strings( step.tool_inputs, trans.app, ignore_errors=True ) |
---|
195 | module.errors = step.tool_errors |
---|
196 | # module.post_job_actions = step.post_job_actions |
---|
197 | module.workflow_outputs = step.workflow_outputs |
---|
198 | pjadict = {} |
---|
199 | for pja in step.post_job_actions: |
---|
200 | pjadict[pja.action_type] = pja |
---|
201 | module.post_job_actions = pjadict |
---|
202 | return module |
---|
203 | |
---|
204 | def save_to_step( self, step ): |
---|
205 | step.type = self.type |
---|
206 | step.tool_id = self.tool_id |
---|
207 | step.tool_version = self.get_tool_version() |
---|
208 | step.tool_inputs = self.tool.params_to_strings( self.state.inputs, self.trans.app ) |
---|
209 | step.tool_errors = self.errors |
---|
210 | for k, v in self.post_job_actions.iteritems(): |
---|
211 | # Must have action_type, step. output and a_args are optional. |
---|
212 | if 'output_name' in v: |
---|
213 | output_name = v['output_name'] |
---|
214 | else: |
---|
215 | output_name = None |
---|
216 | if 'action_arguments' in v: |
---|
217 | action_arguments = v['action_arguments'] |
---|
218 | else: |
---|
219 | action_arguments = None |
---|
220 | n_p = PostJobAction(v['action_type'], step, output_name, action_arguments) |
---|
221 | |
---|
222 | def get_name( self ): |
---|
223 | return self.tool.name |
---|
224 | def get_tool_id( self ): |
---|
225 | return self.tool_id |
---|
226 | def get_tool_version( self ): |
---|
227 | return self.tool.version |
---|
228 | def get_state( self, secure=True ): |
---|
229 | return self.state.encode( self.tool, self.trans.app, secure=secure ) |
---|
230 | def get_errors( self ): |
---|
231 | return self.errors |
---|
232 | def get_tooltip( self ): |
---|
233 | return self.tool.help |
---|
234 | |
---|
235 | def get_data_inputs( self ): |
---|
236 | data_inputs = [] |
---|
237 | def callback( input, value, prefixed_name, prefixed_label ): |
---|
238 | if isinstance( input, DataToolParameter ): |
---|
239 | data_inputs.append( dict( |
---|
240 | name=prefixed_name, |
---|
241 | label=prefixed_label, |
---|
242 | extensions=input.extensions ) ) |
---|
243 | visit_input_values( self.tool.inputs, self.state.inputs, callback ) |
---|
244 | return data_inputs |
---|
245 | def get_data_outputs( self ): |
---|
246 | data_outputs = [] |
---|
247 | for name, tool_output in self.tool.outputs.iteritems(): |
---|
248 | formats = [ tool_output.format ] |
---|
249 | for change_elem in tool_output.change_format: |
---|
250 | for when_elem in change_elem.findall( 'when' ): |
---|
251 | format = when_elem.get( 'format', None ) |
---|
252 | if format and format not in formats: |
---|
253 | formats.append( format ) |
---|
254 | data_outputs.append( dict( name=name, extensions=formats ) ) |
---|
255 | return data_outputs |
---|
256 | |
---|
257 | def get_post_job_actions( self ): |
---|
258 | return self.post_job_actions |
---|
259 | |
---|
260 | def get_config_form( self ): |
---|
261 | self.add_dummy_datasets() |
---|
262 | return self.trans.fill_template( "workflow/editor_tool_form.mako", |
---|
263 | tool=self.tool, values=self.state.inputs, errors=( self.errors or {} ) ) |
---|
264 | |
---|
265 | def update_state( self, incoming ): |
---|
266 | # Build a callback that handles setting an input to be required at |
---|
267 | # runtime. We still process all other parameters the user might have |
---|
268 | # set. We also need to make sure all datasets have a dummy value |
---|
269 | # for dependencies to see |
---|
270 | |
---|
271 | self.post_job_actions = ActionBox.handle_incoming(incoming) |
---|
272 | |
---|
273 | make_runtime_key = incoming.get( 'make_runtime', None ) |
---|
274 | make_buildtime_key = incoming.get( 'make_buildtime', None ) |
---|
275 | def item_callback( trans, key, input, value, error, old_value, context ): |
---|
276 | # Dummy value for Data parameters |
---|
277 | if isinstance( input, DataToolParameter ): |
---|
278 | return DummyDataset(), None |
---|
279 | # Deal with build/runtime (does not apply to Data parameters) |
---|
280 | if key == make_buildtime_key: |
---|
281 | return input.get_initial_value( trans, context ), None |
---|
282 | elif isinstance( old_value, RuntimeValue ): |
---|
283 | return old_value, None |
---|
284 | elif key == make_runtime_key: |
---|
285 | return RuntimeValue(), None |
---|
286 | else: |
---|
287 | return value, error |
---|
288 | # Update state using incoming values |
---|
289 | errors = self.tool.update_state( self.trans, self.tool.inputs, self.state.inputs, incoming, item_callback=item_callback ) |
---|
290 | self.errors = errors or None |
---|
291 | |
---|
292 | def check_and_update_state( self ): |
---|
293 | return self.tool.check_and_update_param_values( self.state.inputs, self.trans ) |
---|
294 | |
---|
295 | def add_dummy_datasets( self, connections=None): |
---|
296 | if connections: |
---|
297 | # Store onnections by input name |
---|
298 | input_connections_by_name = \ |
---|
299 | dict( ( conn.input_name, conn ) for conn in connections ) |
---|
300 | else: |
---|
301 | input_connections_by_name = {} |
---|
302 | # Any connected input needs to have value DummyDataset (these |
---|
303 | # are not persisted so we need to do it every time) |
---|
304 | def callback( input, value, prefixed_name, prefixed_label ): |
---|
305 | if isinstance( input, DataToolParameter ): |
---|
306 | if connections is None or prefixed_name in input_connections_by_name: |
---|
307 | return DummyDataset() |
---|
308 | visit_input_values( self.tool.inputs, self.state.inputs, callback ) |
---|
309 | |
---|
310 | |
---|
311 | class WorkflowModuleFactory( object ): |
---|
312 | def __init__( self, module_types ): |
---|
313 | self.module_types = module_types |
---|
314 | def new( self, trans, type, tool_id=None ): |
---|
315 | """ |
---|
316 | Return module for type and (optional) tool_id intialized with |
---|
317 | new / default state. |
---|
318 | """ |
---|
319 | assert type in self.module_types |
---|
320 | return self.module_types[type].new( trans, tool_id ) |
---|
321 | def from_dict( self, trans, d, **kwargs ): |
---|
322 | """ |
---|
323 | Return module initialized from the data in dictionary `d`. |
---|
324 | """ |
---|
325 | type = d['type'] |
---|
326 | assert type in self.module_types |
---|
327 | return self.module_types[type].from_dict( trans, d, **kwargs ) |
---|
328 | def from_workflow_step( self, trans, step ): |
---|
329 | """ |
---|
330 | Return module initializd from the WorkflowStep object `step`. |
---|
331 | """ |
---|
332 | type = step.type |
---|
333 | return self.module_types[type].from_workflow_step( trans, step ) |
---|
334 | |
---|
335 | module_factory = WorkflowModuleFactory( dict( data_input=InputDataModule, tool=ToolModule ) ) |
---|