1- import type {
2- Aslcontext ,
3- Associations ,
4- Bval ,
5- Bvec ,
6- Channels ,
7- Coordsystem ,
8- Events ,
9- M0Scan ,
10- Magnitude ,
11- Magnitude1 ,
12- } from '@bids/schema/context'
1+ import type { Aslcontext , Associations , Bval , Bvec , Channels , Events } from '@bids/schema/context'
132import type { Schema as MetaSchema } from '@bids/schema/metaschema'
143
15- import type { BIDSFile , FileTree } from '../types/filetree.ts'
4+ import type { BIDSFile } from '../types/filetree.ts'
165import type { BIDSContext } from './context.ts'
17- import type { DatasetIssues } from '../issues/datasetIssues.ts'
18- import type { readEntities } from './entities.ts'
196import { loadTSV } from '../files/tsv.ts'
207import { parseBvalBvec } from '../files/dwi.ts'
218import { walkBack } from '../files/inheritance.ts'
229import { evalCheck } from './applyRules.ts'
2310import { expressionFunctions } from './expressionLanguage.ts'
2411
25- // type AssociationsLookup = Record<keyof ContextAssociations, { extensions: string[], inherit: boolean, load: ... }
12+ function defaultAssociation ( file : BIDSFile , _options : any ) : Promise < { path : string } > {
13+ return Promise . resolve ( { path : file . path } )
14+ }
2615
2716/**
28- * This object describes associated files for data files in a bids dataset
29- * For any given datafile we iterate over every key/value in this object.
30- * For each entry we see if any files in the datafiles directory have:
31- * - a suffix that matches the key
32- * - an extension in the entry's extension array.
33- * - that all the files entities and their values match those of the datafile
34- * If the entry allows for inheritance we recurse up the filetree looking for other applicable files.
35- * The load functions are incomplete, some associations need to read data from a file so they're
36- * returning promises for now.
17+ * This object describes lookup functions for files associated to data files in a bids dataset.
18+ * For any given data file we iterate over the associations defined schema.meta.associations.
19+ * If the selectors match the data file, we attempt to find an associated file,
20+ * and use the given function to load the data from that file.
21+ *
22+ * Many associations only consist of a path; this object is for more complex associations.
3723 */
3824const associationLookup = {
3925 events : async ( file : BIDSFile , options : { maxRows : number } ) : Promise < Events > => {
@@ -60,15 +46,6 @@ const associationLookup = {
6046 volume_type : columns . get ( 'volume_type' ) || [ ] ,
6147 }
6248 } ,
63- m0scan : ( file : BIDSFile , options : any ) : Promise < M0Scan > => {
64- return Promise . resolve ( { path : file . path } )
65- } ,
66- magnitude : ( file : BIDSFile , options : any ) : Promise < Magnitude > => {
67- return Promise . resolve ( { path : file . path } )
68- } ,
69- magnitude1 : ( file : BIDSFile , options : any ) : Promise < Magnitude1 > => {
70- return Promise . resolve ( { path : file . path } )
71- } ,
7249 bval : async ( file : BIDSFile , options : any ) : Promise < Bval > => {
7350 const contents = await file . text ( )
7451 const rows = parseBvalBvec ( contents )
@@ -106,9 +83,6 @@ const associationLookup = {
10683 sampling_frequency : columns . get ( 'sampling_frequency' ) ,
10784 }
10885 } ,
109- coordsystem : ( file : BIDSFile , options : any ) : Promise < Coordsystem > => {
110- return Promise . resolve ( { path : file . path } )
111- } ,
11286}
11387
11488export async function buildAssociations (
@@ -117,24 +91,39 @@ export async function buildAssociations(
11791 const associations : Associations = { }
11892
11993 const schema : MetaSchema = context . dataset . schema as MetaSchema
94+ // Augment rule type with an entities field that should be present in BIDS 1.10.1+
95+ type ruleType = MetaSchema [ 'meta' ] [ 'associations' ] [ keyof MetaSchema [ 'meta' ] [ 'associations' ] ]
96+ type AugmentedRuleType = ruleType & {
97+ target : ruleType [ 'target' ] & { entities ?: string [ ] }
98+ }
12099
121100 Object . assign ( context , expressionFunctions )
122101 // @ts -expect-error
123102 context . exists . bind ( context )
124103
125- for ( const [ key , rule ] of Object . entries ( schema . meta . associations ) ) {
104+ for ( const key of Object . keys ( schema . meta . associations ) ) {
105+ const rule = schema . meta . associations [ key ] as AugmentedRuleType
126106 if ( ! rule . selectors ! . every ( ( x ) => evalCheck ( x , context ) ) ) {
127107 continue
128108 }
129- let file
109+ let file : BIDSFile | BIDSFile [ ]
130110 let extension : string [ ] = [ ]
131111 if ( typeof rule . target . extension === 'string' ) {
132112 extension = [ rule . target . extension ]
133113 } else if ( Array . isArray ( rule . target . extension ) ) {
134114 extension = rule . target . extension
135115 }
136116 try {
137- file = walkBack ( context . file , rule . inherit , extension , rule . target . suffix ) . next ( ) . value
117+ file = walkBack (
118+ context . file ,
119+ rule . inherit ,
120+ extension ,
121+ rule . target . suffix ,
122+ rule . target ?. entities ?? [ ] ,
123+ ) . next ( ) . value
124+ if ( Array . isArray ( file ) ) {
125+ file = file [ 0 ]
126+ }
138127 } catch ( error ) {
139128 if (
140129 error && typeof error === 'object' && 'code' in error &&
@@ -150,7 +139,7 @@ export async function buildAssociations(
150139
151140 if ( file ) {
152141 // @ts -expect-error
153- const load = associationLookup [ key ]
142+ const load = associationLookup [ key ] ?? defaultAssociation
154143 // @ts -expect-error
155144 associations [ key ] = await load ( file , { maxRows : context . dataset . options ?. maxRows } ) . catch (
156145 ( error : any ) => {
0 commit comments