Skip to content

Commit 1d656b6

Browse files
authored
Merge pull request #193 from Remi-Gau/save_config
[ENH] add a save config function
2 parents ab06459 + 3c20a7d commit 1d656b6

File tree

7 files changed

+188
-69
lines changed

7 files changed

+188
-69
lines changed

.gitignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ output
1010
*test_code_report.txt
1111
test_report.log
1212
*coverage*
13-
test_report.log
1413
check_my_code_report.txt
1514

1615
*filteredBy*
@@ -19,16 +18,17 @@ tests/*.nii*
1918
tests/*.json*
2019
tests/*.tsv*
2120

21+
*_cfg.json
22+
2223
# jupyter notebook checkpoints
2324
.ipynb_checkpoints
2425
notebooks/source/*
2526
notebooks/new_experiment/*
2627

2728
# ignore node js files
28-
node_modules/*
29+
node_modules
2930
package.json
3031
package-lock.json
31-
package.json
3232

3333
# visual studio code stuff
3434
.vscode

docs/source/function_description.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ List of functions in the ``src`` folder.
1212
.. autofunction:: createDatasetDescription
1313
.. autofunction:: createFilename
1414
.. autofunction:: createJson
15+
.. autofunction:: saveCfg
1516

16-
``createJson`` can be used to save in a
17+
``saveCfg`` can be used to save in a
1718
human readable format the extra parameters that you used to run your experiment.
1819
This will most likely make the json file non-bids compliant but it can prove useful,
1920
to keep this information in your source dataset

lib/bids-matlab

Submodule bids-matlab updated 56 files

src/createJson.m

Lines changed: 34 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,44 @@ function createJson(varargin)
3030
% - :``*.json``: (jsonfile) The file name corresponds to the run + suffix depending
3131
% on the arguments passed in.
3232
%
33-
% .. TODO:
34-
%
35-
% - use input parser for this one
3633
%
3734
% (C) Copyright 2020 CPP_BIDS developers
3835

39-
[cfg, modality, extraInfo] = checkInput(varargin);
36+
%% Parse inputs
37+
% modality is either given as a string by user or guessed from cfg.fileName
38+
% extraInfo must be a structure
39+
40+
args = inputParser;
41+
42+
default_modality = '';
43+
default_extraInfo = struct([]);
44+
45+
charOrStruct = @(x) isstruct(x) || ischar(x);
46+
47+
args.addRequired('input', @isstruct);
48+
args.addOptional('modality', default_modality, charOrStruct);
49+
args.addOptional('extraInfo', default_extraInfo, @isstruct);
50+
51+
args.parse(varargin{:});
52+
53+
cfg = args.Results.input;
54+
modality = args.Results.modality;
55+
extraInfo = args.Results.extraInfo;
4056

57+
if ischar(modality) && strcmp(modality, default_modality) && isfield(cfg.fileName, 'modality')
58+
modality = cfg.fileName.modality;
59+
end
60+
61+
if isstruct(modality) && isfield(cfg.fileName, 'modality')
62+
extraInfo = modality;
63+
modality = cfg.fileName.modality;
64+
end
65+
66+
if ~ischar(modality) || all(~strcmpi(modality, {'beh', 'func', 'eeg', 'ieeg', 'meg'}))
67+
errorCreateJson('wrongModalityInput', modality);
68+
end
69+
70+
% adapt depending on input
4171
if strcmp(modality, 'func')
4272
fileName = strrep(cfg.fileName.events, '_events', '_bold');
4373
jsonContent = cfg.bids.mri;
@@ -61,66 +91,10 @@ function createJson(varargin)
6191

6292
end
6393

64-
function [cfg, modality, extraInfo] = checkInput(varargin)
65-
66-
% trying to parse inputs
67-
% modality is either given as a string by user or guessed from cfg.fileName
68-
% extraInfo must be a structure
69-
70-
input = varargin{1};
71-
72-
modality = '';
73-
extraInfo = struct();
74-
75-
switch numel(input)
76-
77-
case 0
78-
errorCreateJson('notEnoughInput');
79-
80-
case 1
81-
cfg = input{1};
82-
if isfield(cfg.fileName, 'modality')
83-
modality = cfg.fileName.modality;
84-
end
85-
86-
case 2
87-
cfg = input{1};
88-
if ischar(input{2})
89-
modality = input{2};
90-
elseif isstruct(input{2})
91-
modality = cfg.fileName.modality;
92-
extraInfo = input{2};
93-
else
94-
errorCreateJson('wrongInputType');
95-
end
96-
97-
otherwise
98-
cfg = input{1};
99-
modality = input{2};
100-
extraInfo = input{3};
101-
102-
end
103-
104-
if ~ischar(modality) || ...
105-
all(~strcmpi(modality, {'beh', 'func', 'eeg', 'ieeg', 'meg'}))
106-
errorCreateJson('wrongModalityInput', modality);
107-
end
108-
109-
if ~isstruct(extraInfo)
110-
warning('Additional info added to a json file must be a structure.');
111-
end
112-
113-
end
114-
11594
function errorCreateJson(identifier, varargin)
11695

11796
switch identifier
118-
case 'notEnoughInput'
119-
errorStruct.message = 'First input must be a valid cfg structure.';
12097

121-
case 'wrongInputType'
122-
errorStruct.message = ['The second input must be a string (modality)' ...
123-
'or a structure (extraInfo)'];
12498
case 'wrongModalityInput'
12599
errorStruct.message = sprintf(['The given modality is not valid: %s.\n', ...
126100
'Only the following string are accepted: ' ...

src/saveCfg.m

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
function filename = saveCfg(varargin)
2+
%
3+
% Saves config as JSON.
4+
%
5+
% USAGE::
6+
%
7+
% saveCfg(cfg [, filename])
8+
%
9+
% :param cfg: Required. Configuration. See ``checkCFG()``.
10+
% :type cfg: structure
11+
%
12+
% :param filename: Optional. Fullpath filename for the output file.
13+
% :type filename: path
14+
%
15+
% :output: filename
16+
%
17+
% If a filename is provided, this will be used as an output file (and will
18+
% create any required directory).
19+
%
20+
% If no filename is provided, it will try to create one based on the content
21+
% of ``cfg.fileName`` and ``cfg.dir``. This would for example create a
22+
% file::
23+
%
24+
% ./output/source/sub-01/func/sub-01_task-testTask_run-001_date-202203181752_cfg.json
25+
%
26+
%
27+
% If this fails it will save the file
28+
% in the ``pwd`` under ``'date-yyyymmddHHMM_cfg.json'``.
29+
%
30+
%
31+
% (C) Copyright 2022 CPP_BIDS developers
32+
33+
%% Parse inputs
34+
% modality is either given as a string by user or guessed from cfg.fileName
35+
% extraInfo must be a structure
36+
37+
args = inputParser;
38+
39+
default_filename = '';
40+
41+
args.addRequired('cfg', @isstruct);
42+
args.addOptional('filename', default_filename, @ischar);
43+
44+
args.parse(varargin{:});
45+
46+
cfg = args.Results.cfg;
47+
filename = args.Results.filename;
48+
49+
% if no name, get name from config
50+
if strcmp(filename, '')
51+
52+
if isfield(cfg, 'fileName') && isfield(cfg.fileName, 'events')
53+
filename = strrep(cfg.fileName.events, '_events.tsv', '_cfg.json');
54+
filename = getFullFilename(filename, cfg);
55+
end
56+
57+
end
58+
59+
% if this did not work we make one up
60+
if strcmp(filename, '')
61+
tmp = checkCFG();
62+
bf = struct('suffix', 'cfg', ...
63+
'ext', '.json', ...
64+
'entities', struct('date', datestr(now, tmp.fileName.dateFormat)));
65+
bf = bids.File(bf);
66+
filename = fullfile(pwd, bf.filename);
67+
end
68+
69+
if ~isdir(fileparts(filename))
70+
bids.util.mkdir(fileparts(filename));
71+
end
72+
73+
%% save
74+
jsonContent = cfg;
75+
bids.util.jsonencode(filename, jsonContent);
76+
77+
end

src/utils/getFullFilename.m

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,16 @@
1313
%
1414
% (C) Copyright 2020 CPP_BIDS developers
1515

16-
fullFilename = fullfile( ...
17-
cfg.dir.outputSubject, ...
18-
cfg.fileName.modality, ...
19-
fileName);
16+
if isfield(cfg, 'dir') && isfield(cfg.dir, 'outputSubject') && ...
17+
isfield(cfg, 'fileName') && isfield(cfg.fileName, 'modality')
18+
19+
fullFilename = fullfile(cfg.dir.outputSubject, ...
20+
cfg.fileName.modality, ...
21+
fileName);
22+
else
23+
warning('Not enough information to build a fullpath filename');
24+
fullFilename = fileName;
25+
26+
end
2027

2128
end

tests/test_saveCfg.m

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
% (C) Copyright 2020 CPP_BIDS developers
2+
3+
function test_suite = test_saveCfg %#ok<*STOUT>
4+
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
5+
test_functions = localfunctions(); %#ok<*NASGU>
6+
catch % no problem; early Matlab versions can use initTestSuite fine
7+
end
8+
initTestSuite;
9+
end
10+
11+
function test_saveCfg_basic()
12+
13+
cfg = setUp();
14+
15+
filename = saveCfg(cfg);
16+
17+
expected = fullfile(cfg.dir.outputSubject, ...
18+
cfg.fileName.modality, ...
19+
strrep(cfg.fileName.events, '_events.tsv', '_cfg.json'));
20+
21+
assertEqual(filename, expected);
22+
23+
content = bids.util.jsondecode(filename);
24+
25+
delete(expected);
26+
27+
end
28+
29+
function test_saveCfg_bare_bone()
30+
31+
cfg.testingDevice = 'mri';
32+
33+
filename = saveCfg(cfg);
34+
35+
expected = fullfile(pwd, ['date-' datestr(now, 'yyyymmddHHMM') '_cfg.json']);
36+
37+
assertEqual(filename, expected);
38+
39+
content = bids.util.jsondecode(filename);
40+
41+
assertEqual(content, struct('testingDevice', 'mri'));
42+
43+
delete(expected);
44+
45+
end
46+
47+
function test_saveCfg_filename_provided()
48+
49+
cfg.testingDevice = 'mri';
50+
51+
filename = saveCfg(cfg, fullfile(pwd, 'cfg', 'cfg.json'));
52+
53+
expected = fullfile(pwd, 'cfg', 'cfg.json');
54+
55+
assertEqual(filename, expected);
56+
57+
delete(expected);
58+
rmdir(fullfile(pwd, 'cfg'));
59+
60+
end

0 commit comments

Comments
 (0)