The validation context#
The core structure of the validator is the context
,
a namespace that aggregates properties of the dataset (the dataset
variable, above)
and the current file being validated.
Its type can be described as follows:
Context: {
// Dataset properties
dataset: {
dataset_description: object
datatypes: string[]
modalities: string[]
// Lists of subjects as discovered in different locations
subjects: {
sub_dirs: string[]
participant_id: string[]
phenotype: string[]
}
}
// Properties of the current subject
subject: {
// Lists of sessions as discovered in different locations
sessions: {
ses_dirs: string[]
session_id: string[]
phenotype: string[]
}
}
// Path properties
path: string
entities: object
datatype: string
suffix: string
extension: string
// Inferred property
modality: string
// Inheritance principle constructions
sidecar: object
associations: {
// Paths and properties of files associated with the current file
aslcontext: { path: string, n_rows: integer, volume_type: string[] }
...
}
// Content properties
size: integer
// File type-specific content properties
columns: object
gzip: object
json: object
nifti_header: object
ome: object
tiff: object
}
To take an example, in a minimal dataset containing only a single subject’s T1-weighted image,
the context
for that image might be:
dataset:
dataset_description:
Name: "Example dataset"
BIDSVersion: "1.10.0"
DatasetType: "raw"
datatypes: ["anat"]
modalities: ["mri"]
subjects:
sub_dirs: ["sub-01"]
participant_id: null
phenotype: null
subject:
sessions: { ses_dirs: null, session_id: null, phenotype: null }
path: "/sub-01/anat/sub-01_T1w.nii.gz"
entities:
subject: "01"
datatype: "anat"
suffix: "T1w"
extension: ".nii.gz"
modality: "mri"
sidecar:
MagneticFieldStrength: 3
...
associations: {}
size: 22017017
nifti_header:
dim: 3
voxel_sizes: [1, 1, 1]
...
Fields from this context can be queried using object dot notation.
For example, sidecar.MagneticFieldStrengh
has the integer value 3
,
and entities.subject
has the string value "01"
.
This permits the use of boolean expressions, such as
sidecar.RepetitionTime == nifti_header.pixdim[4]
.
As the validator validates each file in turn, it constructs a new context.
The dataset
property remains constant,
while a new subject
property is constructed when inspecting a new subject directory,
and the remaining properties are constructed for each file, individually.
Context definition#
The validation context is largely dictated by the schema, and the full type generated from the schema definition can be found in jsr:@bids/schema/context.
Context construction#
The construction of a validation context is where BIDS concepts are implemented. Again, this is easiest to explain with pseudocode:
def buildFileContext(dataset, file):
context = namespace()
context.dataset = dataset
context.path = file.path
context.size = file.size
fileParts = parsePath(file.path)
context.entities = fileParts.entities
context.datatype = fileParts.datatype
context.suffix = fileParts.suffix
context.extension = fileParts.extension
context.subject = buildSubjectContext(dataset, context.entities.subject)
context.sidecar = loadSidecar(file)
context.associations = namespace({
association: loadAssociation(file, association)
for association in associationTypes(file)
})
if isTSV(file):
context.columns = loadColumns(file)
if isNIfTI(file):
context.nifti_header = loadNiftiHeader(file)
... # And so on
return context
The heavy lifting is done in parsePath
, loadSidecar
and loadAssociation
.
parsePath
is relatively simple, but loadSidecar
and loadAssociation
implement the BIDS Inheritance Principle.