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.MagneticFieldStrength 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.