From 20c54567b7411eae7bac001fce56776287764484 Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Thu, 12 Feb 2026 14:57:42 +1000 Subject: [PATCH 1/7] Replace nipreps.json with schema-driven config generation Entity definitions are now generated from the BIDS schema via pybids' generate_extended_config(). NiPreps-specific entities (hash, fmapid, pvc) and the figures datatype are layered on via ConfigExtension. Path patterns are defined inline as they all use NiPreps-specific entities and suffixes. The reports module now receives the generated config dict directly via add_config_paths(). nipreps.json is no longer needed and has been removed. --- niworkflows/data/nipreps.json | 211 --------------------------------- niworkflows/interfaces/bids.py | 108 ++++++++++++++++- niworkflows/reports/core.py | 3 +- 3 files changed, 109 insertions(+), 213 deletions(-) delete mode 100644 niworkflows/data/nipreps.json diff --git a/niworkflows/data/nipreps.json b/niworkflows/data/nipreps.json deleted file mode 100644 index 56b86770e5a..00000000000 --- a/niworkflows/data/nipreps.json +++ /dev/null @@ -1,211 +0,0 @@ -{ - "name": "nipreps", - "entities": [ - { - "name": "subject", - "pattern": "[/\\\\]+sub-([a-zA-Z0-9+]+)", - "directory": "{subject}" - }, - { - "name": "session", - "pattern": "[_/\\\\]+ses-([a-zA-Z0-9+]+)", - "mandatory": false, - "directory": "{subject}{session}" - }, - { - "name": "task", - "pattern": "[_/\\\\]+task-([a-zA-Z0-9+]+)" - }, - { - "name": "acquisition", - "pattern": "[_/\\\\]+acq-([a-zA-Z0-9+]+)" - }, - { - "name": "ceagent", - "pattern": "[_/\\\\]+ce-([a-zA-Z0-9+]+)" - }, - { - "name": "reconstruction", - "pattern": "[_/\\\\]+rec-([a-zA-Z0-9+]+)" - }, - { - "name": "tracer", - "pattern": "[_/\\\\]+trc-([a-zA-Z0-9+]+)" - }, - { - "name": "direction", - "pattern": "[_/\\\\]+dir-([a-zA-Z0-9+]+)" - }, - { - "name": "run", - "pattern": "[_/\\\\]+run-(\\d+)", - "dtype": "int" - }, - { - "name": "proc", - "pattern": "[_/\\\\]+proc-([a-zA-Z0-9+]+)" - }, - { - "name": "pvc", - "pattern": "[_/\\\\]+pvc-([a-zA-Z0-9+]+)" - }, - { - "name": "modality", - "pattern": "[_/\\\\]+mod-([a-zA-Z0-9+]+)" - }, - { - "name": "echo", - "pattern": "[_/\\\\]+echo-([0-9]+)" - }, - { - "name": "flip", - "pattern": "[_/\\\\]+flip-([0-9]+)" - }, - { - "name": "inv", - "pattern": "[_/\\\\]+inv-([0-9]+)" - }, - { - "name": "mt", - "pattern": "[_/\\\\]+mt-(on|off)" - }, - { - "name": "part", - "pattern": "[_/\\\\]+part-(mag|phase|real|imag)" - }, - { - "name": "recording", - "pattern": "[_/\\\\]+recording-([a-zA-Z0-9+]+)" - }, - { - "name": "space", - "pattern": "[_/\\\\]+space-([a-zA-Z0-9+]+)" - }, - { - "name": "suffix", - "pattern": "[._]*([a-zA-Z0-9]*?)\\.[^/\\\\]+$" - }, - { - "name": "scans", - "pattern": "(.*\\_scans.tsv)$" - }, - { - "name": "fmap", - "pattern": "(phasediff|magnitude[1-2]|phase[1-2]|fieldmap|epi)\\.nii" - }, - { - "name": "datatype", - "pattern": "[/\\\\]+(func|anat|pet|fmap|dwi|perf|meg|eeg|figures)[/\\\\]+" - }, - { - "name": "extension", - "pattern": "[._]*[a-zA-Z0-9]*?(\\.[^/\\\\]+)$" - }, - { - "name": "atlas", - "pattern": "[_/\\\\]+atlas-([a-zA-Z0-9+]+)" - }, - { - "name": "roi", - "pattern": "[_/\\\\]+roi-([a-zA-Z0-9+]+)" - }, - { - "name": "label", - "pattern": "[_/\\\\]+label-([a-zA-Z0-9+]+)" - }, - { - "name": "fmapid", - "pattern": "[_/\\\\]+fmapid-([a-zA-Z0-9+]+)" - }, - { - "name": "desc", - "pattern": "[_/\\\\]+desc-([a-zA-Z0-9+]+)" - }, - { - "name": "from", - "pattern": "(?:^|_)from-([a-zA-Z0-9+]+).*xfm" - }, - { - "name": "to", - "pattern": "(?:^|_)to-([a-zA-Z0-9+]+).*xfm" - }, - { - "name": "mode", - "pattern": "(?:^|_)mode-(image|points).*xfm" - }, - { - "name": "hemi", - "pattern": "hemi-(L|R)" - }, - { - "name": "model", - "pattern": "model-([a-zA-Z0-9+]+)" - }, - { - "name": "subset", - "pattern": "subset-([a-zA-Z0-9+]+)" - }, - { - "name": "resolution", - "pattern": "res-([a-zA-Z0-9+]+)" - }, - { - "name": "density", - "pattern": "res-([a-zA-Z0-9+]+)" - }, - { - "name": "cohort", - "pattern": "[_/\\\\]+cohort-0*(\\d+)", - "dtype": "int" - }, - { - "name": "hash", - "pattern": "hash-([a-zA-Z0-9+]+)" - } - ], - "default_path_patterns": [ - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_label-{label}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_label-{label}[_desc-{desc}]_{suffix|probseg}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_label-{label}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}]_desc-{desc}_{suffix|mask}{extension<.label.gii|.json>|.label.gii}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|AROMAnoiseICs}{extension<.csv|.tsv>|.csv}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|components}{extension<.json|.tsv|.nii|.nii.gz>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|decomposition}{extension<.json>|.json}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_hemi-{hemi}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dtseries.nii|.dtseries.json|.func.gii|.func.json>}", - "sub-{subject}[/ses-{session}]/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.json|.nii.gz|.nii>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_desc-{desc}_{suffix}{extension<.json|.nii.gz|.nii>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.tsv|.bval|.bvec|.b>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_{suffix}{extension<.tsv|.json>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.json|.tsv>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json|.tsv>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|fmap}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|fmap}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_fmapid-{fmapid}][_label-{label}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_pvc-{pvc}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_desc-{desc}_{suffix|mask}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}]_label-{label}[_desc-{desc}]_{suffix|probseg}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_label-{label}][_pvc-{pvc}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}][_pvc-{pvc}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dtseries.nii|.dtseries.json|.func.gii|.func.json>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_res-{resolution}]_desc-{desc}_{suffix}{extension<.tsv|.json>|.tsv}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}" - ] -} diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index b01841fe823..36bf381efb7 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -57,7 +57,113 @@ from ..utils.misc import _copy_any, unlink regz = re.compile(r'\.gz$') -_pybids_spec = loads(data.load.readable('nipreps.json').read_text()) + +# Generate the pybids config from the BIDS schema with NiPreps extensions. +# Standard derivative patterns come from the schema (with hash entity injected); +# only patterns with non-schema suffixes or NiPreps-specific entities are listed +# explicitly below. +from bids.layout.config_gen import ConfigExtension, generate_extended_config + +# NiPreps-specific path patterns that cannot be generated from the BIDS schema. +# Schema-covered patterns (mask, probseg, dseg, phase, fieldmap, T2starmap, dwi +# sidecars) come from rule_groups=["deriv"] with hash injection; only patterns +# with non-schema suffixes, figures datatype, fmapid/pvc entities, or +# from/to/mode transforms need to be listed here. +_NIPREPS_PATH_PATTERNS = [ + # --- anat: non-schema suffixes --- + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_label-{label}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", + "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}]_desc-{desc}_{suffix|mask}{extension<.label.gii|.json>|.label.gii}", + # --- func: non-schema suffixes --- + "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", + "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|AROMAnoiseICs}{extension<.csv|.tsv>|.csv}", + "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", + "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|components}{extension<.json|.tsv|.nii|.nii.gz>|.tsv}", + "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|decomposition}{extension<.json>|.json}", + "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_hemi-{hemi}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dtseries.nii|.dtseries.json|.func.gii|.func.json>}", + # --- dwi: non-schema suffixes --- + "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.json|.nii.gz|.nii>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", + # --- perf: non-schema suffixes --- + "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_{suffix}{extension<.tsv|.json>|.tsv}", + "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", + "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.json|.tsv>|.tsv}", + "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json|.tsv>|.tsv}", + # --- fmap: fmapid entity --- + "sub-{subject}[/ses-{session}]/{datatype|fmap}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|fmap}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_fmapid-{fmapid}][_label-{label}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", + # --- pet: pvc entity and non-schema suffixes --- + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_pvc-{pvc}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_label-{label}][_pvc-{pvc}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}][_pvc-{pvc}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dtseries.nii|.dtseries.json|.func.gii|.func.json>}", + "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_res-{resolution}]_desc-{desc}_{suffix}{extension<.tsv|.json>|.tsv}", + # --- figures --- + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", +] + +_nipreps_extension = ConfigExtension( + name="nipreps", + extra_entities=[ + { + "name": "hash", + "pattern": "hash-([a-zA-Z0-9+]+)", + "position": "after:session", + }, + { + "name": "fmapid", + "pattern": "[_/\\\\]+fmapid-([a-zA-Z0-9+]+)", + "position": "after:label", + }, + { + "name": "pvc", + "pattern": "[_/\\\\]+pvc-([a-zA-Z0-9+]+)", + "position": "after:tracer", + }, + { + "name": "from", + "pattern": "(?:^|_)from-([a-zA-Z0-9+]+).*xfm", + }, + { + "name": "to", + "pattern": "(?:^|_)to-([a-zA-Z0-9+]+).*xfm", + }, + { + "name": "mode", + "pattern": "(?:^|_)mode-(image|points).*xfm", + }, + ], + # Rename schema-key entity names to match the short names used in + # pybids conventions and the old nipreps.json config. + entity_overrides={ + "description": {"name": "desc"}, + "hemisphere": {"name": "hemi"}, + "processing": {"name": "proc"}, + "inversion": {"name": "inv"}, + "mtransfer": {"name": "mt"}, + }, + extra_datatypes=["figures"], + extra_path_patterns=_NIPREPS_PATH_PATTERNS, + inject_entity_segments=[ + {"segment": "[_hash-{hash}]", "after": "[_ses-{session}]"}, + ], +) +_pybids_spec = generate_extended_config( + name="nipreps", + extensions=[_nipreps_extension], + rule_groups=["deriv"], +) + BIDS_DERIV_ENTITIES = _pybids_spec['entities'] BIDS_DERIV_PATTERNS = tuple(_pybids_spec['default_path_patterns']) diff --git a/niworkflows/reports/core.py b/niworkflows/reports/core.py index 698fd67ddcd..7f2a9409639 100644 --- a/niworkflows/reports/core.py +++ b/niworkflows/reports/core.py @@ -40,7 +40,8 @@ # Add a new figures spec try: - add_config_paths(figures=data.load('nipreps.json')) + from niworkflows.interfaces.bids import _pybids_spec + add_config_paths(figures=_pybids_spec) except ValueError as e: if "Configuration 'figures' already exists" != str(e): raise From df574cd00f40e7460b3c7a342c22776c737a3fe2 Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Thu, 12 Feb 2026 23:52:28 +1000 Subject: [PATCH 2/7] Replace hardcoded path patterns with declarative extra_rules Convert 28 monolithic pattern strings to compact rule dicts that specify only datatypes, suffixes, extensions, and entity deviations. Entity ordering and standard derivative entities are inherited from the BIDS schema. Figures patterns remain as literal strings due to their non-standard directory layout. --- niworkflows/interfaces/bids.py | 162 +++++++++++++++++++++++++-------- 1 file changed, 122 insertions(+), 40 deletions(-) diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index 36bf381efb7..83f16a6ea3c 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -64,47 +64,125 @@ # explicitly below. from bids.layout.config_gen import ConfigExtension, generate_extended_config -# NiPreps-specific path patterns that cannot be generated from the BIDS schema. +# NiPreps-specific extra rules for path pattern generation. Each rule dict +# specifies datatypes, suffixes, and extensions; entities are inherited from the +# schema's derivative rules for the given datatypes and only *additions* (like +# the non-schema entities fmapid, pvc, from/to/mode) or *overrides* (like +# hemisphere=required for surface files) need to be listed explicitly. +# # Schema-covered patterns (mask, probseg, dseg, phase, fieldmap, T2starmap, dwi -# sidecars) come from rule_groups=["deriv"] with hash injection; only patterns -# with non-schema suffixes, figures datatype, fmapid/pvc entities, or -# from/to/mode transforms need to be listed here. -_NIPREPS_PATH_PATTERNS = [ - # --- anat: non-schema suffixes --- - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_label-{label}][_desc-{desc}]_{suffix}{extension<.tsv|.json>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|anat}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}]_desc-{desc}_{suffix|mask}{extension<.label.gii|.json>|.label.gii}", - # --- func: non-schema suffixes --- - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|AROMAnoiseICs}{extension<.csv|.tsv>|.csv}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|components}{extension<.json|.tsv|.nii|.nii.gz>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix|decomposition}{extension<.json>|.json}", - "sub-{subject}[/ses-{session}]/{datatype|func}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_hemi-{hemi}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dtseries.nii|.dtseries.json|.func.gii|.func.json>}", - # --- dwi: non-schema suffixes --- - "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_desc-{desc}]_{suffix}{extension<.json|.nii.gz|.nii>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_from-{from}_to-{to}_mode-{mode|image}[_desc-{desc}]_{suffix|xfm}{extension<.txt|.h5>}", - # --- perf: non-schema suffixes --- - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_{suffix}{extension<.tsv|.json>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.json|.tsv>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|perf}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_space-{space}][_atlas-{atlas}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json|.tsv>|.tsv}", +# sidecars) come from rule_groups=["deriv"] with hash injection. +_XFM_ENTITIES = { + "from": "required", + "to": "required", + "mode": {"level": "required", "enum": ["image", "points"], "default": "image"}, +} +_PET_EXTRA_ENTITIES = { + "acquisition": "optional", + "ceagent": "optional", + "cohort": "optional", + "density": "optional", + "direction": "optional", + "hemisphere": "optional", + "label": "optional", + "part": "optional", +} +_PERF_EXTRA_ENTITIES = { + "task": "optional", + "ceagent": "optional", + "atlas": "optional", + "label": "optional", +} +_NIPREPS_EXTRA_RULES = [ + # --- anat --- + {"datatypes": ["anat"], + "suffixes": ["T1w", "T2w", "T1rho", "T1map", "T2map", "T2starmap", + "FLAIR", "FLASH", "PDmap", "PD", "PDT2", "dseg", + "inplaneT1", "inplaneT2", "angio", "T2starw", "MTw", "TSE"], + "extensions": [".nii", ".nii.gz", ".json"]}, + {"datatypes": ["anat"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], + "entities": _XFM_ENTITIES}, + {"datatypes": ["anat"], + "suffixes": ["white", "smoothwm", "pial", "midthickness", "inflated", + "vinflated", "sphere", "flat", "sulc", "curv", "thickness"], + "extensions": [".surf.gii", ".shape.gii"], + "entities": {"hemisphere": "required"}}, + {"datatypes": ["anat"], "suffixes": ["sulc", "curv", "thickness"], + "extensions": [".dscalar.nii", ".json"]}, + {"datatypes": ["anat"], "suffixes": ["morph"], "extensions": [".tsv", ".json"]}, + {"datatypes": ["anat"], "suffixes": ["mask"], + "extensions": [".label.gii", ".json"], + "entities": {"hemisphere": "required", "description": "required"}}, + # --- func --- + {"datatypes": ["func"], + "suffixes": ["bold", "cbv", "sbref", "boldref", "boldmap", "dseg"], + "extensions": [".nii", ".nii.gz", ".json"]}, + {"datatypes": ["func"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], + "entities": _XFM_ENTITIES}, + {"datatypes": ["func"], "suffixes": ["AROMAnoiseICs"], + "extensions": [".csv", ".tsv"]}, + {"datatypes": ["func"], "suffixes": ["timeseries", "regressors"], + "extensions": [".json", ".tsv"]}, + {"datatypes": ["func"], "suffixes": ["components", "mixing"], + "extensions": [".json", ".tsv", ".nii", ".nii.gz"]}, + {"datatypes": ["func"], "suffixes": ["decomposition"], + "extensions": [".json"]}, + {"datatypes": ["func"], "suffixes": ["bold", "boldmap"], + "extensions": [".dtseries.nii", ".dtseries.json", ".func.gii", ".func.json"], + "entities": {"hemisphere": "optional", "density": "optional"}}, + # --- dwi --- + {"datatypes": ["dwi"], + "suffixes": ["dwi", "dwiref", "epiref", "lowb", "dseg"], + "extensions": [".json", ".nii.gz", ".nii"]}, + {"datatypes": ["dwi"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], + "entities": _XFM_ENTITIES}, + # --- perf --- + {"datatypes": ["perf"], "suffixes": ["aslcontext"], + "extensions": [".tsv", ".json"], + "entities": {"task": "optional"}}, + {"datatypes": ["perf"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], + "entities": {**_PERF_EXTRA_ENTITIES, **_XFM_ENTITIES}}, + {"datatypes": ["perf"], "suffixes": ["timeseries"], + "extensions": [".json", ".tsv"], + "entities": _PERF_EXTRA_ENTITIES}, + {"datatypes": ["perf"], + "suffixes": ["asl", "aslref", "att", "cbf", "coverage", "mask"], + "extensions": [".nii", ".nii.gz", ".json", ".tsv"], + "entities": _PERF_EXTRA_ENTITIES}, # --- fmap: fmapid entity --- - "sub-{subject}[/ses-{session}]/{datatype|fmap}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|fmap}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_fmapid-{fmapid}][_label-{label}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - # --- pet: pvc entity and non-schema suffixes --- - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_res-{resolution}][_pvc-{pvc}][_desc-{desc}]_{suffix}{extension<.nii|.nii.gz|.json>|.nii.gz}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_hemi-{hemi}]_from-{from}_to-{to}_mode-{mode|image}_{suffix|xfm}{extension<.txt|.h5>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}]_hemi-{hemi}[_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.surf.gii|.shape.gii>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dscalar.nii|.json>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_part-{part}][_space-{space}][_cohort-{cohort}][_label-{label}][_pvc-{pvc}][_desc-{desc}]_{suffix|timeseries}{extension<.json|.tsv>|.tsv}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_hemi-{hemi}][_pvc-{pvc}][_space-{space}][_cohort-{cohort}][_den-{density}][_desc-{desc}]_{suffix}{extension<.dtseries.nii|.dtseries.json|.func.gii|.func.json>}", - "sub-{subject}[/ses-{session}]/{datatype|pet}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_res-{resolution}]_desc-{desc}_{suffix}{extension<.tsv|.json>|.tsv}", - # --- figures --- + {"datatypes": ["fmap"], "suffixes": ["fieldmap"], + "extensions": [".nii", ".nii.gz", ".json"], + "entities": {"fmapid": "optional"}}, + {"datatypes": ["fmap"], "suffixes": ["mask"], + "extensions": [".nii", ".nii.gz", ".json"], + "entities": {"fmapid": "optional"}}, + # --- pet: extra entities not in schema --- + {"datatypes": ["pet"], "suffixes": ["pet", "petref"], + "extensions": [".nii", ".nii.gz", ".json"], + "entities": {**_PET_EXTRA_ENTITIES, "pvc": "optional"}}, + {"datatypes": ["pet"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], + "entities": {**_PET_EXTRA_ENTITIES, **_XFM_ENTITIES}}, + {"datatypes": ["pet"], + "suffixes": ["white", "smoothwm", "pial", "midthickness", "inflated", + "vinflated", "sphere", "flat", "sulc", "curv", "thickness"], + "extensions": [".surf.gii", ".shape.gii"], + "entities": {**_PET_EXTRA_ENTITIES, "hemisphere": "required"}}, + {"datatypes": ["pet"], "suffixes": ["sulc", "curv", "thickness"], + "extensions": [".dscalar.nii", ".json"], + "entities": _PET_EXTRA_ENTITIES}, + {"datatypes": ["pet"], "suffixes": ["timeseries", "regressors", "tacs"], + "extensions": [".json", ".tsv"], + "entities": {**_PET_EXTRA_ENTITIES, "pvc": "optional"}}, + {"datatypes": ["pet"], "suffixes": ["pet"], + "extensions": [".dtseries.nii", ".dtseries.json", ".func.gii", ".func.json"], + "entities": {**_PET_EXTRA_ENTITIES, "pvc": "optional"}}, + {"datatypes": ["pet"], "suffixes": ["morph"], "extensions": [".tsv", ".json"], + "entities": {**_PET_EXTRA_ENTITIES, "description": "required"}}, +] + +# Figure patterns use a non-standard directory layout (no session directory), +# so they cannot be generated from rules and are kept as literal patterns. +_NIPREPS_FIGURE_PATTERNS = [ "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", @@ -133,14 +211,17 @@ { "name": "from", "pattern": "(?:^|_)from-([a-zA-Z0-9+]+).*xfm", + "position": "after:hemi", }, { "name": "to", "pattern": "(?:^|_)to-([a-zA-Z0-9+]+).*xfm", + "position": "after:from", }, { "name": "mode", "pattern": "(?:^|_)mode-(image|points).*xfm", + "position": "after:to", }, ], # Rename schema-key entity names to match the short names used in @@ -153,7 +234,8 @@ "mtransfer": {"name": "mt"}, }, extra_datatypes=["figures"], - extra_path_patterns=_NIPREPS_PATH_PATTERNS, + extra_rules=_NIPREPS_EXTRA_RULES, + extra_path_patterns=_NIPREPS_FIGURE_PATTERNS, inject_entity_segments=[ {"segment": "[_hash-{hash}]", "after": "[_ses-{session}]"}, ], From 196fb5a98b860fcf1f10369c5d86e7f0c5bcdd9d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:57:53 +0000 Subject: [PATCH 3/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- niworkflows/interfaces/bids.py | 367 +++++++++++++++++++++------------ niworkflows/reports/core.py | 1 + 2 files changed, 240 insertions(+), 128 deletions(-) diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index 83f16a6ea3c..8fa39971cdb 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -27,7 +27,7 @@ import shutil from collections import defaultdict from contextlib import suppress -from json import dumps, loads +from json import dumps from pathlib import Path import nibabel as nb @@ -51,7 +51,6 @@ from nipype.interfaces.io import add_traits from nipype.utils.filemanip import hash_infile -from .. import data from ..utils.bids import _init_layout, relative_to_root from ..utils.images import set_consumables, unsafe_write_nifti_header_and_data from ..utils.misc import _copy_any, unlink @@ -73,177 +72,289 @@ # Schema-covered patterns (mask, probseg, dseg, phase, fieldmap, T2starmap, dwi # sidecars) come from rule_groups=["deriv"] with hash injection. _XFM_ENTITIES = { - "from": "required", - "to": "required", - "mode": {"level": "required", "enum": ["image", "points"], "default": "image"}, + 'from': 'required', + 'to': 'required', + 'mode': {'level': 'required', 'enum': ['image', 'points'], 'default': 'image'}, } _PET_EXTRA_ENTITIES = { - "acquisition": "optional", - "ceagent": "optional", - "cohort": "optional", - "density": "optional", - "direction": "optional", - "hemisphere": "optional", - "label": "optional", - "part": "optional", + 'acquisition': 'optional', + 'ceagent': 'optional', + 'cohort': 'optional', + 'density': 'optional', + 'direction': 'optional', + 'hemisphere': 'optional', + 'label': 'optional', + 'part': 'optional', } _PERF_EXTRA_ENTITIES = { - "task": "optional", - "ceagent": "optional", - "atlas": "optional", - "label": "optional", + 'task': 'optional', + 'ceagent': 'optional', + 'atlas': 'optional', + 'label': 'optional', } _NIPREPS_EXTRA_RULES = [ # --- anat --- - {"datatypes": ["anat"], - "suffixes": ["T1w", "T2w", "T1rho", "T1map", "T2map", "T2starmap", - "FLAIR", "FLASH", "PDmap", "PD", "PDT2", "dseg", - "inplaneT1", "inplaneT2", "angio", "T2starw", "MTw", "TSE"], - "extensions": [".nii", ".nii.gz", ".json"]}, - {"datatypes": ["anat"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], - "entities": _XFM_ENTITIES}, - {"datatypes": ["anat"], - "suffixes": ["white", "smoothwm", "pial", "midthickness", "inflated", - "vinflated", "sphere", "flat", "sulc", "curv", "thickness"], - "extensions": [".surf.gii", ".shape.gii"], - "entities": {"hemisphere": "required"}}, - {"datatypes": ["anat"], "suffixes": ["sulc", "curv", "thickness"], - "extensions": [".dscalar.nii", ".json"]}, - {"datatypes": ["anat"], "suffixes": ["morph"], "extensions": [".tsv", ".json"]}, - {"datatypes": ["anat"], "suffixes": ["mask"], - "extensions": [".label.gii", ".json"], - "entities": {"hemisphere": "required", "description": "required"}}, + { + 'datatypes': ['anat'], + 'suffixes': [ + 'T1w', + 'T2w', + 'T1rho', + 'T1map', + 'T2map', + 'T2starmap', + 'FLAIR', + 'FLASH', + 'PDmap', + 'PD', + 'PDT2', + 'dseg', + 'inplaneT1', + 'inplaneT2', + 'angio', + 'T2starw', + 'MTw', + 'TSE', + ], + 'extensions': ['.nii', '.nii.gz', '.json'], + }, + { + 'datatypes': ['anat'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': _XFM_ENTITIES, + }, + { + 'datatypes': ['anat'], + 'suffixes': [ + 'white', + 'smoothwm', + 'pial', + 'midthickness', + 'inflated', + 'vinflated', + 'sphere', + 'flat', + 'sulc', + 'curv', + 'thickness', + ], + 'extensions': ['.surf.gii', '.shape.gii'], + 'entities': {'hemisphere': 'required'}, + }, + { + 'datatypes': ['anat'], + 'suffixes': ['sulc', 'curv', 'thickness'], + 'extensions': ['.dscalar.nii', '.json'], + }, + {'datatypes': ['anat'], 'suffixes': ['morph'], 'extensions': ['.tsv', '.json']}, + { + 'datatypes': ['anat'], + 'suffixes': ['mask'], + 'extensions': ['.label.gii', '.json'], + 'entities': {'hemisphere': 'required', 'description': 'required'}, + }, # --- func --- - {"datatypes": ["func"], - "suffixes": ["bold", "cbv", "sbref", "boldref", "boldmap", "dseg"], - "extensions": [".nii", ".nii.gz", ".json"]}, - {"datatypes": ["func"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], - "entities": _XFM_ENTITIES}, - {"datatypes": ["func"], "suffixes": ["AROMAnoiseICs"], - "extensions": [".csv", ".tsv"]}, - {"datatypes": ["func"], "suffixes": ["timeseries", "regressors"], - "extensions": [".json", ".tsv"]}, - {"datatypes": ["func"], "suffixes": ["components", "mixing"], - "extensions": [".json", ".tsv", ".nii", ".nii.gz"]}, - {"datatypes": ["func"], "suffixes": ["decomposition"], - "extensions": [".json"]}, - {"datatypes": ["func"], "suffixes": ["bold", "boldmap"], - "extensions": [".dtseries.nii", ".dtseries.json", ".func.gii", ".func.json"], - "entities": {"hemisphere": "optional", "density": "optional"}}, + { + 'datatypes': ['func'], + 'suffixes': ['bold', 'cbv', 'sbref', 'boldref', 'boldmap', 'dseg'], + 'extensions': ['.nii', '.nii.gz', '.json'], + }, + { + 'datatypes': ['func'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': _XFM_ENTITIES, + }, + {'datatypes': ['func'], 'suffixes': ['AROMAnoiseICs'], 'extensions': ['.csv', '.tsv']}, + { + 'datatypes': ['func'], + 'suffixes': ['timeseries', 'regressors'], + 'extensions': ['.json', '.tsv'], + }, + { + 'datatypes': ['func'], + 'suffixes': ['components', 'mixing'], + 'extensions': ['.json', '.tsv', '.nii', '.nii.gz'], + }, + {'datatypes': ['func'], 'suffixes': ['decomposition'], 'extensions': ['.json']}, + { + 'datatypes': ['func'], + 'suffixes': ['bold', 'boldmap'], + 'extensions': ['.dtseries.nii', '.dtseries.json', '.func.gii', '.func.json'], + 'entities': {'hemisphere': 'optional', 'density': 'optional'}, + }, # --- dwi --- - {"datatypes": ["dwi"], - "suffixes": ["dwi", "dwiref", "epiref", "lowb", "dseg"], - "extensions": [".json", ".nii.gz", ".nii"]}, - {"datatypes": ["dwi"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], - "entities": _XFM_ENTITIES}, + { + 'datatypes': ['dwi'], + 'suffixes': ['dwi', 'dwiref', 'epiref', 'lowb', 'dseg'], + 'extensions': ['.json', '.nii.gz', '.nii'], + }, + { + 'datatypes': ['dwi'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': _XFM_ENTITIES, + }, # --- perf --- - {"datatypes": ["perf"], "suffixes": ["aslcontext"], - "extensions": [".tsv", ".json"], - "entities": {"task": "optional"}}, - {"datatypes": ["perf"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], - "entities": {**_PERF_EXTRA_ENTITIES, **_XFM_ENTITIES}}, - {"datatypes": ["perf"], "suffixes": ["timeseries"], - "extensions": [".json", ".tsv"], - "entities": _PERF_EXTRA_ENTITIES}, - {"datatypes": ["perf"], - "suffixes": ["asl", "aslref", "att", "cbf", "coverage", "mask"], - "extensions": [".nii", ".nii.gz", ".json", ".tsv"], - "entities": _PERF_EXTRA_ENTITIES}, + { + 'datatypes': ['perf'], + 'suffixes': ['aslcontext'], + 'extensions': ['.tsv', '.json'], + 'entities': {'task': 'optional'}, + }, + { + 'datatypes': ['perf'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': {**_PERF_EXTRA_ENTITIES, **_XFM_ENTITIES}, + }, + { + 'datatypes': ['perf'], + 'suffixes': ['timeseries'], + 'extensions': ['.json', '.tsv'], + 'entities': _PERF_EXTRA_ENTITIES, + }, + { + 'datatypes': ['perf'], + 'suffixes': ['asl', 'aslref', 'att', 'cbf', 'coverage', 'mask'], + 'extensions': ['.nii', '.nii.gz', '.json', '.tsv'], + 'entities': _PERF_EXTRA_ENTITIES, + }, # --- fmap: fmapid entity --- - {"datatypes": ["fmap"], "suffixes": ["fieldmap"], - "extensions": [".nii", ".nii.gz", ".json"], - "entities": {"fmapid": "optional"}}, - {"datatypes": ["fmap"], "suffixes": ["mask"], - "extensions": [".nii", ".nii.gz", ".json"], - "entities": {"fmapid": "optional"}}, + { + 'datatypes': ['fmap'], + 'suffixes': ['fieldmap'], + 'extensions': ['.nii', '.nii.gz', '.json'], + 'entities': {'fmapid': 'optional'}, + }, + { + 'datatypes': ['fmap'], + 'suffixes': ['mask'], + 'extensions': ['.nii', '.nii.gz', '.json'], + 'entities': {'fmapid': 'optional'}, + }, # --- pet: extra entities not in schema --- - {"datatypes": ["pet"], "suffixes": ["pet", "petref"], - "extensions": [".nii", ".nii.gz", ".json"], - "entities": {**_PET_EXTRA_ENTITIES, "pvc": "optional"}}, - {"datatypes": ["pet"], "suffixes": ["xfm"], "extensions": [".txt", ".h5"], - "entities": {**_PET_EXTRA_ENTITIES, **_XFM_ENTITIES}}, - {"datatypes": ["pet"], - "suffixes": ["white", "smoothwm", "pial", "midthickness", "inflated", - "vinflated", "sphere", "flat", "sulc", "curv", "thickness"], - "extensions": [".surf.gii", ".shape.gii"], - "entities": {**_PET_EXTRA_ENTITIES, "hemisphere": "required"}}, - {"datatypes": ["pet"], "suffixes": ["sulc", "curv", "thickness"], - "extensions": [".dscalar.nii", ".json"], - "entities": _PET_EXTRA_ENTITIES}, - {"datatypes": ["pet"], "suffixes": ["timeseries", "regressors", "tacs"], - "extensions": [".json", ".tsv"], - "entities": {**_PET_EXTRA_ENTITIES, "pvc": "optional"}}, - {"datatypes": ["pet"], "suffixes": ["pet"], - "extensions": [".dtseries.nii", ".dtseries.json", ".func.gii", ".func.json"], - "entities": {**_PET_EXTRA_ENTITIES, "pvc": "optional"}}, - {"datatypes": ["pet"], "suffixes": ["morph"], "extensions": [".tsv", ".json"], - "entities": {**_PET_EXTRA_ENTITIES, "description": "required"}}, + { + 'datatypes': ['pet'], + 'suffixes': ['pet', 'petref'], + 'extensions': ['.nii', '.nii.gz', '.json'], + 'entities': {**_PET_EXTRA_ENTITIES, 'pvc': 'optional'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': {**_PET_EXTRA_ENTITIES, **_XFM_ENTITIES}, + }, + { + 'datatypes': ['pet'], + 'suffixes': [ + 'white', + 'smoothwm', + 'pial', + 'midthickness', + 'inflated', + 'vinflated', + 'sphere', + 'flat', + 'sulc', + 'curv', + 'thickness', + ], + 'extensions': ['.surf.gii', '.shape.gii'], + 'entities': {**_PET_EXTRA_ENTITIES, 'hemisphere': 'required'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['sulc', 'curv', 'thickness'], + 'extensions': ['.dscalar.nii', '.json'], + 'entities': _PET_EXTRA_ENTITIES, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['timeseries', 'regressors', 'tacs'], + 'extensions': ['.json', '.tsv'], + 'entities': {**_PET_EXTRA_ENTITIES, 'pvc': 'optional'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['pet'], + 'extensions': ['.dtseries.nii', '.dtseries.json', '.func.gii', '.func.json'], + 'entities': {**_PET_EXTRA_ENTITIES, 'pvc': 'optional'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['morph'], + 'extensions': ['.tsv', '.json'], + 'entities': {**_PET_EXTRA_ENTITIES, 'description': 'required'}, + }, ] # Figure patterns use a non-standard directory layout (no session directory), # so they cannot be generated from rules and are kept as literal patterns. _NIPREPS_FIGURE_PATTERNS = [ - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", - "sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}", + 'sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}', + 'sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_fmapid-{fmapid}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}', + 'sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}', + 'sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_dir-{direction}][_run-{run}][_echo-{echo}][_part-{part}][_space-{space}][_cohort-{cohort}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}', + 'sub-{subject}/{datatype}/sub-{subject}[_ses-{session}][_hash-{hash}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_trc-{tracer}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_label-{label}][_desc-{desc}]_{suffix}{extension<.html|.svg>|.svg}', ] _nipreps_extension = ConfigExtension( - name="nipreps", + name='nipreps', extra_entities=[ { - "name": "hash", - "pattern": "hash-([a-zA-Z0-9+]+)", - "position": "after:session", + 'name': 'hash', + 'pattern': 'hash-([a-zA-Z0-9+]+)', + 'position': 'after:session', }, { - "name": "fmapid", - "pattern": "[_/\\\\]+fmapid-([a-zA-Z0-9+]+)", - "position": "after:label", + 'name': 'fmapid', + 'pattern': '[_/\\\\]+fmapid-([a-zA-Z0-9+]+)', + 'position': 'after:label', }, { - "name": "pvc", - "pattern": "[_/\\\\]+pvc-([a-zA-Z0-9+]+)", - "position": "after:tracer", + 'name': 'pvc', + 'pattern': '[_/\\\\]+pvc-([a-zA-Z0-9+]+)', + 'position': 'after:tracer', }, { - "name": "from", - "pattern": "(?:^|_)from-([a-zA-Z0-9+]+).*xfm", - "position": "after:hemi", + 'name': 'from', + 'pattern': '(?:^|_)from-([a-zA-Z0-9+]+).*xfm', + 'position': 'after:hemi', }, { - "name": "to", - "pattern": "(?:^|_)to-([a-zA-Z0-9+]+).*xfm", - "position": "after:from", + 'name': 'to', + 'pattern': '(?:^|_)to-([a-zA-Z0-9+]+).*xfm', + 'position': 'after:from', }, { - "name": "mode", - "pattern": "(?:^|_)mode-(image|points).*xfm", - "position": "after:to", + 'name': 'mode', + 'pattern': '(?:^|_)mode-(image|points).*xfm', + 'position': 'after:to', }, ], # Rename schema-key entity names to match the short names used in # pybids conventions and the old nipreps.json config. entity_overrides={ - "description": {"name": "desc"}, - "hemisphere": {"name": "hemi"}, - "processing": {"name": "proc"}, - "inversion": {"name": "inv"}, - "mtransfer": {"name": "mt"}, + 'description': {'name': 'desc'}, + 'hemisphere': {'name': 'hemi'}, + 'processing': {'name': 'proc'}, + 'inversion': {'name': 'inv'}, + 'mtransfer': {'name': 'mt'}, }, - extra_datatypes=["figures"], + extra_datatypes=['figures'], extra_rules=_NIPREPS_EXTRA_RULES, extra_path_patterns=_NIPREPS_FIGURE_PATTERNS, inject_entity_segments=[ - {"segment": "[_hash-{hash}]", "after": "[_ses-{session}]"}, + {'segment': '[_hash-{hash}]', 'after': '[_ses-{session}]'}, ], ) _pybids_spec = generate_extended_config( - name="nipreps", + name='nipreps', extensions=[_nipreps_extension], - rule_groups=["deriv"], + rule_groups=['deriv'], ) BIDS_DERIV_ENTITIES = _pybids_spec['entities'] diff --git a/niworkflows/reports/core.py b/niworkflows/reports/core.py index 7f2a9409639..831f0796c6a 100644 --- a/niworkflows/reports/core.py +++ b/niworkflows/reports/core.py @@ -41,6 +41,7 @@ # Add a new figures spec try: from niworkflows.interfaces.bids import _pybids_spec + add_config_paths(figures=_pybids_spec) except ValueError as e: if "Configuration 'figures' already exists" != str(e): From f232df2e776b5d50910191f96972fe752d601d05 Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Fri, 13 Feb 2026 00:24:16 +1000 Subject: [PATCH 4/7] Add comments explaining why schema-covered suffixes remain in extra_rules Schema splits .json into sidecar patterns, but DerivativesDataSink outputs .nii + .json together as multi-file, requiring a single pattern with all extensions. Document this and other reasons for apparent redundancy. --- niworkflows/interfaces/bids.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index 8fa39971cdb..194fb55b654 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -94,6 +94,9 @@ } _NIPREPS_EXTRA_RULES = [ # --- anat --- + # Non-schema suffixes + schema suffixes that need .json in a single pattern + # (DerivativesDataSink may output .nii + .json together as multi-file; + # the schema splits .json into a sidecar pattern which breaks that). { 'datatypes': ['anat'], 'suffixes': [ @@ -178,6 +181,7 @@ 'extensions': ['.json', '.tsv', '.nii', '.nii.gz'], }, {'datatypes': ['func'], 'suffixes': ['decomposition'], 'extensions': ['.json']}, + # Non-schema CIFTI/surface extensions for bold { 'datatypes': ['func'], 'suffixes': ['bold', 'boldmap'], @@ -196,7 +200,7 @@ 'extensions': ['.txt', '.h5'], 'entities': _XFM_ENTITIES, }, - # --- perf --- + # --- perf: non-schema suffixes and extra entities --- { 'datatypes': ['perf'], 'suffixes': ['aslcontext'], @@ -221,7 +225,7 @@ 'extensions': ['.nii', '.nii.gz', '.json', '.tsv'], 'entities': _PERF_EXTRA_ENTITIES, }, - # --- fmap: fmapid entity --- + # --- fmap: fmapid entity (schema has fieldmap/mask but without fmapid) --- { 'datatypes': ['fmap'], 'suffixes': ['fieldmap'], @@ -234,7 +238,7 @@ 'extensions': ['.nii', '.nii.gz', '.json'], 'entities': {'fmapid': 'optional'}, }, - # --- pet: extra entities not in schema --- + # --- pet: extra entities not in schema (pvc, acq, ce, hemi, etc.) --- { 'datatypes': ['pet'], 'suffixes': ['pet', 'petref'], From a3a9c8d8d0dd79a93c8bc0f9fbcac56d83aa4ae3 Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Fri, 13 Feb 2026 00:41:55 +1000 Subject: [PATCH 5/7] Use sidecar_split=False to remove redundant schema suffixes from extra_rules With sidecar_split=False, schema derivative patterns include all extensions in a single pattern, so extra_rules no longer need to duplicate schema-known suffixes for multi-file output. Remove 22 redundant suffixes from 3 rules: - anat: keep only MTw, TSE (removed 16 schema suffixes) - func: keep only boldref, boldmap (removed 4 schema suffixes) - dwi: keep only dwiref, epiref, lowb (removed 2 schema suffixes) --- niworkflows/interfaces/bids.py | 35 +++++++--------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index 194fb55b654..ca1a7320d2b 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -93,32 +93,10 @@ 'label': 'optional', } _NIPREPS_EXTRA_RULES = [ - # --- anat --- - # Non-schema suffixes + schema suffixes that need .json in a single pattern - # (DerivativesDataSink may output .nii + .json together as multi-file; - # the schema splits .json into a sidecar pattern which breaks that). + # --- anat: non-schema suffixes --- { 'datatypes': ['anat'], - 'suffixes': [ - 'T1w', - 'T2w', - 'T1rho', - 'T1map', - 'T2map', - 'T2starmap', - 'FLAIR', - 'FLASH', - 'PDmap', - 'PD', - 'PDT2', - 'dseg', - 'inplaneT1', - 'inplaneT2', - 'angio', - 'T2starw', - 'MTw', - 'TSE', - ], + 'suffixes': ['MTw', 'TSE'], 'extensions': ['.nii', '.nii.gz', '.json'], }, { @@ -157,10 +135,10 @@ 'extensions': ['.label.gii', '.json'], 'entities': {'hemisphere': 'required', 'description': 'required'}, }, - # --- func --- + # --- func: non-schema suffixes --- { 'datatypes': ['func'], - 'suffixes': ['bold', 'cbv', 'sbref', 'boldref', 'boldmap', 'dseg'], + 'suffixes': ['boldref', 'boldmap'], 'extensions': ['.nii', '.nii.gz', '.json'], }, { @@ -188,10 +166,10 @@ 'extensions': ['.dtseries.nii', '.dtseries.json', '.func.gii', '.func.json'], 'entities': {'hemisphere': 'optional', 'density': 'optional'}, }, - # --- dwi --- + # --- dwi: non-schema suffixes --- { 'datatypes': ['dwi'], - 'suffixes': ['dwi', 'dwiref', 'epiref', 'lowb', 'dseg'], + 'suffixes': ['dwiref', 'epiref', 'lowb'], 'extensions': ['.json', '.nii.gz', '.nii'], }, { @@ -359,6 +337,7 @@ name='nipreps', extensions=[_nipreps_extension], rule_groups=['deriv'], + sidecar_split=False, ) BIDS_DERIV_ENTITIES = _pybids_spec['entities'] From a137a6bbc805995424a53200400545e863febcce Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Fri, 13 Feb 2026 00:57:54 +1000 Subject: [PATCH 6/7] Refactor extra_rules: extract shared constants, merge duplicates - Extract _SURFACE_SUFFIXES, _SCALAR_SUFFIXES, _CIFTI_EXTENSIONS constants - Merge xfm rules for anat/func/dwi into one multi-datatype rule - Merge fmap fieldmap + mask into one rule - Rename _PET_EXTRA_ENTITIES -> _PET_ENTITIES, _PERF_EXTRA_ENTITIES -> _PERF_ENTITIES - Reorganize rules by category (transforms, surfaces, CIFTI, then per-datatype) 28 rules -> 25 rules, -95 lines net. --- niworkflows/interfaces/bids.py | 263 +++++++++++---------------------- 1 file changed, 84 insertions(+), 179 deletions(-) diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index ca1a7320d2b..d9145be094d 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -71,12 +71,13 @@ # # Schema-covered patterns (mask, probseg, dseg, phase, fieldmap, T2starmap, dwi # sidecars) come from rule_groups=["deriv"] with hash injection. +# Shared entity sets _XFM_ENTITIES = { 'from': 'required', 'to': 'required', 'mode': {'level': 'required', 'enum': ['image', 'points'], 'default': 'image'}, } -_PET_EXTRA_ENTITIES = { +_PET_ENTITIES = { 'acquisition': 'optional', 'ceagent': 'optional', 'cohort': 'optional', @@ -86,191 +87,95 @@ 'label': 'optional', 'part': 'optional', } -_PERF_EXTRA_ENTITIES = { +_PERF_ENTITIES = { 'task': 'optional', 'ceagent': 'optional', 'atlas': 'optional', 'label': 'optional', } + +# Shared suffix/extension lists +_SURFACE_SUFFIXES = [ + 'white', 'smoothwm', 'pial', 'midthickness', + 'inflated', 'vinflated', 'sphere', 'flat', + 'sulc', 'curv', 'thickness', +] +_SCALAR_SUFFIXES = ['sulc', 'curv', 'thickness'] +_CIFTI_EXTENSIONS = ['.dtseries.nii', '.dtseries.json', '.func.gii', '.func.json'] + _NIPREPS_EXTRA_RULES = [ - # --- anat: non-schema suffixes --- - { - 'datatypes': ['anat'], - 'suffixes': ['MTw', 'TSE'], - 'extensions': ['.nii', '.nii.gz', '.json'], - }, - { - 'datatypes': ['anat'], - 'suffixes': ['xfm'], - 'extensions': ['.txt', '.h5'], - 'entities': _XFM_ENTITIES, - }, - { - 'datatypes': ['anat'], - 'suffixes': [ - 'white', - 'smoothwm', - 'pial', - 'midthickness', - 'inflated', - 'vinflated', - 'sphere', - 'flat', - 'sulc', - 'curv', - 'thickness', - ], - 'extensions': ['.surf.gii', '.shape.gii'], - 'entities': {'hemisphere': 'required'}, - }, - { - 'datatypes': ['anat'], - 'suffixes': ['sulc', 'curv', 'thickness'], - 'extensions': ['.dscalar.nii', '.json'], - }, + # --- Transforms (all datatypes) --- + {'datatypes': ['anat', 'func', 'dwi'], + 'suffixes': ['xfm'], 'extensions': ['.txt', '.h5'], + 'entities': _XFM_ENTITIES}, + {'datatypes': ['perf'], 'suffixes': ['xfm'], 'extensions': ['.txt', '.h5'], + 'entities': {**_PERF_ENTITIES, **_XFM_ENTITIES}}, + {'datatypes': ['pet'], 'suffixes': ['xfm'], 'extensions': ['.txt', '.h5'], + 'entities': {**_PET_ENTITIES, **_XFM_ENTITIES}}, + # --- Surface anatomy (anat + pet) --- + {'datatypes': ['anat'], 'suffixes': _SURFACE_SUFFIXES, + 'extensions': ['.surf.gii', '.shape.gii'], + 'entities': {'hemisphere': 'required'}}, + {'datatypes': ['anat'], 'suffixes': _SCALAR_SUFFIXES, + 'extensions': ['.dscalar.nii', '.json']}, {'datatypes': ['anat'], 'suffixes': ['morph'], 'extensions': ['.tsv', '.json']}, - { - 'datatypes': ['anat'], - 'suffixes': ['mask'], - 'extensions': ['.label.gii', '.json'], - 'entities': {'hemisphere': 'required', 'description': 'required'}, - }, - # --- func: non-schema suffixes --- - { - 'datatypes': ['func'], - 'suffixes': ['boldref', 'boldmap'], - 'extensions': ['.nii', '.nii.gz', '.json'], - }, - { - 'datatypes': ['func'], - 'suffixes': ['xfm'], - 'extensions': ['.txt', '.h5'], - 'entities': _XFM_ENTITIES, - }, - {'datatypes': ['func'], 'suffixes': ['AROMAnoiseICs'], 'extensions': ['.csv', '.tsv']}, - { - 'datatypes': ['func'], - 'suffixes': ['timeseries', 'regressors'], - 'extensions': ['.json', '.tsv'], - }, - { - 'datatypes': ['func'], - 'suffixes': ['components', 'mixing'], - 'extensions': ['.json', '.tsv', '.nii', '.nii.gz'], - }, - {'datatypes': ['func'], 'suffixes': ['decomposition'], 'extensions': ['.json']}, - # Non-schema CIFTI/surface extensions for bold - { - 'datatypes': ['func'], - 'suffixes': ['bold', 'boldmap'], - 'extensions': ['.dtseries.nii', '.dtseries.json', '.func.gii', '.func.json'], - 'entities': {'hemisphere': 'optional', 'density': 'optional'}, - }, - # --- dwi: non-schema suffixes --- - { - 'datatypes': ['dwi'], - 'suffixes': ['dwiref', 'epiref', 'lowb'], - 'extensions': ['.json', '.nii.gz', '.nii'], - }, - { - 'datatypes': ['dwi'], - 'suffixes': ['xfm'], - 'extensions': ['.txt', '.h5'], - 'entities': _XFM_ENTITIES, - }, - # --- perf: non-schema suffixes and extra entities --- - { - 'datatypes': ['perf'], - 'suffixes': ['aslcontext'], - 'extensions': ['.tsv', '.json'], - 'entities': {'task': 'optional'}, - }, - { - 'datatypes': ['perf'], - 'suffixes': ['xfm'], - 'extensions': ['.txt', '.h5'], - 'entities': {**_PERF_EXTRA_ENTITIES, **_XFM_ENTITIES}, - }, - { - 'datatypes': ['perf'], - 'suffixes': ['timeseries'], - 'extensions': ['.json', '.tsv'], - 'entities': _PERF_EXTRA_ENTITIES, - }, - { - 'datatypes': ['perf'], - 'suffixes': ['asl', 'aslref', 'att', 'cbf', 'coverage', 'mask'], - 'extensions': ['.nii', '.nii.gz', '.json', '.tsv'], - 'entities': _PERF_EXTRA_ENTITIES, - }, - # --- fmap: fmapid entity (schema has fieldmap/mask but without fmapid) --- - { - 'datatypes': ['fmap'], - 'suffixes': ['fieldmap'], - 'extensions': ['.nii', '.nii.gz', '.json'], - 'entities': {'fmapid': 'optional'}, - }, - { - 'datatypes': ['fmap'], - 'suffixes': ['mask'], - 'extensions': ['.nii', '.nii.gz', '.json'], - 'entities': {'fmapid': 'optional'}, - }, - # --- pet: extra entities not in schema (pvc, acq, ce, hemi, etc.) --- - { - 'datatypes': ['pet'], - 'suffixes': ['pet', 'petref'], - 'extensions': ['.nii', '.nii.gz', '.json'], - 'entities': {**_PET_EXTRA_ENTITIES, 'pvc': 'optional'}, - }, - { - 'datatypes': ['pet'], - 'suffixes': ['xfm'], - 'extensions': ['.txt', '.h5'], - 'entities': {**_PET_EXTRA_ENTITIES, **_XFM_ENTITIES}, - }, - { - 'datatypes': ['pet'], - 'suffixes': [ - 'white', - 'smoothwm', - 'pial', - 'midthickness', - 'inflated', - 'vinflated', - 'sphere', - 'flat', - 'sulc', - 'curv', - 'thickness', - ], - 'extensions': ['.surf.gii', '.shape.gii'], - 'entities': {**_PET_EXTRA_ENTITIES, 'hemisphere': 'required'}, - }, - { - 'datatypes': ['pet'], - 'suffixes': ['sulc', 'curv', 'thickness'], - 'extensions': ['.dscalar.nii', '.json'], - 'entities': _PET_EXTRA_ENTITIES, - }, - { - 'datatypes': ['pet'], - 'suffixes': ['timeseries', 'regressors', 'tacs'], - 'extensions': ['.json', '.tsv'], - 'entities': {**_PET_EXTRA_ENTITIES, 'pvc': 'optional'}, - }, - { - 'datatypes': ['pet'], - 'suffixes': ['pet'], - 'extensions': ['.dtseries.nii', '.dtseries.json', '.func.gii', '.func.json'], - 'entities': {**_PET_EXTRA_ENTITIES, 'pvc': 'optional'}, - }, - { - 'datatypes': ['pet'], - 'suffixes': ['morph'], - 'extensions': ['.tsv', '.json'], - 'entities': {**_PET_EXTRA_ENTITIES, 'description': 'required'}, - }, + {'datatypes': ['anat'], 'suffixes': ['mask'], + 'extensions': ['.label.gii', '.json'], + 'entities': {'hemisphere': 'required', 'description': 'required'}}, + {'datatypes': ['pet'], 'suffixes': _SURFACE_SUFFIXES, + 'extensions': ['.surf.gii', '.shape.gii'], + 'entities': {**_PET_ENTITIES, 'hemisphere': 'required'}}, + {'datatypes': ['pet'], 'suffixes': _SCALAR_SUFFIXES, + 'extensions': ['.dscalar.nii', '.json'], + 'entities': _PET_ENTITIES}, + {'datatypes': ['pet'], 'suffixes': ['morph'], 'extensions': ['.tsv', '.json'], + 'entities': {**_PET_ENTITIES, 'description': 'required'}}, + # --- CIFTI/surface extensions for schema suffixes --- + {'datatypes': ['func'], 'suffixes': ['bold', 'boldmap'], + 'extensions': _CIFTI_EXTENSIONS, + 'entities': {'hemisphere': 'optional', 'density': 'optional'}}, + {'datatypes': ['pet'], 'suffixes': ['pet'], + 'extensions': _CIFTI_EXTENSIONS, + 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}}, + # --- anat --- + {'datatypes': ['anat'], 'suffixes': ['MTw', 'TSE'], + 'extensions': ['.nii', '.nii.gz', '.json']}, + # --- func --- + {'datatypes': ['func'], 'suffixes': ['boldref', 'boldmap'], + 'extensions': ['.nii', '.nii.gz', '.json']}, + {'datatypes': ['func'], 'suffixes': ['AROMAnoiseICs'], + 'extensions': ['.csv', '.tsv']}, + {'datatypes': ['func'], 'suffixes': ['timeseries', 'regressors'], + 'extensions': ['.json', '.tsv']}, + {'datatypes': ['func'], 'suffixes': ['components', 'mixing'], + 'extensions': ['.json', '.tsv', '.nii', '.nii.gz']}, + {'datatypes': ['func'], 'suffixes': ['decomposition'], + 'extensions': ['.json']}, + # --- dwi --- + {'datatypes': ['dwi'], 'suffixes': ['dwiref', 'epiref', 'lowb'], + 'extensions': ['.nii', '.nii.gz', '.json']}, + # --- perf --- + {'datatypes': ['perf'], 'suffixes': ['aslcontext'], + 'extensions': ['.tsv', '.json'], + 'entities': {'task': 'optional'}}, + {'datatypes': ['perf'], 'suffixes': ['timeseries'], + 'extensions': ['.json', '.tsv'], + 'entities': _PERF_ENTITIES}, + {'datatypes': ['perf'], + 'suffixes': ['asl', 'aslref', 'att', 'cbf', 'coverage', 'mask'], + 'extensions': ['.nii', '.nii.gz', '.json', '.tsv'], + 'entities': _PERF_ENTITIES}, + # --- fmap --- + {'datatypes': ['fmap'], 'suffixes': ['fieldmap', 'mask'], + 'extensions': ['.nii', '.nii.gz', '.json'], + 'entities': {'fmapid': 'optional'}}, + # --- pet --- + {'datatypes': ['pet'], 'suffixes': ['pet', 'petref'], + 'extensions': ['.nii', '.nii.gz', '.json'], + 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}}, + {'datatypes': ['pet'], 'suffixes': ['timeseries', 'regressors', 'tacs'], + 'extensions': ['.json', '.tsv'], + 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}}, ] # Figure patterns use a non-standard directory layout (no session directory), From b5fd0eb3c4512dbf7aff64ebc8e75cb4cbfd908d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 14:58:20 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- niworkflows/interfaces/bids.py | 200 ++++++++++++++++++++++----------- 1 file changed, 135 insertions(+), 65 deletions(-) diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index d9145be094d..685acb2ee80 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -96,86 +96,156 @@ # Shared suffix/extension lists _SURFACE_SUFFIXES = [ - 'white', 'smoothwm', 'pial', 'midthickness', - 'inflated', 'vinflated', 'sphere', 'flat', - 'sulc', 'curv', 'thickness', + 'white', + 'smoothwm', + 'pial', + 'midthickness', + 'inflated', + 'vinflated', + 'sphere', + 'flat', + 'sulc', + 'curv', + 'thickness', ] _SCALAR_SUFFIXES = ['sulc', 'curv', 'thickness'] _CIFTI_EXTENSIONS = ['.dtseries.nii', '.dtseries.json', '.func.gii', '.func.json'] _NIPREPS_EXTRA_RULES = [ # --- Transforms (all datatypes) --- - {'datatypes': ['anat', 'func', 'dwi'], - 'suffixes': ['xfm'], 'extensions': ['.txt', '.h5'], - 'entities': _XFM_ENTITIES}, - {'datatypes': ['perf'], 'suffixes': ['xfm'], 'extensions': ['.txt', '.h5'], - 'entities': {**_PERF_ENTITIES, **_XFM_ENTITIES}}, - {'datatypes': ['pet'], 'suffixes': ['xfm'], 'extensions': ['.txt', '.h5'], - 'entities': {**_PET_ENTITIES, **_XFM_ENTITIES}}, + { + 'datatypes': ['anat', 'func', 'dwi'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': _XFM_ENTITIES, + }, + { + 'datatypes': ['perf'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': {**_PERF_ENTITIES, **_XFM_ENTITIES}, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['xfm'], + 'extensions': ['.txt', '.h5'], + 'entities': {**_PET_ENTITIES, **_XFM_ENTITIES}, + }, # --- Surface anatomy (anat + pet) --- - {'datatypes': ['anat'], 'suffixes': _SURFACE_SUFFIXES, - 'extensions': ['.surf.gii', '.shape.gii'], - 'entities': {'hemisphere': 'required'}}, - {'datatypes': ['anat'], 'suffixes': _SCALAR_SUFFIXES, - 'extensions': ['.dscalar.nii', '.json']}, + { + 'datatypes': ['anat'], + 'suffixes': _SURFACE_SUFFIXES, + 'extensions': ['.surf.gii', '.shape.gii'], + 'entities': {'hemisphere': 'required'}, + }, + {'datatypes': ['anat'], 'suffixes': _SCALAR_SUFFIXES, 'extensions': ['.dscalar.nii', '.json']}, {'datatypes': ['anat'], 'suffixes': ['morph'], 'extensions': ['.tsv', '.json']}, - {'datatypes': ['anat'], 'suffixes': ['mask'], - 'extensions': ['.label.gii', '.json'], - 'entities': {'hemisphere': 'required', 'description': 'required'}}, - {'datatypes': ['pet'], 'suffixes': _SURFACE_SUFFIXES, - 'extensions': ['.surf.gii', '.shape.gii'], - 'entities': {**_PET_ENTITIES, 'hemisphere': 'required'}}, - {'datatypes': ['pet'], 'suffixes': _SCALAR_SUFFIXES, - 'extensions': ['.dscalar.nii', '.json'], - 'entities': _PET_ENTITIES}, - {'datatypes': ['pet'], 'suffixes': ['morph'], 'extensions': ['.tsv', '.json'], - 'entities': {**_PET_ENTITIES, 'description': 'required'}}, + { + 'datatypes': ['anat'], + 'suffixes': ['mask'], + 'extensions': ['.label.gii', '.json'], + 'entities': {'hemisphere': 'required', 'description': 'required'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': _SURFACE_SUFFIXES, + 'extensions': ['.surf.gii', '.shape.gii'], + 'entities': {**_PET_ENTITIES, 'hemisphere': 'required'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': _SCALAR_SUFFIXES, + 'extensions': ['.dscalar.nii', '.json'], + 'entities': _PET_ENTITIES, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['morph'], + 'extensions': ['.tsv', '.json'], + 'entities': {**_PET_ENTITIES, 'description': 'required'}, + }, # --- CIFTI/surface extensions for schema suffixes --- - {'datatypes': ['func'], 'suffixes': ['bold', 'boldmap'], - 'extensions': _CIFTI_EXTENSIONS, - 'entities': {'hemisphere': 'optional', 'density': 'optional'}}, - {'datatypes': ['pet'], 'suffixes': ['pet'], - 'extensions': _CIFTI_EXTENSIONS, - 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}}, + { + 'datatypes': ['func'], + 'suffixes': ['bold', 'boldmap'], + 'extensions': _CIFTI_EXTENSIONS, + 'entities': {'hemisphere': 'optional', 'density': 'optional'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['pet'], + 'extensions': _CIFTI_EXTENSIONS, + 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}, + }, # --- anat --- - {'datatypes': ['anat'], 'suffixes': ['MTw', 'TSE'], - 'extensions': ['.nii', '.nii.gz', '.json']}, + { + 'datatypes': ['anat'], + 'suffixes': ['MTw', 'TSE'], + 'extensions': ['.nii', '.nii.gz', '.json'], + }, # --- func --- - {'datatypes': ['func'], 'suffixes': ['boldref', 'boldmap'], - 'extensions': ['.nii', '.nii.gz', '.json']}, - {'datatypes': ['func'], 'suffixes': ['AROMAnoiseICs'], - 'extensions': ['.csv', '.tsv']}, - {'datatypes': ['func'], 'suffixes': ['timeseries', 'regressors'], - 'extensions': ['.json', '.tsv']}, - {'datatypes': ['func'], 'suffixes': ['components', 'mixing'], - 'extensions': ['.json', '.tsv', '.nii', '.nii.gz']}, - {'datatypes': ['func'], 'suffixes': ['decomposition'], - 'extensions': ['.json']}, + { + 'datatypes': ['func'], + 'suffixes': ['boldref', 'boldmap'], + 'extensions': ['.nii', '.nii.gz', '.json'], + }, + {'datatypes': ['func'], 'suffixes': ['AROMAnoiseICs'], 'extensions': ['.csv', '.tsv']}, + { + 'datatypes': ['func'], + 'suffixes': ['timeseries', 'regressors'], + 'extensions': ['.json', '.tsv'], + }, + { + 'datatypes': ['func'], + 'suffixes': ['components', 'mixing'], + 'extensions': ['.json', '.tsv', '.nii', '.nii.gz'], + }, + {'datatypes': ['func'], 'suffixes': ['decomposition'], 'extensions': ['.json']}, # --- dwi --- - {'datatypes': ['dwi'], 'suffixes': ['dwiref', 'epiref', 'lowb'], - 'extensions': ['.nii', '.nii.gz', '.json']}, + { + 'datatypes': ['dwi'], + 'suffixes': ['dwiref', 'epiref', 'lowb'], + 'extensions': ['.nii', '.nii.gz', '.json'], + }, # --- perf --- - {'datatypes': ['perf'], 'suffixes': ['aslcontext'], - 'extensions': ['.tsv', '.json'], - 'entities': {'task': 'optional'}}, - {'datatypes': ['perf'], 'suffixes': ['timeseries'], - 'extensions': ['.json', '.tsv'], - 'entities': _PERF_ENTITIES}, - {'datatypes': ['perf'], - 'suffixes': ['asl', 'aslref', 'att', 'cbf', 'coverage', 'mask'], - 'extensions': ['.nii', '.nii.gz', '.json', '.tsv'], - 'entities': _PERF_ENTITIES}, + { + 'datatypes': ['perf'], + 'suffixes': ['aslcontext'], + 'extensions': ['.tsv', '.json'], + 'entities': {'task': 'optional'}, + }, + { + 'datatypes': ['perf'], + 'suffixes': ['timeseries'], + 'extensions': ['.json', '.tsv'], + 'entities': _PERF_ENTITIES, + }, + { + 'datatypes': ['perf'], + 'suffixes': ['asl', 'aslref', 'att', 'cbf', 'coverage', 'mask'], + 'extensions': ['.nii', '.nii.gz', '.json', '.tsv'], + 'entities': _PERF_ENTITIES, + }, # --- fmap --- - {'datatypes': ['fmap'], 'suffixes': ['fieldmap', 'mask'], - 'extensions': ['.nii', '.nii.gz', '.json'], - 'entities': {'fmapid': 'optional'}}, + { + 'datatypes': ['fmap'], + 'suffixes': ['fieldmap', 'mask'], + 'extensions': ['.nii', '.nii.gz', '.json'], + 'entities': {'fmapid': 'optional'}, + }, # --- pet --- - {'datatypes': ['pet'], 'suffixes': ['pet', 'petref'], - 'extensions': ['.nii', '.nii.gz', '.json'], - 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}}, - {'datatypes': ['pet'], 'suffixes': ['timeseries', 'regressors', 'tacs'], - 'extensions': ['.json', '.tsv'], - 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}}, + { + 'datatypes': ['pet'], + 'suffixes': ['pet', 'petref'], + 'extensions': ['.nii', '.nii.gz', '.json'], + 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}, + }, + { + 'datatypes': ['pet'], + 'suffixes': ['timeseries', 'regressors', 'tacs'], + 'extensions': ['.json', '.tsv'], + 'entities': {**_PET_ENTITIES, 'pvc': 'optional'}, + }, ] # Figure patterns use a non-standard directory layout (no session directory),