Skip to content

Commit

Permalink
Merge pull request #514 from olgn/merged-dictionary-schema-check
Browse files Browse the repository at this point in the history
schema checks are made against merged json dictionaries
  • Loading branch information
nellh authored Aug 29, 2018
2 parents cbe3f5e + e98216b commit 22962bd
Show file tree
Hide file tree
Showing 5 changed files with 377 additions and 258 deletions.
155 changes: 125 additions & 30 deletions tests/json.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,39 @@ describe('JSON', function() {
name: 'task-rest_bold.json',
relativePath: '/task-rest_bold.json',
}

it('should catch missing closing brackets', function() {
validate.JSON(file, '{', function(issues) {
assert(issues && issues.length > 0)
})
})
var jsonDict = {}

it('sidecars should have key/value pair for "RepetitionTime" expressed in seconds', function() {
var jsonObj =
'{"RepetitionTime": 1.2, "echo_time": 0.005, "flip_angle": 90, "TaskName": "Rest"}'
validate.JSON(file, jsonObj, function(issues) {
var jsonObj = {
RepetitionTime: 1.2,
echo_time: 0.005,
flip_angle: 90,
TaskName: 'Rest',
}
jsonDict[file.relativePath] = jsonObj
validate.JSON(file, jsonDict, function(issues) {
assert(issues.length === 0)
})
var jsonObjInval =
'{"RepetitionTime": 1200, "echo_time": 0.005, "flip_angle": 90, "TaskName": "Rest"}'
validate.JSON(file, jsonObjInval, function(issues) {
var jsonObjInval = {
RepetitionTime: 1200,
echo_time: 0.005,
flip_angle: 90,
TaskName: 'Rest',
}
jsonDict[file.relativePath] = jsonObjInval
validate.JSON(file, jsonDict, function(issues) {
assert(issues && issues.length === 1)
})
})

it('should detect negative value for SliceTiming', function() {
var jsonObj =
'{"RepetitionTime": 1.2, "SliceTiming": [-1.0, 0.0, 1.0], "TaskName": "Rest"}'
validate.JSON(file, jsonObj, function(issues) {
var jsonObj = {
RepetitionTime: 1.2,
SliceTiming: [-1.0, 0.0, 1.0],
TaskName: 'Rest',
}
jsonDict[file.relativePath] = jsonObj
validate.JSON(file, jsonDict, function(issues) {
assert(issues.length === 1 && issues[0].code == 55)
})
})
Expand All @@ -40,17 +49,24 @@ describe('JSON', function() {
}

it('*_meg.json sidecars should have required key/value pairs', function() {
var jsonObj =
'{"TaskName": "Audiovis", "SamplingFrequency": 1000, ' +
' "PowerLineFrequency": 50, "DewarPosition": "Upright", ' +
' "SoftwareFilters": "n/a", "DigitizedLandmarks": true,' +
' "DigitizedHeadPoints": false}'
validate.JSON(meg_file, jsonObj, function(issues) {
var jsonObj = {
TaskName: 'Audiovis',
SamplingFrequency: 1000,
PowerLineFrequency: 50,
DewarPosition: 'Upright',
SoftwareFilters: 'n/a',
DigitizedLandmarks: true,
DigitizedHeadPoints: false,
}
jsonDict[meg_file.relativePath] = jsonObj
validate.JSON(meg_file, jsonDict, function(issues) {
assert(issues.length === 0)
})

var jsonObjInval = jsonObj.replace(/"SamplingFrequency": 1000, /g, '')
validate.JSON(meg_file, jsonObjInval, function(issues) {
var jsonObjInval = jsonObj
jsonObjInval['SamplingFrequency'] = ''
jsonDict[meg_file.relativePath] = jsonObjInval
validate.JSON(meg_file, jsonDict, function(issues) {
assert(issues && issues.length === 1)
})
})
Expand All @@ -61,16 +77,95 @@ describe('JSON', function() {
}

it('*_ieeg.json sidecars should have required key/value pairs', function() {
var jsonObj =
'{"TaskName": "Audiovis", "Manufacturer": "TDT", ' +
' "PowerLineFrequency": 50, "SamplingFrequency": 10, "iEEGReference": "reference"}'
validate.JSON(ieeg_file, jsonObj, function(issues) {
var jsonObj = {
TaskName: 'Audiovis',
Manufacturer: 'TDT',
PowerLineFrequency: 50,
SamplingFrequency: 10,
iEEGReference: 'reference',
}
jsonDict[ieeg_file.relativePath] = jsonObj
validate.JSON(ieeg_file, jsonDict, function(issues) {
assert(issues.length === 0)
})

var jsonObjInval = jsonObj.replace(/"Manufacturer": "TDT", /g, '')
validate.JSON(ieeg_file, jsonObjInval, function(issues) {
var jsonObjInval = jsonObj
jsonObjInval['Manufacturer'] = ''
jsonDict[ieeg_file.relativePath] = jsonObjInval
validate.JSON(ieeg_file, jsonDict, function(issues) {
assert(issues && issues.length === 1)
})
})

it('should use inherited sidecars to find missing fields', function() {
const multiEntryJsonDict = {}

// this json file is missing the SamplingFrequency field
const partialJsonObj = {
TaskName: 'Audiovis',
PowerLineFrequency: 50,
DewarPosition: 'Upright',
SoftwareFilters: 'n/a',
DigitizedLandmarks: true,
DigitizedHeadPoints: false,
}
multiEntryJsonDict[meg_file.relativePath] = partialJsonObj

// this json file (sitting at the root directory level)
// provides the missing json field
const inheritedMegFile = {
name: 'meg.json',
relativePath: '/meg.json',
}

const restOfJsonObj = {
SamplingFrequency: 2000,
}
multiEntryJsonDict[inheritedMegFile.relativePath] = restOfJsonObj

// json validation will pass because (when merged) there are no
// missing data fields
validate.JSON(meg_file, multiEntryJsonDict, function(issues) {
assert(issues.length == 0)
})
})

it('should favor the sidecar on the directory level closest to the file being validated', function() {
const multiEntryJsonDict = {}
const lowLevelFile = {
name: 'run-01_meg.json',
relativePath: '/sub-01/run-01_meg.json',
}

// this json file has a good SamplingFrequency field
const partialJsonObj = {
TaskName: 'Audiovis',
SamplingFrequency: 1000,
PowerLineFrequency: 50,
DewarPosition: 'Upright',
SoftwareFilters: 'n/a',
DigitizedLandmarks: true,
DigitizedHeadPoints: false,
}
multiEntryJsonDict[lowLevelFile.relativePath] = partialJsonObj

// this json file (sitting at the root directory level)
// also has a SamplingFrequency field, but it is wrong.
const inheritedMegFile = {
name: 'meg.json',
relativePath: '/meg.json',
}

const restOfJsonObj = {
SamplingFrequency: '',
}
multiEntryJsonDict[inheritedMegFile.relativePath] = restOfJsonObj

// json validation will pass because merged dictionaries prefer
// field values of the json sidecar furthest from the root.
// /meg.json is closer to the root than /sub-01/run-01_meg.json
// and so the values of the latter should be preferred.
validate.JSON(lowLevelFile, multiEntryJsonDict, function(issues) {
assert(issues.length == 0)
})
})
})
48 changes: 24 additions & 24 deletions utils/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,34 @@ var fileUtils = {
* In node the file should be a path to a file.
*
*/
function readFile(file, callback) {
if (fs) {
testFile(file, function(issue) {
if (issue) {
process.nextTick(function() {
callback(issue, null)
})
return
}
fs.readFile(file.path, 'utf8', function(err, data) {
process.nextTick(function() {
callback(null, data)
function readFile(file) {
return new Promise((resolve, reject) => {
if (fs) {
testFile(file, function(issue) {
if (issue) {
process.nextTick(function() {
return reject(issue)
})
}
fs.readFile(file.path, 'utf8', function(err, data) {
process.nextTick(function() {
return resolve(data)
})
})
})
})
} else {
var reader = new FileReader()
reader.onloadend = function(e) {
if (e.target.readyState == FileReader.DONE) {
if (!e.target.result) {
callback(new Issue({ code: 44, file: file }), null)
return
} else {
var reader = new FileReader()
reader.onloadend = function(e) {
if (e.target.readyState == FileReader.DONE) {
if (!e.target.result) {
return reject(new Issue({ code: 44, file: file }))
}
return resolve(e.target.result)
}
callback(null, e.target.result)
}
reader.readAsBinaryString(file)
}
reader.readAsBinaryString(file)
}
})
}

function getBIDSIgnoreFileObjNode(dir) {
Expand Down Expand Up @@ -120,7 +120,7 @@ function getBIDSIgnore(dir, callback) {

var bidsIgnoreFileObj = getBIDSIgnoreFileObj(dir)
if (bidsIgnoreFileObj) {
readFile(bidsIgnoreFileObj, function(issue, content) {
readFile(bidsIgnoreFileObj).then(content => {
ig = ig.add(content)
callback(ig)
})
Expand Down
13 changes: 7 additions & 6 deletions utils/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ module.exports = {
loadConfig: function(config, callback) {
if (typeof config === 'string') {
// load file
files.readFile({ path: config }, function(issue, contents) {
if (issue) {
callback([issue], { path: config }, null)
} else {
files
.readFile({ path: config })
.then(contents => {
callback(null, { path: config }, contents)
}
})
})
.catch(issue => {
callback([issue], { path: config }, null)
})
} else if (typeof config === 'object') {
callback(null, { path: 'config' }, JSON.stringify(config))
}
Expand Down
Loading

0 comments on commit 22962bd

Please sign in to comment.