Skip to content

Commit

Permalink
Merge pull request #1797 from effigies/enh/associations
Browse files Browse the repository at this point in the history
ENH: Finish populating associations
  • Loading branch information
rwblair authored Sep 20, 2023
2 parents 2d08b5a + 4647010 commit ada255e
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 19 deletions.
29 changes: 29 additions & 0 deletions bids-validator/src/files/dwi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* DWI
* Module for parsing DWI-associated files
*/
const normalizeEOL = (str: string): string =>
str.replace(/\r\n/g, '\n').replace(/\r/g, '\n')

export function parseBval(contents: string): number[][] {
// BVAL files are a single row of numbers, and may contain
// trailing whitespace
return [contents
.split(/\s+/)
.filter((x) => x !== '')
.map((x) => Number(x))]
}

export function parseBvec(contents: string): number[][] {
// BVEC files are a matrix of numbers, with each row being
// a different axis
return normalizeEOL(contents)
.split(/\s*\n/)
.filter((x) => x !== '')
.map((row) =>
row
.split(/\s+/)
.filter((x) => x !== '')
.map((x) => Number(x)),
)
}
80 changes: 61 additions & 19 deletions bids-validator/src/schema/associations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FileTree } from '../types/filetree.ts'
import { BIDSContext } from './context.ts'
import { readEntities } from './entities.ts'
import { parseTSV } from '../files/tsv.ts'
import { parseBval, parseBvec } from '../files/dwi.ts'

// type AssociationsLookup = Record<keyof ContextAssociations, { extensions: string[], inherit: boolean, load: ... }

