Skip to content

Commit eaced0d

Browse files
authored
Merge pull request #268 from drammock/feat/add-keys-to-assoc-coordsys
BEP042 patches
2 parents fe09d89 + 5cb3dc4 commit eaced0d

File tree

2 files changed

+97
-22
lines changed

2 files changed

+97
-22
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!--
2+
A new scriv changelog fragment.
3+
4+
Uncomment the section that is right (remove the HTML comment wrapper).
5+
For top level release notes, leave all the headers commented out.
6+
-->
7+
8+
9+
### Added
10+
11+
- Implement `associations.coordsystems` to collate multiple `coordsystem.json` files,
12+
as required by BEP 042 (EMG).
13+
14+
<!--
15+
### Changed
16+
17+
- A bullet item for the Changed category.
18+
19+
-->
20+
<!--
21+
### Fixed
22+
23+
- A bullet item for the Fixed category.
24+
25+
-->
26+
<!--
27+
### Deprecated
28+
29+
- A bullet item for the Deprecated category.
30+
31+
-->
32+
<!--
33+
### Removed
34+
35+
- A bullet item for the Removed category.
36+
37+
-->
38+
<!--
39+
### Security
40+
41+
- A bullet item for the Security category.
42+
43+
-->
44+
<!--
45+
### Infrastructure
46+
47+
- A bullet item for the Infrastructure category.
48+
49+
-->

src/schema/associations.ts

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@ import type { Schema as MetaSchema } from '@bids/schema/metaschema'
33

44
import type { BIDSFile } from '../types/filetree.ts'
55
import type { BIDSContext } from './context.ts'
6+
import { loadJSON } from '../files/json.ts'
67
import { loadTSV } from '../files/tsv.ts'
78
import { parseBvalBvec } from '../files/dwi.ts'
89
import { readSidecars, walkBack } from '../files/inheritance.ts'
910
import { evalCheck } from './applyRules.ts'
1011
import { expressionFunctions } from './expressionLanguage.ts'
12+
import { readEntities } from './entities.ts'
1113

1214
import { readText } from '../files/access.ts'
1315

1416
interface WithSidecar {
1517
sidecar: Record<string, unknown>
1618
}
19+
type LoadFunction = (file: BIDSFile, options: any) => Promise<any>
20+
type MultiLoadFunction = (files: BIDSFile[], options: any) => Promise<any>
1721

1822
function defaultAssociation(file: BIDSFile, _options: any): Promise<{ path: string }> {
1923
return Promise.resolve({ path: file.path })
@@ -33,7 +37,7 @@ async function constructSidecar(file: BIDSFile): Promise<Record<string, unknown>
3337
*
3438
* Many associations only consist of a path; this object is for more complex associations.
3539
*/
36-
const associationLookup = {
40+
const associationLookup: Record<string, LoadFunction> = {
3741
events: async (file: BIDSFile, options: { maxRows: number }): Promise<Events & WithSidecar> => {
3842
const columns = await loadTSV(file, options.maxRows)
3943
.catch((e) => {
@@ -103,6 +107,24 @@ const associationLookup = {
103107
}
104108
},
105109
}
110+
const multiAssociationLookup: Record<string, MultiLoadFunction> = {
111+
coordsystems: async (
112+
files: BIDSFile[],
113+
options: any,
114+
): Promise<{ paths: string[]; spaces: string[]; ParentCoordinateSystems: string[] }> => {
115+
const jsons = await Promise.allSettled(
116+
files.map((f) => loadJSON(f).catch(() => ({} as Record<string, unknown>))),
117+
)
118+
const parents = jsons.map((j) =>
119+
j.status === 'fulfilled' ? j.value?.ParentCoordinateSystem : undefined
120+
).filter((p) => p) as string[]
121+
return {
122+
paths: files.map((f) => f.path),
123+
spaces: files.map((f) => readEntities(f.name).entities?.space),
124+
ParentCoordinateSystems: parents,
125+
}
126+
},
127+
}
106128

107129
export async function buildAssociations(
108130
context: BIDSContext,
@@ -134,33 +156,37 @@ export async function buildAssociations(
134156
rule.target.suffix,
135157
rule.target?.entities ?? [],
136158
).next().value
137-
if (Array.isArray(file)) {
138-
file = file[0]
139-
}
140-
} catch (error) {
141-
if (
142-
error && typeof error === 'object' && 'code' in error &&
143-
error.code === 'MULTIPLE_INHERITABLE_FILES'
144-
) {
145-
// @ts-expect-error
159+
} catch (error: any) {
160+
if (error?.code === 'MULTIPLE_INHERITABLE_FILES') {
146161
context.dataset.issues.add(error)
147-
break
162+
continue
148163
} else {
149164
throw error
150165
}
151166
}
152167

153-
if (file) {
154-
// @ts-expect-error
155-
const load = associationLookup[key] ?? defaultAssociation
156-
// @ts-expect-error
157-
associations[key] = await load(file, { maxRows: context.dataset.options?.maxRows }).catch(
158-
(error: any) => {
159-
if (error.code) {
160-
context.dataset.issues.add({ ...error, location: file.path })
161-
}
162-
},
163-
)
168+
if (file && !(Array.isArray(file) && file.length === 0)) {
169+
const options = { maxRows: context.dataset.options?.maxRows }
170+
if (key in multiAssociationLookup) {
171+
const load = multiAssociationLookup[key]
172+
if (!Array.isArray(file)) {
173+
file = [file]
174+
}
175+
associations[key as keyof Associations] = await load(file, options).catch((e: any) => {})
176+
} else {
177+
const load = associationLookup[key] ?? defaultAssociation
178+
if (Array.isArray(file)) {
179+
file = file[0]
180+
}
181+
const location = file.path
182+
associations[key as keyof Associations] = await load(file, options).catch(
183+
(error: any) => {
184+
if (error.code) {
185+
context.dataset.issues.add({ ...error, location })
186+
}
187+
},
188+
)
189+
}
164190
}
165191
}
166192
return Promise.resolve(associations)

0 commit comments

Comments
 (0)