Skip to content

Commit 0e7ee1c

Browse files
authored
Merge pull request #381 from chrisfilo/enh/bidsignore
Implementation of .bidsignore
2 parents 973bebd + 59e6203 commit 0e7ee1c

File tree

7 files changed

+90
-24
lines changed

7 files changed

+90
-24
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ If you would like to test individual files you can use the file specific checks
4444

4545
Additionally you can reformat stored errors against a new config using `validate.reformat()`
4646

47+
#### .bidsignore
48+
Optionally one can include a `.bidsignore` file in the root of the dataset. This file lists patterns (compatible
49+
with the [.gitignore syntax](https://git-scm.com/docs/gitignore)) defining files that should be ignored by the
50+
validator. This option is useful when the validated dataset includes file types not yet supported by BIDS specification.
51+
4752
#### Configuration
4853

4954
You can configure the severity of errors by passing a json configuration file with a --c or --config flag to the command line interface or by defining a config object on the options object passed during javascript usage.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
"pako": "^1.0.4",
3434
"path": "^0.12.7",
3535
"pluralize": "^3.1.0",
36-
"yargs": "^6.6.0"
36+
"yargs": "^6.6.0",
37+
"ignore": "^3.3.7"
3738
},
3839
"devDependencies": {
3940
"adm-zip": "",

tests/data/valid_headers/.bidsignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*_not_bids.txt

tests/data/valid_headers/sub-01/func/extra_file_that_is_not_bids.txt

Whitespace-only changes.

utils/files.js

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
var nifti = require('nifti-js');
44
var Issue = require('./issues').Issue;
5-
var type = require('./type');
5+
var ignore = require('ignore');
6+
var path = require('path');
67

78
/**
89
* If the current environment is server side
@@ -65,7 +66,61 @@ function readFile (file, callback) {
6566
}
6667
}
6768

69+
function getBIDSIgnoreFileObjNode(dir) {
70+
var bidsIgnoreFileObj = null;
71+
var path = dir + "/.bidsignore";
72+
if (fs.existsSync(path)) {
73+
bidsIgnoreFileObj = {path: path};
74+
}
75+
return bidsIgnoreFileObj;
76+
}
77+
78+
function getBIDSIgnoreFileObjBrowser(dir) {
79+
var bidsIgnoreFileObj = null;
80+
for (var i = 0; i < dir.length; i++) {
81+
var fileObj = dir[i];
82+
var relativePath = harmonizeRelativePath(fileObj.webkitRelativePath);
83+
if (relativePath === "/.bidsignore") {
84+
bidsIgnoreFileObj = fileObj;
85+
break;
86+
}
87+
}
88+
return bidsIgnoreFileObj;
89+
}
90+
91+
/**
92+
* Get File object corresponding to the .bidsignore file
93+
* @param dir
94+
* @returns File object or null if not found
95+
*/
96+
function getBIDSIgnoreFileObj(dir) {
97+
var bidsIgnoreFileObj = null;
98+
if (fs) {
99+
bidsIgnoreFileObj = getBIDSIgnoreFileObjNode(dir);
100+
} else {
101+
bidsIgnoreFileObj = getBIDSIgnoreFileObjBrowser(dir);
102+
}
103+
return bidsIgnoreFileObj;
104+
}
68105

106+
function getBIDSIgnore(dir, callback) {
107+
var ig = ignore()
108+
.add('/derivatives')
109+
.add('/sourcedata')
110+
.add('/code')
111+
.add('.*');
112+
113+
var bidsIgnoreFileObj = getBIDSIgnoreFileObj(dir);
114+
if (bidsIgnoreFileObj) {
115+
readFile(bidsIgnoreFileObj, function (issue, content) {
116+
ig = ig.add(content);
117+
callback(ig);
118+
});
119+
} else {
120+
callback(ig);
121+
}
122+
return ig;
123+
}
69124

70125
/**
71126
* Read Directory
@@ -80,16 +135,20 @@ function readFile (file, callback) {
80135
function readDir (dir, callback) {
81136
var filesObj = {};
82137
var filesList = [];
83-
if (fs) {
84-
filesList = preprocessNode(dir);
85-
} else {
86-
filesList = preprocessBrowser(dir);
87-
}
88-
// converting array to object
89-
for (var j = 0; j < filesList.length; j++) {
90-
filesObj[j] = filesList[j];
138+
139+
function callbackWrapper(ig) {
140+
if (fs) {
141+
filesList = preprocessNode(dir, ig);
142+
} else {
143+
filesList = preprocessBrowser(dir, ig);
144+
}
145+
// converting array to object
146+
for (var j = 0; j < filesList.length; j++) {
147+
filesObj[j] = filesList[j];
148+
}
149+
callback(filesObj);
91150
}
92-
callback(filesObj);
151+
getBIDSIgnore(dir, callbackWrapper);
93152
}
94153

95154
/**
@@ -98,12 +157,12 @@ function readDir (dir, callback) {
98157
* 1. Filters out ignored files and folder.
99158
* 2. Adds 'relativePath' field of each file object.
100159
*/
101-
function preprocessBrowser(filesObj) {
160+
function preprocessBrowser(filesObj, ig) {
102161
var filesList = [];
103162
for (var i = 0; i < filesObj.length; i++) {
104163
var fileObj = filesObj[i];
105164
fileObj.relativePath = harmonizeRelativePath(fileObj.webkitRelativePath);
106-
if (type.isIgnoredPath(fileObj.relativePath)) {
165+
if (ig.ignores(path.relative('/', fileObj.relativePath))) {
107166
continue;
108167
}
109168
filesList.push(fileObj);
@@ -118,23 +177,23 @@ function preprocessBrowser(filesObj) {
118177
* 2. Filters out ignored files and folder.
119178
* 3. Harmonizes the 'relativePath' field
120179
*/
121-
function preprocessNode(dir) {
180+
function preprocessNode(dir, ig) {
122181
var str = dir.substr(dir.lastIndexOf('/') + 1) + '$';
123182
var rootpath = dir.replace(new RegExp(str), '');
124-
return getFiles(dir, [], rootpath);
183+
return getFiles(dir, [], rootpath, ig);
125184
}
126185

127186
/**
128187
* Recursive helper function for 'preprocessNode'
129188
*/
130-
function getFiles(dir, files_, rootpath){
189+
function getFiles(dir, files_, rootpath, ig){
131190
files_ = files_ || [];
132191
var files = fs.readdirSync(dir);
133192
for (var i = 0; i < files.length; i++) {
134193
var fullPath = dir + '/' + files[i];
135194
var relativePath = fullPath.replace(rootpath, '');
136195
relativePath = harmonizeRelativePath(relativePath);
137-
if (type.isIgnoredPath(relativePath)){
196+
if (ig.ignores(path.relative('/', relativePath))) {
138197
continue;
139198
}
140199
var fileName = files[i];
@@ -146,7 +205,7 @@ function getFiles(dir, files_, rootpath){
146205
};
147206

148207
if (fs.lstatSync(fullPath).isDirectory()) {
149-
getFiles(fullPath, files_, rootpath);
208+
getFiles(fullPath, files_, rootpath, ig);
150209
} else {
151210
files_.push(fileObj);
152211
}

utils/issues/list.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ module.exports = {
99
1: {
1010
key: 'NOT_INCLUDED',
1111
severity: 'error',
12-
reason: 'Files with such naming scheme are not part of BIDS specification. This error is most commonly caused by typos in file names that make them not BIDS compatible. Please consult the specification and make sure your files are named correctly. If this is not a file naming issue (for example when including files not yet covered by the BIDS specification) you can ignore this warning. Please note that derived (processed) data should be placed in /derivatives folder and source data (such as DICOMS or behavioural logs in proprietary formats) should be placed in the /sourcedata folder.'
12+
reason: 'Files with such naming scheme are not part of BIDS specification. This error is most commonly ' +
13+
'caused by typos in file names that make them not BIDS compatible. Please consult the specification and ' +
14+
'make sure your files are named correctly. If this is not a file naming issue (for example when including ' +
15+
'files not yet covered by the BIDS specification) you should include a ".bidsignore" file in your dataset. Please ' +
16+
'note that derived (processed) data should be placed in /derivatives folder and source data (such as DICOMS ' +
17+
'or behavioural logs in proprietary formats) should be placed in the /sourcedata folder.'
1318
},
1419
2: {
1520
key: 'REPETITION_TIME_GREATER_THAN',

utils/type.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,6 @@ module.exports = {
199199
return !isNaN(parseFloat(n)) && isFinite(n);
200200
},
201201

202-
isIgnoredPath: function (path) {
203-
var ignoredDirsRe = new RegExp('^\\/(derivatives|sourcedata|code).*$');
204-
var ignoreHiddenRe = new RegExp('^.*\\/[\\.].+$');
205-
return conditionalMatch(ignoredDirsRe, path) || conditionalMatch(ignoreHiddenRe, path);
206-
},
207202

208203
/**
209204
* Get Path Values

0 commit comments

Comments
 (0)