Expand All @@ -23,63 +24,104 @@ import { parseTSV } from '../files/tsv.ts'
*/
const associationLookup = {
events: {
suffix: 'events',
extensions: ['.tsv'],
inherit: true,
load: (file: BIDSFile): Promise<ContextAssociations['events']> => {
return file
.text()
.then((text) => parseTSV(text) as ContextAssociationsEvents)
load: async (file: BIDSFile): Promise<ContextAssociations['events']> => {
const text = await file.text()
const columns = parseTSV(text)
return {
path: file.path,
onset: columns.get('onset') || [],
}
},
},
aslcontext: {
suffix: 'aslcontext',
extensions: ['.tsv'],
inherit: true,
load: (file: BIDSFile): Promise<ContextAssociations['aslcontext']> => {
return Promise.resolve({ path: file.path, n_rows: 0, volume_type: [] })
load: async (
file: BIDSFile,
): Promise<ContextAssociations['aslcontext']> => {
const contents = await file.text()
const columns = parseTSV(contents)
return {
path: file.path,
n_rows: columns.get('volume_type')?.length || 0,
volume_type: columns.get('volume_type') || [],
}
},
},
m0scan: {
suffix: 'm0scan',
extensions: ['.nii', '.nii.gz'],
inherit: false,
load: (file: BIDSFile): Promise<ContextAssociations['m0scan']> => {
return Promise.resolve({ path: file.path })
},
},
magnitude: {
suffix: 'magnitude',
extensions: ['.nii', '.nii.gz'],
inherit: false,
load: (file: BIDSFile): Promise<ContextAssociations['magnitude']> => {
return Promise.resolve({ path: file.path, onset: 'silly' })
return Promise.resolve({ path: file.path })
},
},
magnitude1: {
suffix: 'magnitude1',
extensions: ['.nii', '.nii.gz'],
inherit: false,
load: (file: BIDSFile): Promise<ContextAssociations['magnitude1']> => {
return Promise.resolve({ path: file.path })
},
},
bval: {
extensions: ['.nii', '.nii.gz'],
suffix: 'dwi',
extensions: ['.bval'],
inherit: true,
load: (file: BIDSFile): Promise<ContextAssociations['bval']> => {
return Promise.resolve({ path: file.path, n_cols: 0 })
load: async (file: BIDSFile): Promise<ContextAssociations['bval']> => {
const contents = await file.text()
const columns = parseBval(contents)
return {
path: file.path,
n_cols: columns ? columns[0].length : 0,
}
},
},
bvec: {
extensions: ['.nii', '.nii.gz'],
suffix: 'dwi',
extensions: ['.bvec'],
inherit: true,
load: (file: BIDSFile): Promise<ContextAssociations['bvec']> => {
return Promise.resolve({ path: file.path, n_cols: 0 })
load: async (file: BIDSFile): Promise<ContextAssociations['bvec']> => {
const contents = await file.text()
const columns = parseBvec(contents)
return {
path: file.path,
n_cols: columns ? columns[0].length : 0,
}
},
},
channels: {
suffix: 'channels',
extensions: ['.tsv'],
inherit: true,
load: (file: BIDSFile): Promise<ContextAssociations['events']> => {
return file
.text()
.then((text) => parseTSV(text) as ContextAssociationsEvents)
load: async (file: BIDSFile): Promise<ContextAssociations['channels']> => {
const contents = await file.text()
const columns = parseTSV(contents)
return {
path: file.path,
type: columns.get('type') || [],
short_channel: columns.get('short_channel') || [],
}
},
},
coordsystem: {
suffix: 'coordsystem',
extensions: ['.json'],
inherit: true,
load: (file: BIDSFile): Promise<ContextAssociations['coordsystem']> => {
return Promise.resolve({ path: file.path })
},
},
}
Expand All @@ -90,9 +132,9 @@ export async function buildAssociations(
): Promise<ContextAssociations> {
const associations: ContextAssociations = {}
for (const key in associationLookup as typeof associationLookup) {
const { extensions, inherit } =
const { suffix, extensions, inherit } =
associationLookup[key as keyof typeof associationLookup]
const paths = getPaths(fileTree, source, key, extensions)
const paths = getPaths(fileTree, source, suffix, extensions)
if (paths.length === 0) {
continue
}
Expand Down
25 changes: 25 additions & 0 deletions bids-validator/src/schema/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import {
ContextSubject,
ContextAssociations,
ContextNiftiHeader,
ContextData,
} from '../types/context.ts'
import { BIDSFile } from '../types/file.ts'
import { FileTree } from '../types/filetree.ts'
import { ColumnsMap } from '../types/columns.ts'
import { BIDSEntities, readEntities } from './entities.ts'
import { DatasetIssues } from '../issues/datasetIssues.ts'
import { parseTSV } from '../files/tsv.ts'
import { parseBval, parseBvec } from '../files/dwi.ts'
import { loadHeader } from '../files/nifti.ts'
import { buildAssociations } from './associations.ts'
import { ValidatorOptions } from '../setup/options.ts'
Expand Down Expand Up @@ -66,6 +68,7 @@ export class BIDSContext implements Context {
columns: ColumnsMap
associations: ContextAssociations
nifti_header?: ContextNiftiHeader
data?: ContextData

constructor(
fileTree: FileTree,
Expand Down Expand Up @@ -171,6 +174,27 @@ export class BIDSContext implements Context {
}
}

// Currently un-specified bit of context needed for bval/bvec
async loadData(): Promise<void> {
let parser
if (this.file.path.endsWith('.bval')) {
parser = parseBval
} else if (this.file.path.endsWith('.bvec')) {
parser = parseBvec
}
if (parser) {
this.data = await this.file
.text()
.then(parser as (value: string) => number[][])
.then((data) => {
return {
n_rows: data.length,
n_cols: data ? data[0].length : 0,
}
}).then((ret) => {console.log(ret); return ret})
}
}

async loadColumns(): Promise<void> {
if (this.extension !== '.tsv') {
return
Expand Down Expand Up @@ -198,6 +222,7 @@ export class BIDSContext implements Context {
this.loadSidecar(),
this.loadColumns(),
this.loadAssociations(),
this.loadData(),
])
this.loadNiftiHeader()
}
Expand Down
15 changes: 15 additions & 0 deletions bids-validator/src/types/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ export interface ContextAssociationsBvec {
path: string
n_cols: number
}
export interface ContextAssociationsChannels {
path?: string
type?: string[]
short_channel?: string[]
}
export interface ContextAssociationsCoordsystem {
path: string
}
export interface ContextAssociations {
events?: ContextAssociationsEvents
aslcontext?: ContextAssociationsAslcontext
Expand All @@ -56,6 +64,8 @@ export interface ContextAssociations {
magnitude1?: ContextAssociationsMagnitude1
bval?: ContextAssociationsBval
bvec?: ContextAssociationsBvec
channels?: ContextAssociationsChannels
coordsystem?: ContextAssociationsCoordsystem
}
export interface ContextNiftiHeaderDimInfo {
freq: number
Expand All @@ -74,6 +84,10 @@ export interface ContextNiftiHeader {
qform_code: number
sform_code: number
}
export interface ContextData {
n_cols?: number
n_rows?: number
}
export interface Context {
dataset: ContextDataset
subject: ContextSubject
Expand All @@ -88,4 +102,5 @@ export interface Context {
columns: object
json: object
nifti_header?: ContextNiftiHeader
data?: ContextData
}

0 comments on commit ada255e

Please sign in to comment.