Skip to content

Commit 9aa30db

Browse files
committed
Merge remote-tracking branch 'cpp-lln-lab/master'
2 parents 17f9d1e + c6659f7 commit 9aa30db

22 files changed

+540
-108
lines changed

.gitignore

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66
*octave-workspace
77

88
# exclude content of logfiles folders
9-
tests/output/*
10-
manualTests/output/*
11-
output/*
9+
*output*
1210
*.tsv
1311
*.mat
1412

1513
# exclude temp files from tests and coverage
16-
tests/coverage*
17-
tests/test_code_report.txt
18-
test_code_report.txt
14+
*test_code_report.txt
15+
*coverage*
16+
17+
tests/*.nii*
18+
tests/*.json*
19+
tests/*.tsv*
1920

2021
# exclude report from check_my_code
2122
check_my_code_report.txt

README.md

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
1-
[![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-) [![Build Status](https://travis-ci.com/cpp-lln-lab/CPP_BIDS.svg?branch=master)](https://travis-ci.com/cpp-lln-lab/CPP_BIDS)
1+
**Unit tests and coverage**
22

3+
[![](https://img.shields.io/badge/Octave-CI-blue?logo=Octave&logoColor=white)](https://github.com/cpp-lln-lab/CPP_BIDS/actions)
4+
![](https://github.com/cpp-lln-lab/CPP_BIDS/workflows/CI/badge.svg)
5+
6+
[![codecov](https://codecov.io/gh/cpp-lln-lab/CPP_BIDS/branch/master/graph/badge.svg)](https://codecov.io/gh/cpp-lln-lab/CPP_BIDS)
7+
8+
**BIDS validator and linter**
9+
10+
[![Build Status](https://travis-ci.com/cpp-lln-lab/CPP_BIDS.svg?branch=master)](https://travis-ci.com/cpp-lln-lab/CPP_BIDS)
11+
12+
**Contributors**
13+
14+
[![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-)
15+
16+
---
17+
318
# CPP_BIDS
419

520
<!-- TOC -->
@@ -15,9 +30,11 @@
1530
- [saveEventsFile](#saveeventsfile)
1631
- [checkCFG](#checkcfg)
1732
- [CFG content](#cfg-content)
33+
- [createBoldJson](#createboldjson)
1834
- [How to install](#how-to-install)
1935
- [Download with git](#download-with-git)
2036
- [Add as a submodule](#add-as-a-submodule)
37+
- [Example for submodule usage](#example-for-submodule-usage)
2138
- [Direct download](#direct-download)
2239
- [Contributing](#contributing)
2340
- [Guidestyle](#guidestyle)
@@ -36,7 +53,7 @@ Subjects, session and run number labels will be numbers with zero padding up to
3653

3754
A session folder will ALWAYS be created even if not requested (default will be `ses-001`).
3855

39-
Task labels will be printed in camelCase in the filenames.
56+
Task labels will be printed in camelCase in the filenames.
4057

4158
Time stamps are added directly in the filename by adding a suffix `_date-YYYYMMDDHHMM` which makes the file name non-BIDS compliant. This was added to prevent overwriting files in case a certain run needs to be done a second time because of a crash (Some of us are paranoid about keeping even cancelled runs during my experiments). This suffix should be removed to make the data set BIDS compliant. See `convertSourceToRaw.m` for more details.
4259

@@ -56,14 +73,14 @@ sub-090/ses-003/sub-090_ses-003_task-auditoryTask_run-023_events_date-2020072915
5673
cfg.outputDir = fullfile(pwd, '..', 'output');
5774
5875
% define the name of the task
59-
cfg.task = 'test task';
76+
cfg.task.name = 'test task';
6077
6178
% can use the userInputs function to collect subject info
6279
% cfg = userInputs;
6380
6481
% or declare it directly
65-
cfg.subjectNb = 1;
66-
cfg.runNb = 1;
82+
cfg.subject.subjectNb = 1;
83+
cfg.subject.runNb = 1;
6784
6885
% by default we assume you are running things on a behavioral PC with no eyetracker
6986
% cfg.eyeTracker = false;
@@ -109,9 +126,9 @@ saveEventsFile('close', cfg, logFile);
109126
If you want to save more complex events.tsv file you can save several columns at once.
110127

111128
```matlab
112-
cfg.subjectNb = 1;
113-
cfg.runNb = 1;
114-
cfg.task = 'testtask';
129+
cfg.subject.subjectNb = 1;
130+
cfg.subject.runNb = 1;
131+
cfg.task.name = 'testtask';
115132
cfg.outputDir = outputDir;
116133
117134
cfg.testingDevice = 'mri';
@@ -206,7 +223,7 @@ For the moment the date of acquisition is appended to the filename
206223

207224
Function to save output files for events that will be BIDS compliant.
208225

209-
If the user DOES NOT provide `onset`, `trial_type`, this events will be skipped. `duration` will be set to "NaN" if
226+
If the user DOES NOT provide `onset`, `trial_type`, this events will be skipped. `duration` will be set to "NaN" if
210227
no value is provided.
211228

212229
### checkCFG
@@ -216,7 +233,7 @@ Check that we have all the fields that we need in the experiment parameters.
216233
## CFG content
217234

218235
```matlab
219-
%% Can be modified by users
236+
%% Can be modified by users
220237
% but their effect might only be effective after running
221238
% checkCFG
222239
@@ -281,6 +298,26 @@ cfg.fileName.datasetDescription
281298
282299
```
283300

301+
### createBoldJson
302+
303+
```
304+
createBoldJson(cfg)
305+
```
306+
307+
This function creates a very light-weight version of the side-car JSON file for a BOLD functional run.
308+
309+
This will only contain the minimum BIDS requirement and will likely be less complete than the info you could from DICOM conversion.
310+
311+
If you put the following line at the end of your experiment script, it will dump the content of the `extraInfo` structure in the json file.
312+
313+
```
314+
createBoldJson(cfg, extraInfo)
315+
```
316+
317+
This allows to add all the parameters that you used to run your experiment in a human readable format: so that when you write your methods sections 2 years later ("the reviewer asked me for the size of my fixation cross... FML"), the info you used WHEN you ran the experiment is saved in an easily accessible text format. For the love of the flying spaghetti monster do not save all your parameters in a `.mat` file: think of the case when you won't have matlab or octave installed on a computer (plus not everyone uses those).
318+
319+
Also to reading your experiment parameters, you won't have to read it from the `setParameters.m` file and wonder if those might have been modified when running the experiment and you did not commit and tagged that change with git.
320+
284321
## How to install
285322

286323
### Download with git
@@ -290,7 +327,7 @@ cd fullpath_to_directory_where_to_install
290327
# use git to download the code
291328
git clone https://github.com/cpp-lln-lab/CPP_BIDS.git
292329
# move into the folder you have just created
293-
cd CPP_PTB
330+
cd CPP_BIDS
294331
# add the src folder to the matlab path and save the path
295332
matlab -nojvm -nosplash -r "addpath(fullfile(pwd, 'src')); savepath ();"
296333
```
@@ -316,18 +353,37 @@ cd fullpath_to_directory_where_to_install
316353
# use git to download the code
317354
git submodule add https://github.com/cpp-lln-lab/CPP_BIDS.git
318355
# move into the folder you have just created
319-
cd CPP_PTB
356+
cd CPP_BIDS
320357
# add the src folder to the matlab path and save the path
321358
matlab -nojvm -nosplash -r "addpath(fullfile(pwd, 'src'))"
322359
```
323360

324-
To get the latest commit you then need to update the submodule with the information
361+
To get the latest commit you then need to update the submodule with the information
325362
on its remote repository and then merge those locally.
326363
```bash
327364
git submodule update --remote --merge
328365
```
329366

330-
Remember that updates to submodules need to be commited as well.
367+
Remember that updates to submodules need to be committed as well.
368+
369+
#### Example for submodule usage
370+
371+
So say you want to clone a repo that has some nested submodules, then you would type this to get the content of all the submodules at once (here with assumption that you want to clone my experiment repo):
372+
```bash
373+
# clone the repo
374+
git clone https://github.com/user_name/myExperiment.git
375+
376+
# go into the directory
377+
cd myExperiment
378+
379+
# initialize and get the content of the first level of submodules (e.g. CPP_PTB and CPP_BIDS)
380+
git submodule init
381+
git submodule update
382+
383+
# get the nested submodules JSONio and BIDS-matlab for CPP_BIDS
384+
git submodule foreach --recursive 'git submodule init'
385+
git submodule foreach --recursive 'git submodule update'
386+
```
331387

332388
**TO DO**
333389
<!-- Submodules
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"Instructions": "",
3+
"RepetitionTime": [],
4+
"SliceTiming": [],
5+
"TaskDescription": "",
6+
"TaskName": "testtask",
7+
"extraInfo": {
8+
"nestedExtraInfo": "something extra"
9+
}
10+
}

manualTests/test_createBoldJson.m

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
function test_suite = test_createBoldJson %#ok<*STOUT>
2+
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
3+
test_functions = localfunctions(); %#ok<*NASGU>
4+
catch % no problem; early Matlab versions can use initTestSuite fine
5+
end
6+
initTestSuite;
7+
end
8+
9+
function test_createBoldJsonExtra()
10+
11+
outputDir = fullfile(fileparts(mfilename('fullpath')), 'output');
12+
13+
%% set up
14+
15+
cfg.verbose = false;
16+
17+
cfg.subject.subjectNb = 1;
18+
cfg.subject.runNb = 1;
19+
20+
cfg.task.name = 'testtask';
21+
22+
cfg.dir.output = outputDir;
23+
24+
cfg.testingDevice = 'mri';
25+
26+
cfg = createFilename(cfg);
27+
28+
logFile = saveEventsFile('init', cfg); %#ok<*NASGU>
29+
30+
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
31+
32+
createBoldJson(cfg, extraInfo);
33+
34+
%% check content
35+
fileName = strrep(cfg.fileName.events, '_events', '_bold');
36+
fileName = strrep(fileName, '.tsv', '.json');
37+
38+
actualStruct = bids.util.jsondecode(fullfile( ...
39+
cfg.dir.outputSubject, ...
40+
cfg.fileName.modality, ...
41+
fileName));
42+
43+
% data to test against
44+
expectedStruct = bids.util.jsondecode( ...
45+
fullfile(pwd, 'testData', 'extra_bold.json'));
46+
47+
% test
48+
assertEqual(expectedStruct, actualStruct);
49+
50+
end

manualTests/test_makeRawDataset.m

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,36 @@ function test_makeRawDataset()
77
end
88

99
%% set up
10-
11-
%%% set up
12-
13-
cfg.subject.subjectNb = 1;
14-
cfg.subject.runNb = 1;
15-
16-
cfg.task.name = 'testtask';
17-
1810
cfg.dir.output = outputDir;
1911

2012
cfg.bids.datasetDescription.Name = 'dummy';
2113
cfg.bids.datasetDescription.BIDSVersion = '1.0.0';
2214
cfg.bids.datasetDescription.Authors = {'Jane Doe', 'John Doe'};
2315

24-
cfg.bids.mri.RepetitionTime = 1.56;
25-
2616
cfg.testingDevice = 'mri';
2717

18+
%% MRI task data
19+
cfg.mri.repetitionTime = 1.56;
20+
21+
cfg.subject.subjectNb = 1;
22+
cfg.subject.runNb = 1;
23+
24+
cfg.task.name = 'testtask';
25+
2826
logFile.extraColumns.Speed.length = 1;
2927
logFile.extraColumns.LHL24.length = 3;
3028
logFile.extraColumns.is_Fixation.length = 1;
3129

32-
%%% do stuff
33-
3430
cfg = createFilename(cfg);
3531

36-
% create the events file and header
37-
logFile = saveEventsFile('open', cfg, logFile);
32+
extraInfo = struct('extraInfo', struct('nestedExtraInfo', 'something extra'));
33+
createBoldJson(cfg, extraInfo);
3834

39-
createBoldJson(cfg);
4035
createDatasetDescription(cfg);
4136

37+
% create the events file and header
38+
logFile = saveEventsFile('open', cfg, logFile);
39+
4240
% ROW 2: normal events : all info is there
4341
logFile(1, 1).onset = 2;
4442
logFile(end, 1).trial_type = 'MotionUp';
@@ -71,14 +69,67 @@ function test_makeRawDataset()
7169
% close the file
7270
saveEventsFile('close', cfg, logFile);
7371

72+
% add dummy stim data
73+
stimLogFile = saveEventsFile('open_stim', cfg, logFile);
74+
for i = 1:100
75+
stimLogFile(i, 1).onset = cfg.mri.repetitionTime * i;
76+
stimLogFile(i, 1).trial_type = 'test';
77+
stimLogFile(i, 1).duration = 1;
78+
stimLogFile(i, 1).Speed = rand(1);
79+
stimLogFile(i, 1).is_Fixation = rand > 0.5;
80+
stimLogFile(i, 1).LHL24 = randn(1, 3);
81+
end
82+
saveEventsFile('save', cfg, stimLogFile);
83+
saveEventsFile('close', cfg, stimLogFile);
84+
7485
% add dummy functional data
7586
funcDir = fullfile(cfg.dir.output, 'source', 'sub-001', 'ses-001', 'func');
7687
boldFilename = 'sub-001_ses-001_task-testtask_run-001_bold.nii.gz';
88+
7789
copyfile( ...
78-
fullfile('..', 'dummyData', 'dummyData.nii.gz'), ...
90+
fullfile('dummyData', 'dummyData.nii.gz'), ...
7991
fullfile(funcDir, boldFilename));
8092

81-
%%
93+
%% MRI bold rest data and fancy suffixes
94+
clear cfg;
95+
96+
cfg.dir.output = outputDir;
97+
98+
cfg.testingDevice = 'mri';
99+
100+
cfg.subject.subjectNb = 2;
101+
cfg.subject.sessionNb = 3;
102+
cfg.subject.runNb = 4;
103+
104+
% deal with MRI suffixes
105+
cfg.mri.reconstruction = 'fast recon';
106+
cfg.mri.contrastEnhancement = 'test';
107+
cfg.mri.phaseEncodingDirection = 'y pos';
108+
cfg.mri.echo = '1';
109+
cfg.mri.acquisition = ' new tYpe';
110+
cfg.mri.repetitionTime = 1.56;
111+
112+
cfg.task.name = 'rest';
113+
114+
cfg = createFilename(cfg);
82115

116+
createBoldJson(cfg);
117+
118+
%% add dummy functional data
119+
funcDir = fullfile(cfg.dir.output, 'source', 'sub-002', 'ses-003', 'func');
120+
boldFilename = ['sub-002_ses-003_task-rest', ...
121+
'_acq-newTYpe_ce-test_dir-yPos_rec-fastRecon', ...
122+
'_run-004_echo-1_bold.nii.gz'];
123+
124+
copyfile( ...
125+
fullfile('dummyData', 'dummyData.nii.gz'), ...
126+
fullfile(funcDir, boldFilename));
127+
128+
%% actually do the conversion of the source data thus created
129+
clear;
130+
131+
outputDir = fullfile(fileparts(mfilename('fullpath')), 'output');
132+
cfg.dir.output = outputDir;
83133
convertSourceToRaw(cfg);
134+
84135
end

0 commit comments

Comments
 (0)