-
Notifications
You must be signed in to change notification settings - Fork 7
/
swe_jobman.m
550 lines (520 loc) · 19.7 KB
/
swe_jobman.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
function varargout = swe_jobman(varargin)
% Main interface for SPM Batch System
% Copy of spm_jobman using swe_select instead of spm_select
% This function provides a compatibility layer between SPM and matlabbatch.
%
% FORMAT swe_jobman('initcfg')
% Initialise jobs configuration and set MATLAB path accordingly.
%
% FORMAT swe_jobman('run',job[,input1,...inputN])
% FORMAT output_list = swe_jobman('run',job[,input1,...inputN])
% FORMAT [output_list, hjob] = swe_jobman('run',job[,input1,...inputN])
% Run specified job.
% job - filename of a job (.m or .mat), or
% cell array of filenames, or
% 'jobs'/'matlabbatch' variable, or
% cell array of 'jobs'/'matlabbatch' variables.
% input1,... - optional list of input arguments. These are filled into
% open inputs ('X->' marked in the GUI) before a job is
% run. When using an "{:}" subscript on a cell array,
% MATLAB expands this cell array into a comma separated
% list of arguments. Therefore, one can collect input
% arguments in the right order into a cell array named e.g.
% input_array and call swe_jobman('run',job,input_array{:})
% to run a job using the collected inputs. For files or text
% entry items, these inputs are the values one would specify
% in the GUI. For menus, the item number has to be entered
% (neither the GUI label nor the associated value that is
% saved in a batch).
% output_list - cell array containing the output arguments from each
% module in the job. The format and contents of these
% outputs is defined in the configuration of each module
% (.prog and .vout callbacks).
% hjob - harvested job after it has been filled and run. Note that
% this job has no dependencies any more. If one loads this
% job back to the batch system and changes some of the
% inputs, changed outputs will not be passed on.
%
% FORMAT job_id = swe_jobman
% job_id = swe_jobman('interactive',job[,node])
% job_id = swe_jobman('interactive','',node)
% Run the user interface in interactive mode.
% job - filename of a job (.m or .mat), or
% cell array of filenames, or
% 'jobs'/'matlabbatch' variable, or
% cell array of 'jobs'/'matlabbatch' variables.
% node - indicate which part of the configuration is to be used.
% For example, it could be 'spm.spatial.coreg.estimate'.
% job_id - can be used to manipulate this job in cfg_util. Note that
% changes to the job in cfg_util will not show up in cfg_ui
% unless 'Update View' is called.
%
% FORMAT jobs = swe_jobman('convert',jobs)
% Convert older batch jobs to latest version
% jobs - char or cell array of filenames, or
% 'jobs'/'matlabbbatch' variable
%__________________________________________________________________________
% Copyright (C) 2005-2016 Wellcome Trust Centre for Neuroimaging
% Volkmar Glauche
% Modified by Bryan Guillaume
% Version Info: $Format:%ci$ $Format:%h$
%__________________________________________________________________________
%
% Programmers help:
%
% FORMAT [output_list hjob] = swe_jobman('serial')
% [output_list hjob] = swe_jobman('serial',job[,'', input1,...inputN])
% [output_list hjob] = swe_jobman('serial',job ,node[,input1,...inputN])
% [output_list hjob] = swe_jobman('serial','' ,node[,input1,...inputN])
% Run the user interface in serial mode. If job is not empty, then node
% is silently ignored. Inputs can be a list of arguments. These are passed
% on to the open inputs of the specified job/node. Each input should be
% suitable to be assigned to item.val{1}. For cfg_repeat/cfg_choice items,
% input should be a cell list of indices input{1}...input{k} into
% item.value. See cfg_util('filljob',...) for details.
%
% FORMAT swe_jobman('help',node)
% swe_jobman('help',node,width)
% Create a cell array containing help information. This is justified
% to be 'width' characters wide. e.g.
% h = swe_jobman('help','spm.spatial.coreg.estimate');
% for i=1:numel(h), fprintf('%s\n',h{i}); end
%
% FORMAT [tag, job] = swe_jobman('harvest', job_id|job|cfg_item|cfg_struct)
% Take the job with id job_id in cfg_util and extract what is
% needed to save it as a batch job (for experts only). If a (partial) job
% is given instead, the output job is augmented with default settings.
% If the argument is a cfg_item or cfg_struct tree, it will be harvested
% outside cfg_util.
% tag - tag of the root node of the current job/cfg_item tree
% job - harvested data from the current job/cfg_item tree
%__________________________________________________________________________
%
% This code is based on earlier versions by John Ashburner, Philippe
% Ciuciu and Guillaume Flandin.
% It now relies on matlabbatch
% http://sourceforge.net/projects/matlabbatch/
% Copyright (C) 2008 Freiburg Brain Imaging
%__________________________________________________________________________
%-Force jobs configuration initialisation if needed
%--------------------------------------------------------------------------
persistent isInitCfg;
if isempty(isInitCfg) && ~(nargin == 1 && strcmpi(varargin{1},'initcfg'))
% Run swe_jobman('initcfg') beforehand.
fprintf('Initialising batch system... ');
swe_jobman('initcfg');
fprintf('done.\n');
end
isInitCfg = true;
%-Open GUI when called without input arguments
%--------------------------------------------------------------------------
if ~nargin
swe_jobman('interactive');
if nargout > 0, varargout = {findobj(0,'tag','cfg_ui')}; end
return;
end
%-Warn about deprecated syntax
%--------------------------------------------------------------------------
action = lower(varargin{1});
switch action
case 'run_nogui'
warning('spm:swe_jobman:deprecated', ...
'Callback ''%s'' is deprecated. Use ''run'' instead.',action);
action = 'run';
case {'spm5tospm8','spm5tospm8bulk'}
warning('spm:swe_jobman:deprecated', ...
'Callback ''%s'' is deprecated. Use ''convert'' instead.',action);
action = 'convert';
end
%-Load and convert batch jobs
%--------------------------------------------------------------------------
if ismember(action, {'interactive','run','serial'})
if nargin > 1
% sort out job/node arguments for interactive, serial, run cmds
if nargin>=2 && ~isempty(varargin{2})
% do not consider node if job is given
if ischar(varargin{2}) || iscellstr(varargin{2})
jobs = load_jobs(varargin{2});
elseif iscell(varargin{2})
if iscell(varargin{2}{1})
% assume varargin{2} is a cell of jobs
jobs = varargin{2};
else
% assume varargin{2} is a single job
jobs{1} = varargin{2};
end
end
mljob = canonicalise_jobs(jobs);
elseif ismember(action, {'interactive','serial'}) && nargin>=3 && isempty(varargin{2})
% Node spec only allowed for 'interactive', 'serial'
mod_cfg_id = cfg_util('tag2mod_cfg_id',varargin{3});
else
error('spm:swe_jobman:invalidSyntax', ...
'Don''t know how to handle this ''%s'' call.', action);
end
end
end
%-Perform action
%--------------------------------------------------------------------------
switch action
case {'initcfg'}
if ~isdeployed
addpath(fullfile(spm('Dir'),'matlabbatch'));
addpath(fullfile(spm('Dir'),'config'));
end
try
swe_select('init');
catch
S = which('swe_select','-all');
if numel(S) > 1
fprintf('swe_select appears several times in your MATLAB path:\n');
for i=1:numel(S)
if i==1
fprintf(' %s (SHADOWING)\n',S{1});
else
fprintf(' %s\n',S{i});
end
end
end
rethrow(lasterror);
end
cfg_get_defaults('cfg_util.genscript_run', @genscript_run);
cfg_util('initcfg'); % This must be the first call to cfg_util
%if ~spm('cmdline')
% f = cfg_ui('Visible','off'); % Create invisible batch ui
% f0 = findobj(f, 'Tag','MenuFile'); % Add entries to file menu
% f2 = uimenu(f0,'Label','xxx', 'Callback',@xxx, ...
% 'HandleVisibility','off', 'tag','xxx');
%end
case {'interactive'}
if exist('mljob', 'var')
cjob = cfg_util('initjob', mljob);
elseif exist('mod_cfg_id', 'var')
if isempty(mod_cfg_id)
warning('spm:swe_jobman:NodeNotFound', ...
['Can not find executable node ''%s'' - running '...
'matlabbatch without default node.'], varargin{3});
cjob = cfg_util('initjob');
else
cjob = cfg_util('initjob');
mod_job_id = cfg_util('addtojob', cjob, mod_cfg_id);
cfg_util('harvest', cjob, mod_job_id);
end
else
cjob = cfg_util('initjob');
end
f = findobj(0,'tag','cfg_ui');
if isempty(f), f = cfg_ui; end
cfg_ui('local_showjob', f, cjob);
if nargout > 0
varargout{1} = cjob;
end
case {'serial'}
if exist('mljob', 'var')
cjob = cfg_util('initjob', mljob);
else
cjob = cfg_util('initjob');
if nargin > 2
[mod_cfg_id, item_mod_id] = cfg_util('tag2cfg_id', lower(varargin{3}));
cfg_util('addtojob', cjob, mod_cfg_id);
end
end
sts = fill_run_job('serial', cjob, varargin{4:end});
if sts
if nargout > 0
varargout{1} = cfg_util('getalloutputs', cjob);
end
if nargout > 1
[un, varargout{2}] = cfg_util('harvestrun', cjob);
end
cfg_util('deljob', cjob);
end
case {'run'}
if ~exist('mljob', 'var')
error('Not enough input arguments.');
end
cjob = cfg_util('initjob', mljob);
sts = fill_run_job('run', cjob, varargin{3:end});
if sts
if nargout > 0
varargout{1} = cfg_util('getalloutputs', cjob);
end
if nargout > 1
[un, varargout{2}] = cfg_util('harvestrun', cjob);
end
cfg_util('deljob', cjob);
end
case {'convert'}
varargout{1} = convert_jobs(varargin{2:end});
case {'harvest'}
if nargin == 1
error('spm:swe_jobman:CantHarvest', ...
['Can not harvest job without job_id. Please use ' ...
'swe_jobman(''harvest'', job_id).']);
elseif cfg_util('isjob_id', varargin{2})
[tag, job] = cfg_util('harvest', varargin{2});
elseif iscell(varargin{2})
cjob = cfg_util('initjob', varargin{2});
[tag, job] = cfg_util('harvest', cjob);
cfg_util('deljob', cjob);
elseif isa(varargin{2}, 'cfg_item')
[tag, job] = harvest(varargin{2}, varargin{2}, false, false);
elseif isstruct(varargin{2})
% try to convert into class before harvesting
c = cfg_struct2cfg(varargin{2});
[tag, job] = harvest(c,c,false,false);
else
error('spm:swe_jobman:CantHarvestThis', ...
'Can not harvest this argument.');
end
varargout{1} = tag;
varargout{2} = job;
case {'help'}
if (nargin < 2) || isempty(varargin{2})
node = 'spm';
else
node = varargin{2};
end
if nargin < 3
width = 60;
else
width = varargin{3};
end
varargout{1} = cfg_util('showdocwidth', width, node);
otherwise
error('spm:swe_jobman:unknownOption','Unknown option "%s".',varargin{1});
end
%==========================================================================
% function newjobs = load_jobs(job)
%==========================================================================
function newjobs = load_jobs(job)
% Load a list of possible job files, return a cell list of jobs.
% If a job file failed to load, an empty cell is returned in the list.
filenames = cellstr(job);
newjobs = {};
for i = 1:numel(filenames)
switch spm_file(filenames{i},'ext')
case 'mat'
try
S = load(filenames{i});
if isfield(S,'matlabbatch')
matlabbatch = S.matlabbatch;
elseif isfield(S,'jobs')
jobs = S.jobs;
end
catch
warning('spm:swe_jobman:loadFailed','Load failed: ''%s''',filenames{i});
end
case 'm'
try
str = fileread(filenames{i});
eval(str);
catch
warning('spm:swe_jobman:loadFailed','Load failed: ''%s''',filenames{i});
end
case 'json'
try
S = spm_jsonread(filenames{i});
if isstruct(S)
for j=1:numel(S)
matlabbatch{j} = S(j);
end
else
matlabbatch = S;
end
catch
warning('spm:swe_jobman:loadFailed','Load failed: ''%s''',filenames{i});
end
otherwise
warning('spm:swe_jobman:unknownExt','Unknown extension: ''%s''',filenames{i});
end
if exist('jobs','var')
newjobs = [newjobs(:); {jobs}];
clear jobs;
elseif exist('matlabbatch','var')
newjobs = [newjobs(:); {matlabbatch}];
clear matlabbatch;
else
warning('spm:swe_jobman:jobNotFound','No batch job found in ''%s''',filenames{i});
newjobs = [newjobs(:); {[]}];
end
end
%==========================================================================
% function varargout = convert_jobs(varargin)
%==========================================================================
function varargout = convert_jobs(varargin)
% Convert a list of jobs to latest version
if nargin && iscell(varargin{1}) && ~iscellstr(varargin{1})
varargout = canonicalise_jobs(varargin);
return;
elseif ~nargin || isempty(varargin{1})
[fname, sts] = swe_select([1 Inf], 'batch', 'Select job file(s)');
if ~sts, return; end
else
fname = varargin{1};
end
fname = cellstr(fname);
joblist = load_jobs(fname);
SPMver = spm('Ver');
outnames = cell(numel(fname),1);
for i=1:numel(fname)
if ~isempty(joblist{i})
outnames{i} = spm_file(fname{i},'prefix',[lower(SPMver) '_']);
fprintf('Initial job: %s\n', fname{i});
fprintf('%s job: %s\n',SPMver, outnames{i});
cjob = cfg_util('initjob', canonicalise_jobs(joblist(i)));
cfg_util('savejob', cjob, outnames{i});
cfg_util('deljob', cjob);
end
end
varargout = {outnames};
%==========================================================================
% function [mljob, comp] = canonicalise_jobs(job)
%==========================================================================
function [mljob, comp] = canonicalise_jobs(job)
% job: a cell list of job data structures.
% Check whether job is a SPM5 or matlabbatch job. In the first case, all
% items in job{:} should have a fieldname of either 'temporal', 'spatial',
% 'stats', 'tools' or 'util'. If this is the case, then job will be
% assigned to mljob{1}.spm, which is the tag of the SPM root configuration
% item.
comp = true(size(job));
mljob = cell(size(job));
for i = 1:numel(job)
for j = 1:numel(job{i})
comp(i) = comp(i) && any(strcmp(fieldnames(job{i}{j}), ...
{'temporal', 'spatial', 'stats', 'tools', 'util'}));
if ~comp(i)
break;
end
end
if comp(i)
tmp = sub_canonicalise_job(job{i});
for j=1:numel(tmp)
mljob{i}{j}.spm = tmp{j};
end
else
mljob{i} = job{i};
end
end
%==========================================================================
% function njobs = sub_canonicalise_job(jobs)
%==========================================================================
function njobs = sub_canonicalise_job(jobs)
decel = struct('spatial',struct('realign',[],'coreg',[],'normalise',[]),...
'temporal',[],...
'stats',[],...
'meeg',[],...
'util',[],...
'tools',struct('dartel',[]));
njobs = {};
for i0 = 1:numel(jobs)
tmp0 = fieldnames(jobs{i0});
tmp0 = tmp0{1};
if any(strcmp(tmp0,fieldnames(decel)))
for i1=1:numel(jobs{i0}.(tmp0))
tmp1 = fieldnames(jobs{i0}.(tmp0){i1});
tmp1 = tmp1{1};
if ~isempty(decel.(tmp0))
if any(strcmp(tmp1,fieldnames(decel.(tmp0))))
for i2=1:numel(jobs{i0}.(tmp0){i1}.(tmp1))
njobs{end+1} = struct(tmp0,struct(tmp1,jobs{i0}.(tmp0){i1}.(tmp1){i2}));
end
else
njobs{end+1} = struct(tmp0,jobs{i0}.(tmp0){i1});
end
else
njobs{end+1} = struct(tmp0,jobs{i0}.(tmp0){i1});
end
end
else
njobs{end+1} = jobs{i0};
end
end
%==========================================================================
% function sts = fill_run_job(action, cjob, varargin)
%==========================================================================
function sts = fill_run_job(action, cjob, varargin)
switch lower(action)
case 'serial'
sts = cfg_util('filljobui', cjob, @serial_ui, varargin{:});
case 'run'
sts = cfg_util('filljob', cjob, varargin{:});
end
if sts
cfg_util('run', cjob);
else
cfg_util('deljob', cjob);
error('spm:swe_jobman:jobNotFilled', 'No executable modules, but still unresolved dependencies or incomplete module inputs.');
end
%==========================================================================
% function [val sts] = serial_ui(item)
%==========================================================================
function [val, sts] = serial_ui(item)
% wrapper function to translate cfg_util('filljobui'... input requests into
% spm_input/cfg_select calls.
sts = true;
switch class(item)
case 'cfg_choice'
labels = cell(size(item.values));
values = cell(size(item.values));
for k = 1:numel(item.values)
labels{k} = item.values{k}.name;
values{k} = k;
end
val = spm_input(item.name, 1, 'm', labels, values);
case 'cfg_menu'
val = spm_input(item.name, 1, 'm', item.labels, item.values);
val = val{1};
case 'cfg_repeat'
labels = cell(size(item.values));
values = cell(size(item.values));
for k = 1:numel(item.values)
labels{k} = item.values{k}.name;
values{k} = k;
end
% enter at least item.num(1) values
for k = 1:item.num(1)
val(k) = spm_input(sprintf('%s(%d)', item.name, k), 1, 'm', ...
labels, values);
end
% enter more (up to varargin{3}(2) values
labels = {labels{:} 'Done'};
% values is a cell list of natural numbers, use -1 for Done
values = {values{:} -1};
while numel(val) < item.num(2)
val1 = spm_input(sprintf('%s(%d)', item.name, numel(val)+1), 1, ...
'm', labels, values);
if val1{1} == -1
break;
else
val(end+1) = val1;
end
end
case 'cfg_entry'
val = spm_input(item.name, 1, item.strtype, '', item.num, ...
item.extras);
case 'cfg_files'
[t,sts] = cfg_getfile(item.num, item.filter, item.name, '', ...
item.dir, item.ufilter);
if sts
val = cellstr(t);
else
val = {};
error('File selector was closed.');
end
end
%==========================================================================
% function [code cont] = genscript_run
%==========================================================================
function [code, cont] = genscript_run
% Return code snippet to initialise SPM defaults and run a job generated by
% cfg_util('genscript',...) through swe_jobman.
modality = spm('CheckModality');
code{1} = sprintf('spm(''defaults'', ''%s'');', modality);
code{2} = 'swe_jobman(''run'', jobs, inputs{:});';
cont = false;
%-Compatibility layer for SPM5
function varargout = interactive(varargin)
function varargout = defaults_edit(varargin)
function varargout = run_serial(varargin)