20
20
import sys
21
21
import tempfile
22
22
import simplejson as json
23
+ import copy
24
+ import six
23
25
24
26
from ..scripts .instance import import_module
25
27
@@ -83,9 +85,9 @@ def generate_boutiques_descriptor(
83
85
# Handle compound inputs (inputs that can be of multiple types and are mutually exclusive)
84
86
if isinstance (input , list ):
85
87
mutex_group_members = []
88
+ tool_desc ['command-line' ] += input [0 ]['value-key' ] + " "
86
89
for i in input :
87
90
tool_desc ['inputs' ].append (i )
88
- tool_desc ['command-line' ] += i ['value-key' ] + " "
89
91
mutex_group_members .append (i ['id' ])
90
92
if verbose :
91
93
print ("-> Adding input " + i ['name' ])
@@ -100,6 +102,9 @@ def generate_boutiques_descriptor(
100
102
if verbose :
101
103
print ("-> Adding input " + input ['name' ])
102
104
105
+ # Remove the extra space at the end of the command line
106
+ tool_desc ['command-line' ] = tool_desc ['command-line' ].strip ()
107
+
103
108
# Generates input groups
104
109
tool_desc ['groups' ] += get_boutiques_groups (interface .inputs .traits (transient = None ).items ())
105
110
if len (tool_desc ['groups' ]) == 0 :
@@ -141,14 +146,20 @@ def generate_tool_outputs(outputs, interface, tool_desc, verbose, first_run):
141
146
# If this is the first time we are generating outputs, add the full output to the descriptor.
142
147
# Otherwise, find the existing output and update its path template if it's still undefined.
143
148
if first_run :
144
- tool_desc ['output-files' ].append (output )
149
+ if isinstance (output , list ):
150
+ tool_desc ['output-files' ].extend (output )
151
+ if verbose :
152
+ print ("-> Adding output " + output [0 ]['name' ])
153
+ else :
154
+ tool_desc ['output-files' ].append (output )
155
+ if verbose :
156
+ print ("-> Adding output " + output ['name' ])
145
157
else :
146
158
for existing_output in tool_desc ['output-files' ]:
147
- if output ['id' ] == existing_output ['id' ] and existing_output ['path-template' ] == "" :
159
+ if not isinstance (output , list ) and output ['id' ] == existing_output ['id' ] \
160
+ and existing_output ['path-template' ] == "" :
148
161
existing_output ['path-template' ] = output ['path-template' ]
149
162
break
150
- if verbose :
151
- print ("-> Adding output " + output ['name' ])
152
163
153
164
if len (tool_desc ['output-files' ]) == 0 :
154
165
raise Exception ("Tool has no output." )
@@ -197,9 +208,11 @@ def get_boutiques_input(inputs, interface, input_name, spec,
197
208
input_list = []
198
209
# Recursively create an input for each trait
199
210
for i in range (0 , len (trait_handler .handlers )):
200
- input_list .append (get_boutiques_input (inputs , interface , input_name , spec ,
201
- ignored_template_inputs , verbose ,
202
- ignore_template_numbers , trait_handler .handlers [i ], i ))
211
+ inp = get_boutiques_input (inputs , interface , input_name , spec ,
212
+ ignored_template_inputs , verbose ,
213
+ ignore_template_numbers , trait_handler .handlers [i ], i )
214
+ inp ['optional' ] = True
215
+ input_list .append (inp )
203
216
return input_list
204
217
205
218
if handler_type == "File" or handler_type == "Directory" :
@@ -227,6 +240,7 @@ def get_boutiques_input(inputs, interface, input_name, spec,
227
240
input ['exclusive-maximum' ] = trait_handler .exclude_high
228
241
229
242
# Deal with list inputs
243
+ # TODO handle lists of lists (e.g. FSL ProbTrackX seed input)
230
244
if handler_type == "List" :
231
245
input ['list' ] = True
232
246
trait_type = type (trait_handler .item_trait .trait_type ).__name__
@@ -235,11 +249,13 @@ def get_boutiques_input(inputs, interface, input_name, spec,
235
249
input ['type' ] = "Number"
236
250
elif trait_type == "Float" :
237
251
input ['type' ] = "Number"
252
+ elif trait_type == "File" :
253
+ input ['type' ] = "File"
238
254
else :
239
255
input ['type' ] = "String"
240
- if trait_handler .minlen is not None :
256
+ if trait_handler .minlen != 0 :
241
257
input ['min-list-entries' ] = trait_handler .minlen
242
- if trait_handler .maxlen is not None :
258
+ if trait_handler .maxlen != six . MAXSIZE :
243
259
input ['max-list-entries' ] = trait_handler .maxlen
244
260
245
261
input ['value-key' ] = "[" + input_name .upper (
@@ -266,6 +282,11 @@ def get_boutiques_input(inputs, interface, input_name, spec,
266
282
pass
267
283
else :
268
284
if value_choices is not None :
285
+ if all (isinstance (n , int ) for n in value_choices ):
286
+ input ['type' ] = "Number"
287
+ input ['integer' ] = True
288
+ elif all (isinstance (n , float ) for n in value_choices ):
289
+ input ['type' ] = "Number"
269
290
input ['value-choices' ] = value_choices
270
291
271
292
# Set Boolean types to Flag (there is no Boolean type in Boutiques)
@@ -316,6 +337,18 @@ def get_boutiques_output(outputs, name, spec, interface, tool_inputs, verbose=Fa
316
337
output_value = interface ._list_outputs ()[name ]
317
338
except TypeError :
318
339
output_value = None
340
+ except AttributeError :
341
+ output_value = None
342
+
343
+ # Handle multi-outputs
344
+ if isinstance (output_value , list ):
345
+ output_list = []
346
+ for i in range (0 , len (output_value )):
347
+ output_copy = copy .deepcopy (output )
348
+ output_copy ['path-template' ] = os .path .relpath (output_value [i ])
349
+ output_copy ['id' ] += ("_" + str (i + 1 )) if i > 0 else ""
350
+ output_list .append (output_copy )
351
+ return output_list
319
352
320
353
# If an output value is defined, use its relative path
321
354
# Otherwise, put blank string and try to fill it on another iteration
0 commit comments