diff --git a/bids/index.js b/bids/index.js index 1a89f4f1..aee8dd1c 100644 --- a/bids/index.js +++ b/bids/index.js @@ -1,10 +1,20 @@ -import { BidsDataset, BidsEventFile, BidsHedIssue, BidsTsvFile, BidsIssue, BidsJsonFile, BidsSidecar } from './types' +import { + BidsDataset, + BidsEventFile, + BidsTabularFile, + BidsHedIssue, + BidsTsvFile, + BidsIssue, + BidsJsonFile, + BidsSidecar, +} from './types' import { validateBidsDataset } from './validate' export { BidsDataset, BidsTsvFile, BidsEventFile, + BidsTabularFile, BidsJsonFile, BidsSidecar, BidsIssue, @@ -16,6 +26,7 @@ export default { BidsDataset, BidsTsvFile, BidsEventFile, + BidsTabularFile, BidsJsonFile, BidsSidecar, BidsIssue, diff --git a/bids/tsvParser.js b/bids/tsvParser.js new file mode 100644 index 00000000..5cde3776 --- /dev/null +++ b/bids/tsvParser.js @@ -0,0 +1,31 @@ +/** + * Module for parsing TSV files. + * + * Copied from https://github.com/bids-standard/bids-validator/blob/6fc6d152b52266934575442e61f1477ba18f42ec/bids-validator/validators/tsv/tsvParser.js + */ + +const stripBOM = (str) => str.replace(/^\uFEFF/, '') +const normalizeEOL = (str) => str.replace(/\r\n/g, '\n').replace(/\r/g, '\n') +const isContentfulRow = (row) => row && !/^\s*$/.test(row) + +/** + * Parse a TSV file. + * + * @param {string} contents The contents of a TSV file. + * @return {{headers: string[], rows: string[][]}} The parsed contents of the TSV file. + */ +function parseTSV(contents) { + const content = { + headers: [], + rows: [], + } + contents = stripBOM(contents) + content.rows = normalizeEOL(contents) + .split('\n') + .filter(isContentfulRow) + .map((str) => str.split('\t')) + content.headers = content.rows.length ? content.rows[0] : [] + return content +} + +export default parseTSV diff --git a/bids/types.js b/bids/types.js index 2e2e0a27..0d710a5d 100644 --- a/bids/types.js +++ b/bids/types.js @@ -1,20 +1,28 @@ import { sidecarValueHasHed } from './utils' import { Issue } from '../common/issues/issues' +import parseTSV from './tsvParser' +/** + * Base class for BIDS data. + * @deprecated Will be removed in v4.0.0. + */ class BidsData { /** * A mapping from unparsed HED strings to ParsedHedString objects. + * @deprecated Will be removed in v4.0.0. * @type {Map} */ parsedStringMapping /** * A Mapping from definition names to their associated ParsedHedGroup objects. + * @deprecated Will be removed in v4.0.0. * @type {Map} */ definitions /** * A list of HED validation issues. * This will be converted to BidsIssue objects later on. + * @deprecated Will be removed in v4.0.0. * @type {Issue[]} */ hedIssues @@ -26,6 +34,9 @@ class BidsData { } } +/** + * A BIDS file. + */ class BidsFile extends BidsData { /** * The name of this file. @@ -46,6 +57,9 @@ class BidsFile extends BidsData { } } +/** + * A BIDS JSON file. + */ export class BidsJsonFile extends BidsFile { /** * This file's JSON data. @@ -59,10 +73,13 @@ export class BidsJsonFile extends BidsFile { } } +/** + * A BIDS TSV file. + */ export class BidsTsvFile extends BidsFile { /** * This file's parsed TSV data. - * @type {object} + * @type {{headers: string[], rows: string[][]}} */ parsedTsv /** @@ -70,10 +87,43 @@ export class BidsTsvFile extends BidsFile { * @type {string[]} */ hedColumnHedStrings + /** + * The list of potential JSON sidecars. + * @type {string[]} + */ + potentialSidecars + /** + * The pseudo-sidecar object representing the merged sidecar data. + * @type {BidsSidecar} + */ + mergedSidecar + /** + * The extracted HED data for the merged pseudo-sidecar. + * @type {Map>} + */ + sidecarHedData - constructor(name, parsedTsv, file) { + /** + * Constructor. + * + * @todo This interface is provisional and subject to modification in version 4.0.0. + * + * @param {string} name The name of the TSV file. + * @param {{headers: string[], rows: string[][]}|string} tsvData This file's TSV data. + * @param {object} file The file object representing this file. + * @param {string[]} potentialSidecars The list of potential JSON sidecars. + * @param {object} mergedDictionary The merged sidecar data. + */ + constructor(name, tsvData, file, potentialSidecars = [], mergedDictionary = {}) { super(name, file) - this.parsedTsv = parsedTsv + if (typeof tsvData === 'string') { + tsvData = parseTSV(tsvData) + } + this.parsedTsv = tsvData + this.potentialSidecars = potentialSidecars + + this.mergedSidecar = new BidsSidecar(name, mergedDictionary, null) + this.sidecarHedData = this.mergedSidecar.hedData this._parseHedColumn() } @@ -90,29 +140,43 @@ export class BidsTsvFile extends BidsFile { } } +/** + * A BIDS events.tsv file. + */ export class BidsEventFile extends BidsTsvFile { /** - * The potential JSON sidecar data. - * @type {string[]} + * Constructor. + * + * @todo This interface is subject to modification in version 4.0.0. + * + * @param {string} name The name of the event TSV file. + * @param {string[]} potentialSidecars The list of potential JSON sidecars. + * @param {object} mergedDictionary The merged sidecar data. + * @param {{headers: string[], rows: string[][]}|string} tsvData This file's TSV data. + * @param {object} file The file object representing this file. */ - potentialSidecars + constructor(name, potentialSidecars, mergedDictionary, tsvData, file) { + super(name, tsvData, file, potentialSidecars, mergedDictionary) + } +} + +/** + * A BIDS TSV file other than an events.tsv file. + */ +export class BidsTabularFile extends BidsTsvFile { /** - * The pseudo-sidecar object representing the merged sidecar data. - * @type {BidsSidecar} + * Constructor. + * + * @todo This interface is subject to modification in version 4.0.0. + * + * @param {string} name The name of the TSV file. + * @param {string[]} potentialSidecars The list of potential JSON sidecars. + * @param {object} mergedDictionary The merged sidecar data. + * @param {{headers: string[], rows: string[][]}|string} tsvData This file's TSV data. + * @param {object} file The file object representing this file. */ - mergedSidecar - /** - * The extracted HED data for the merged pseudo-sidecar. - * @type {Map>} - */ - sidecarHedData - - constructor(name, potentialSidecars, mergedDictionary, parsedTsv, file) { - super(name, parsedTsv, file) - this.potentialSidecars = potentialSidecars - - this.mergedSidecar = new BidsSidecar(name, mergedDictionary, null) - this.sidecarHedData = this.mergedSidecar.hedData + constructor(name, potentialSidecars, mergedDictionary, tsvData, file) { + super(name, tsvData, file, potentialSidecars, mergedDictionary) } } @@ -133,6 +197,13 @@ export class BidsSidecar extends BidsJsonFile { */ hedCategoricalStrings + /** + * Constructor. + * + * @param {string} name The name of the sidecar file. + * @param {Object} sidecarData The raw JSON data. + * @param {Object} file The file object representing this file. + */ constructor(name, sidecarData = {}, file) { super(name, sidecarData, file) @@ -182,7 +253,11 @@ export class BidsSidecar extends BidsJsonFile { } } -// TODO: Remove in v4.0.0. +/** + * Fallback default dataset_description.json file. + * @deprecated Will be removed in v4.0.0. + * @type {BidsJsonFile} + */ const fallbackDatasetDescription = new BidsJsonFile('./dataset_description.json', null) export class BidsDataset extends BidsData { diff --git a/bids/validate.js b/bids/validate.js index 831cf4ce..e7b3bb4e 100644 --- a/bids/validate.js +++ b/bids/validate.js @@ -1,6 +1,6 @@ import { validateHedDatasetWithContext } from '../validator/dataset' import { validateHedString } from '../validator/event' -import { BidsDataset, BidsHedIssue, BidsIssue } from './types' +import { BidsDataset, BidsEventFile, BidsHedIssue, BidsIssue } from './types' import { buildBidsSchemas } from './schema' import { generateIssue, Issue, IssueError } from '../common/issues/issues' @@ -39,7 +39,7 @@ function validateFullDataset(dataset, hedSchemas) { return Promise.resolve([...sidecarIssues, ...hedColumnIssues]) } const eventFileIssues = dataset.eventData.map((eventFileData) => { - return validateBidsEventFile(eventFileData, hedSchemas) + return validateBidsTsvFile(eventFileData, hedSchemas) }) return Promise.resolve([].concat(sidecarIssues, hedColumnIssues, ...eventFileIssues)) } catch (e) { @@ -48,18 +48,18 @@ function validateFullDataset(dataset, hedSchemas) { } /** - * Validate a BIDS event TSV file. + * Validate a BIDS TSV file. * - * @param {BidsEventFile} eventFileData A BIDS event TSV file. + * @param {BidsTsvFile} tsvFileData A BIDS TSV file. * @param {Schemas} hedSchemas A HED schema collection. * @return {BidsIssue[]} Any issues found. */ -function validateBidsEventFile(eventFileData, hedSchemas) { - const [hedStrings, tsvIssues] = parseTsvHed(eventFileData) +function validateBidsTsvFile(tsvFileData, hedSchemas) { + const [hedStrings, tsvIssues] = parseTsvHed(tsvFileData) if (!hedStrings) { return [] } else { - const datasetIssues = validateCombinedDataset(hedStrings, hedSchemas, eventFileData) + const datasetIssues = validateCombinedDataset(hedStrings, hedSchemas, tsvFileData) return [...tsvIssues, ...datasetIssues] } } @@ -108,33 +108,33 @@ function validateHedColumn(eventData, hedSchemas) { } /** - * Combine the BIDS sidecar HED data into a BIDS event TSV file's HED data. + * Combine the BIDS sidecar HED data into a BIDS TSV file's HED data. * - * @param {BidsEventFile} eventFileData A BIDS event TSV file. - * @return {[string[], BidsIssue[]]} The combined HED strings for this BIDS event TSV file, and all issues found during the combination. + * @param {BidsTsvFile} tsvFileData A BIDS TSV file. + * @return {[string[], BidsIssue[]]} The combined HED strings for this BIDS TSV file, and all issues found during the combination. */ -function parseTsvHed(eventFileData) { +function parseTsvHed(tsvFileData) { const hedStrings = [] const issues = [] const sidecarHedColumnIndices = {} - for (const sidecarHedColumn of eventFileData.sidecarHedData.keys()) { - const sidecarHedColumnHeader = eventFileData.parsedTsv.headers.indexOf(sidecarHedColumn) + for (const sidecarHedColumn of tsvFileData.sidecarHedData.keys()) { + const sidecarHedColumnHeader = tsvFileData.parsedTsv.headers.indexOf(sidecarHedColumn) if (sidecarHedColumnHeader > -1) { sidecarHedColumnIndices[sidecarHedColumn] = sidecarHedColumnHeader } } - if (eventFileData.hedColumnHedStrings.length + sidecarHedColumnIndices.length === 0) { + if (tsvFileData.hedColumnHedStrings.length + sidecarHedColumnIndices.length === 0) { return [[], []] } - eventFileData.parsedTsv.rows.slice(1).forEach((rowCells, rowIndex) => { + tsvFileData.parsedTsv.rows.slice(1).forEach((rowCells, rowIndex) => { // get the 'HED' field const hedStringParts = [] - if (eventFileData.hedColumnHedStrings[rowIndex]) { - hedStringParts.push(eventFileData.hedColumnHedStrings[rowIndex]) + if (tsvFileData.hedColumnHedStrings[rowIndex]) { + hedStringParts.push(tsvFileData.hedColumnHedStrings[rowIndex]) } for (const [sidecarHedColumn, sidecarHedIndex] of Object.entries(sidecarHedColumnIndices)) { - const sidecarHedData = eventFileData.sidecarHedData.get(sidecarHedColumn) + const sidecarHedData = tsvFileData.sidecarHedData.get(sidecarHedColumn) const rowCell = rowCells[sidecarHedIndex] if (rowCell && rowCell !== 'n/a') { let sidecarHedString @@ -154,9 +154,9 @@ function parseTsvHed(eventFileData) { generateIssue('sidecarKeyMissing', { key: rowCell, column: sidecarHedColumn, - file: eventFileData.file.relativePath, + file: tsvFileData.file.relativePath, }), - eventFileData.file, + tsvFileData.file, ), ) } @@ -176,17 +176,15 @@ function parseTsvHed(eventFileData) { * * @param {string[]} hedStrings The HED strings in the data collection. * @param {Schemas} hedSchemas The HED schema collection to validate against. - * @param {BidsEventFile} eventFileData The BIDS event TSV file being validated. + * @param {BidsTsvFile} tsvFileData The BIDS event TSV file being validated. * @return {BidsHedIssue[]} Any issues found. */ -function validateCombinedDataset(hedStrings, hedSchemas, eventFileData) { - const [, hedIssues] = validateHedDatasetWithContext( - hedStrings, - eventFileData.mergedSidecar.hedStrings, - hedSchemas, - true, - ) - return convertHedIssuesToBidsIssues(hedIssues, eventFileData.file) +function validateCombinedDataset(hedStrings, hedSchemas, tsvFileData) { + const [, hedIssues] = validateHedDatasetWithContext(hedStrings, tsvFileData.mergedSidecar.hedStrings, hedSchemas, { + checkForWarnings: true, + validateDatasetLevel: tsvFileData instanceof BidsEventFile, + }) + return convertHedIssuesToBidsIssues(hedIssues, tsvFileData.file) } /** diff --git a/tests/bids.spec.js b/tests/bids.spec.js index 236e9ed8..011496f7 100644 --- a/tests/bids.spec.js +++ b/tests/bids.spec.js @@ -383,13 +383,7 @@ describe('BIDS datasets', () => { '/sub03/sub03_task-test_run-1_events.tsv', ['/sub03/sub03_task-test_run-1_events.json'], sidecars[2][0], - { - headers: ['onset', 'duration'], - rows: [ - ['onset', 'duration'], - ['7', 'something'], - ], - }, + 'onset\tduration\n' + '7\tsomething', { relativePath: '/sub03/sub03_task-test_run-1_events.tsv', path: '/sub03/sub03_task-test_run-1_events.tsv', @@ -399,13 +393,7 @@ describe('BIDS datasets', () => { '/sub03/sub03_task-test_run-2_events.tsv', ['/sub01/sub01_task-test_run-1_events.json'], sidecars[0][0], - { - headers: ['onset', 'duration', 'color'], - rows: [ - ['onset', 'duration', 'color'], - ['7', 'something', 'red'], - ], - }, + 'onset\tduration\tcolor\n' + '7\tsomething\tred', { relativePath: '/sub03/sub03_task-test_run-2_events.tsv', path: '/sub03/sub03_task-test_run-2_events.tsv', @@ -415,13 +403,7 @@ describe('BIDS datasets', () => { '/sub03/sub03_task-test_run-3_events.tsv', ['/sub01/sub01_task-test_run-2_events.json'], sidecars[0][1], - { - headers: ['onset', 'duration', 'speed'], - rows: [ - ['onset', 'duration', 'speed'], - ['7', 'something', '60'], - ], - }, + 'onset\tduration\tspeed\n' + '7\tsomething\t60', { relativePath: '/sub03/sub03_task-test_run-3_events.tsv', path: '/sub03/sub03_task-test_run-3_events.tsv', @@ -444,13 +426,7 @@ describe('BIDS datasets', () => { '/sub03/sub03_task-test_run-5_events.tsv', ['/sub01/sub01_task-test_run-1_events.json'], sidecars[0][0], - { - headers: ['onset', 'duration', 'color', 'HED'], - rows: [ - ['onset', 'duration', 'color', 'HED'], - ['7', 'something', 'green', 'Laptop-computer'], - ], - }, + 'onset\tduration\tcolor\tHED\n' + '7\tsomething\tgreen\tLaptop-computer', { relativePath: '/sub03/sub03_task-test_run-5_events.tsv', path: '/sub03/sub03_task-test_run-5_events.tsv', @@ -460,13 +436,7 @@ describe('BIDS datasets', () => { '/sub03/sub03_task-test_run-6_events.tsv', ['/sub01/sub01_task-test_run-1_events.json', '/sub01/sub01_task-test_run-2_events.json'], Object.assign({}, sidecars[0][0], sidecars[0][1]), - { - headers: ['onset', 'duration', 'color', 'vehicle', 'speed'], - rows: [ - ['onset', 'duration', 'color', 'vehicle', 'speed'], - ['7', 'something', 'blue', 'train', '150'], - ], - }, + 'onset\tduration\tcolor\tvehicle\tspeed\n' + '7\tsomething\tblue\ttrain\t150', { relativePath: '/sub03/sub03_task-test_run-6_events.tsv', path: '/sub03/sub03_task-test_run-6_events.tsv', @@ -476,15 +446,10 @@ describe('BIDS datasets', () => { '/sub03/sub03_task-test_run-7_events.tsv', ['/sub01/sub01_task-test_run-1_events.json', '/sub01/sub01_task-test_run-2_events.json'], Object.assign({}, sidecars[0][0], sidecars[0][1]), - { - headers: ['onset', 'duration', 'color', 'vehicle', 'speed'], - rows: [ - ['onset', 'duration', 'color', 'vehicle', 'speed'], - ['7', 'something', 'red', 'train', '150'], - ['11', 'else', 'blue', 'boat', '15'], - ['15', 'another', 'green', 'car', '70'], - ], - }, + 'onset\tduration\tcolor\tvehicle\tspeed\n' + + '7\tsomething\tred\ttrain\t150\n' + + '11\telse\tblue\tboat\t15\n' + + '15\tanother\tgreen\tcar\t70', { relativePath: '/sub03/sub03_task-test_run-7_events.tsv', path: '/sub03/sub03_task-test_run-7_events.tsv', @@ -497,16 +462,11 @@ describe('BIDS datasets', () => { '/sub04/sub04_task-test_run-1_events.tsv', ['/sub02/sub02_task-test_run-2_events.json'], sidecars[1][1], - { - headers: ['onset', 'duration', 'emotion', 'HED'], - rows: [ - ['onset', 'duration', 'emotion', 'HED'], - ['7', 'high', 'happy', 'Yellow'], - ['11', 'low', 'sad', 'Blue'], - ['15', 'mad', 'angry', 'Red'], - ['19', 'huh', 'confused', 'Gray'], - ], - }, + 'onset\tduration\temotion\tHED\n' + + '7\thigh\thappy\tYellow\n' + + '11\tlow\tsad\tBlue\n' + + '15\tmad\tangry\tRed\n' + + '19\thuh\tconfused\tGray', { relativePath: '/sub04/sub04_task-test_run-1_events.tsv', path: '/sub04/sub04_task-test_run-1_events.tsv', @@ -516,16 +476,11 @@ describe('BIDS datasets', () => { '/sub04/sub04_task-test_run-2_events.tsv', ['/sub02/sub02_task-test_run-1_events.json'], sidecars[1][0], - { - headers: ['onset', 'duration', 'transport'], - rows: [ - ['onset', 'duration', 'transport'], - ['7', 'wet', 'boat'], - ['11', 'steam', 'train'], - ['15', 'tires', 'car'], - ['19', 'speedy', 'maglev'], - ], - }, + 'onset\tduration\ttransport\n' + + '7\twet\tboat\n' + + '11\tsteam\ttrain\n' + + '15\ttires\tcar\n' + + '19\tspeedy\tmaglev', { relativePath: '/sub04/sub04_task-test_run-2_events.tsv', path: '/sub04/sub04_task-test_run-2_events.tsv', @@ -535,16 +490,11 @@ describe('BIDS datasets', () => { '/sub04/sub04_task-test_run-3_events.tsv', ['/sub01/sub01_task-test_run-2_events.json', '/sub02/sub02_task-test_run-1_events.json'], Object.assign({}, sidecars[0][1], sidecars[1][0]), - { - headers: ['onset', 'duration', 'vehicle', 'transport', 'speed'], - rows: [ - ['onset', 'duration', 'vehicle', 'transport', 'speed'], - ['7', 'ferry', 'train', 'boat', '20'], - ['11', 'autotrain', 'car', 'train', '79'], - ['15', 'towing', 'boat', 'car', '30'], - ['19', 'tugboat', 'boat', 'boat', '5'], - ], - }, + 'onset\tduration\tvehicle\ttransport\tspeed\n' + + '7\tferry\ttrain\tboat\t20\n' + + '11\tautotrain\tcar\ttrain\t79\n' + + '15\ttowing\tboat\tcar\t30\n' + + '19\ttugboat\tboat\tboat\t5', { relativePath: '/sub04/sub04_task-test_run-3_events.tsv', path: '/sub04/sub04_task-test_run-3_events.tsv', @@ -554,13 +504,7 @@ describe('BIDS datasets', () => { '/sub04/sub04_task-test_run-4_events.tsv', ['/sub01/sub01_task-test_run-3_events.json'], sidecars[0][2], - { - headers: ['onset', 'duration', 'age', 'HED'], - rows: [ - ['onset', 'duration', 'age', 'HED'], - ['7', 'ferry', '30', 'Age/30'], - ], - }, + 'onset\tduration\tage\tHED\n' + '7\tferry\t30\tAge/30', { relativePath: '/sub04/sub04_task-test_run-4_events.tsv', path: '/sub04/sub04_task-test_run-4_events.tsv', @@ -570,13 +514,7 @@ describe('BIDS datasets', () => { '/sub04/sub04_task-test_run-5_events.tsv', ['/sub01/sub01_task-test_run-1_events.json'], sidecars[0][0], - { - headers: ['onset', 'duration', 'color'], - rows: [ - ['onset', 'duration', 'color'], - ['7', 'royal', 'purple'], - ], - }, + 'onset\tduration\tcolor\n' + '7\troyal\tpurple', { relativePath: '/sub04/sub04_task-test_run-5_events.tsv', path: '/sub04/sub04_task-test_run-5_events.tsv', @@ -589,13 +527,7 @@ describe('BIDS datasets', () => { '/sub05/sub05_task-test_run-1_events.tsv', ['/sub04/sub04_task-test_run-1_events.json'], sidecars[3][0], - { - headers: ['onset', 'duration', 'test', 'HED'], - rows: [ - ['onset', 'duration', 'test', 'HED'], - ['7', 'something', 'first', 'Event/Duration/55 ms'], - ], - }, + 'onset\tduration\ttest\tHED\n' + '7\tsomething\tfirst\tEvent/Duration/55 ms', { relativePath: '/sub05/sub05_task-test_run-1_events.tsv', path: '/sub05/sub05_task-test_run-1_events.tsv', diff --git a/validator/dataset.js b/validator/dataset.js index 29dbbc26..22536df5 100644 --- a/validator/dataset.js +++ b/validator/dataset.js @@ -113,14 +113,14 @@ export const validateDataset = function (definitions, hedStrings, hedSchemas) { * @param {(string|ParsedHedString)[]} parsedHedStrings The dataset's parsed HED strings. * @param {Schemas} hedSchemas The HED schema container object. * @param {Map} definitions The dataset's parsed definitions. - * @param {boolean} checkForWarnings Whether to check for warnings or only errors. + * @param {Object} settings The configuration settings for validation. * @return {[boolean, Issue[]]} Whether the HED strings are valid and any issues found. */ -export const validateHedEvents = function (parsedHedStrings, hedSchemas, definitions, checkForWarnings) { +export const validateHedEvents = function (parsedHedStrings, hedSchemas, definitions, settings) { let stringsValid = true let stringIssues = [] for (const hedString of parsedHedStrings) { - const [valid, issues] = validateHedEventWithDefinitions(hedString, hedSchemas, definitions, checkForWarnings) + const [valid, issues] = validateHedEventWithDefinitions(hedString, hedSchemas, definitions, settings) stringsValid = stringsValid && valid stringIssues = stringIssues.concat(issues) } @@ -135,15 +135,27 @@ export const validateHedEvents = function (parsedHedStrings, hedSchemas, definit * @param {boolean} checkForWarnings Whether to check for warnings or only errors. * @return {[boolean, Issue[]]} Whether the HED dataset is valid and any issues found. */ -export const validateHedDataset = function (hedStrings, hedSchemas, checkForWarnings = false) { +export const validateHedDataset = function (hedStrings, hedSchemas, ...args) { + let settings + if (args[0] === Object(args[0])) { + settings = { + checkForWarnings: args[0].checkForWarnings ?? false, + validateDatasetLevel: args[0].validateDatasetLevel ?? true, + } + } else { + settings = { + checkForWarnings: args[0] ?? false, + validateDatasetLevel: true, + } + } if (hedStrings.length === 0) { return [true, []] } const [parsedHedStrings, parsingIssues] = parseHedStrings(hedStrings, hedSchemas) const [definitions, definitionIssues] = parseDefinitions(parsedHedStrings) - const [stringsValid, stringIssues] = validateHedEvents(parsedHedStrings, hedSchemas, definitions, checkForWarnings) + const [stringsValid, stringIssues] = validateHedEvents(parsedHedStrings, hedSchemas, definitions, settings) let datasetIssues = [] - if (stringsValid) { + if (stringsValid && settings.validateDatasetLevel) { datasetIssues = validateDataset(definitions, parsedHedStrings, hedSchemas) } const issues = stringIssues.concat(...Object.values(parsingIssues), definitionIssues, datasetIssues) @@ -160,12 +172,19 @@ export const validateHedDataset = function (hedStrings, hedSchemas, checkForWarn * @param {boolean} checkForWarnings Whether to check for warnings or only errors. * @return {[boolean, Issue[]]} Whether the HED dataset is valid and any issues found. */ -export const validateHedDatasetWithContext = function ( - hedStrings, - contextHedStrings, - hedSchemas, - checkForWarnings = false, -) { +export const validateHedDatasetWithContext = function (hedStrings, contextHedStrings, hedSchemas, ...args) { + let settings + if (args[0] === Object(args[0])) { + settings = { + checkForWarnings: args[0].checkForWarnings ?? false, + validateDatasetLevel: args[0].validateDatasetLevel ?? true, + } + } else { + settings = { + checkForWarnings: args[0] ?? false, + validateDatasetLevel: true, + } + } if (hedStrings.length + contextHedStrings.length === 0) { return [true, []] } @@ -173,9 +192,9 @@ export const validateHedDatasetWithContext = function ( const [parsedContextHedStrings, contextParsingIssues] = parseHedStrings(contextHedStrings, hedSchemas) const combinedParsedHedStrings = parsedHedStrings.concat(parsedContextHedStrings) const [definitions, definitionIssues] = parseDefinitions(combinedParsedHedStrings) - const [stringsValid, stringIssues] = validateHedEvents(parsedHedStrings, hedSchemas, definitions, checkForWarnings) + const [stringsValid, stringIssues] = validateHedEvents(parsedHedStrings, hedSchemas, definitions, settings) let datasetIssues = [] - if (stringsValid) { + if (stringsValid && settings.validateDatasetLevel) { datasetIssues = validateDataset(definitions, parsedHedStrings, hedSchemas) } const issues = stringIssues.concat( diff --git a/validator/event/init.js b/validator/event/init.js index 24e9b995..77a9dbef 100644 --- a/validator/event/init.js +++ b/validator/event/init.js @@ -72,6 +72,7 @@ export const validateHedString = function (hedString, hedSchemas, ...args) { settings = { checkForWarnings: args[0] ?? false, expectValuePlaceholderString: args[1] ?? false, + definitionsAllowed: 'yes', } } const [parsedString, parsedStringIssues, hedValidator] = initiallyValidateHedString(hedString, hedSchemas, settings)