diff --git a/.github/workflows/deno_tests.yml b/.github/workflows/deno_tests.yml index 9e0f5f61..435c9ec4 100644 --- a/.github/workflows/deno_tests.yml +++ b/.github/workflows/deno_tests.yml @@ -12,6 +12,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + BIDS_SCHEMA: https://bids-specification--1998.org.readthedocs.build/en/1998/schema.json + jobs: debug_info: runs-on: ubuntu-latest @@ -52,7 +55,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - allow-net: [true, false] + allow-net: [true] # , false] fail-fast: false defaults: run: diff --git a/changelog.d/20250919_172910_dan_add_keys_to_assoc_coordsys.md b/changelog.d/20250919_172910_dan_add_keys_to_assoc_coordsys.md new file mode 100644 index 00000000..3c9307fa --- /dev/null +++ b/changelog.d/20250919_172910_dan_add_keys_to_assoc_coordsys.md @@ -0,0 +1,49 @@ + + + +### Added + +- Implement `associations.coordsystems` to collate multiple `coordsystem.json` files, + as required by BEP 042 (EMG). + + + + + + + diff --git a/src/schema/associations.ts b/src/schema/associations.ts index 6849f6d8..8ef29934 100644 --- a/src/schema/associations.ts +++ b/src/schema/associations.ts @@ -3,14 +3,19 @@ import type { Schema as MetaSchema } from '@bids/schema/metaschema' import type { BIDSFile } from '../types/filetree.ts' import type { BIDSContext } from './context.ts' +import { loadJSON } from '../files/json.ts' import { loadTSV } from '../files/tsv.ts' import { parseBvalBvec } from '../files/dwi.ts' import { walkBack } from '../files/inheritance.ts' import { evalCheck } from './applyRules.ts' import { expressionFunctions } from './expressionLanguage.ts' +import { readEntities } from './entities.ts' import { readText } from '../files/access.ts' +type LoadFunction = (file: BIDSFile, options: any) => Promise +type MultiLoadFunction = (files: BIDSFile[], options: any) => Promise + function defaultAssociation(file: BIDSFile, _options: any): Promise<{ path: string }> { return Promise.resolve({ path: file.path }) } @@ -23,7 +28,7 @@ function defaultAssociation(file: BIDSFile, _options: any): Promise<{ path: stri * * Many associations only consist of a path; this object is for more complex associations. */ -const associationLookup = { +const associationLookup: Record = { events: async (file: BIDSFile, options: { maxRows: number }): Promise => { const columns = await loadTSV(file, options.maxRows) .catch((e) => { @@ -86,6 +91,24 @@ const associationLookup = { } }, } +const multiAssociationLookup: Record = { + coordsystems: async ( + files: BIDSFile[], + options: any, + ): Promise<{ paths: string[]; spaces: string[]; ParentCoordinateSystems: string[] }> => { + const jsons = await Promise.allSettled( + files.map((f) => loadJSON(f).catch(() => ({} as Record))), + ) + const parents = jsons.map((j) => + j.status === 'fulfilled' ? j.value?.ParentCoordinateSystem : undefined + ).filter((p) => p) as string[] + return { + paths: files.map((f) => f.path), + spaces: files.map((f) => readEntities(f.name).entities?.space), + ParentCoordinateSystems: parents, + } + }, +} export async function buildAssociations( context: BIDSContext, @@ -123,33 +146,37 @@ export async function buildAssociations( rule.target.suffix, rule.target?.entities ?? [], ).next().value - if (Array.isArray(file)) { - file = file[0] - } - } catch (error) { - if ( - error && typeof error === 'object' && 'code' in error && - error.code === 'MULTIPLE_INHERITABLE_FILES' - ) { - // @ts-expect-error + } catch (error: any) { + if (error?.code === 'MULTIPLE_INHERITABLE_FILES') { context.dataset.issues.add(error) - break + continue } else { throw error } } - if (file) { - // @ts-expect-error - const load = associationLookup[key] ?? defaultAssociation - // @ts-expect-error - associations[key] = await load(file, { maxRows: context.dataset.options?.maxRows }).catch( - (error: any) => { - if (error.code) { - context.dataset.issues.add({ ...error, location: file.path }) - } - }, - ) + if (file && !(Array.isArray(file) && file.length === 0)) { + const options = { maxRows: context.dataset.options?.maxRows } + if (key in multiAssociationLookup) { + const load = multiAssociationLookup[key] + if (!Array.isArray(file)) { + file = [file] + } + associations[key as keyof Associations] = await load(file, options).catch((e: any) => {}) + } else { + const load = associationLookup[key] ?? defaultAssociation + if (Array.isArray(file)) { + file = file[0] + } + const location = file.path + associations[key as keyof Associations] = await load(file, options).catch( + (error: any) => { + if (error.code) { + context.dataset.issues.add({ ...error, location }) + } + }, + ) + } } } return Promise.resolve(associations) diff --git a/tests/data/bids-examples b/tests/data/bids-examples index bd8a9510..e19fcf1a 160000 --- a/tests/data/bids-examples +++ b/tests/data/bids-examples @@ -1 +1 @@ -Subproject commit bd8a95100a66e0b57498fda905da3f048d76f608 +Subproject commit e19fcf1ae2e53565fb81925be309b2f21284d249