Skip to content

Commit fa9e716

Browse files
authored
Merge pull request #444 from effigies/fix/normalize-fmapids
RF: Add sanitized_id field to FieldmapEstimation
2 parents 57316e4 + fb55b65 commit fa9e716

File tree

5 files changed

+45
-8
lines changed

5 files changed

+45
-8
lines changed

sdcflows/fieldmaps.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ class FieldmapEstimation:
300300
bids_id = attr.ib(default=None, kw_only=True, type=str, on_setattr=_id_setter)
301301
"""The unique ``B0FieldIdentifier`` field of this fieldmap."""
302302

303+
sanitized_id = attr.ib(init=False, repr=False)
304+
"""Sanitized version of the bids_id with special characters replaced by underscores."""
305+
303306
_wf = attr.ib(init=False, default=None, repr=False)
304307
"""Internal pointer to a workflow."""
305308

@@ -436,6 +439,10 @@ def __attrs_post_init__(self):
436439
for intent_file in intents_meta:
437440
_intents[intent_file].add(self.bids_id)
438441

442+
# Provide a sanitized identifier that can be used in cases where
443+
# special characters are not allowed.
444+
self.sanitized_id = re.sub(r'[^a-zA-Z0-9]', '_', self.bids_id)
445+
439446
def paths(self):
440447
"""Return a tuple of paths that are sorted."""
441448
return tuple(sorted(str(f.path) for f in self.sources))
@@ -446,7 +453,7 @@ def get_workflow(self, set_inputs=True, **kwargs):
446453
return self._wf
447454

448455
# Override workflow name
449-
kwargs["name"] = f"wf_{self.bids_id}"
456+
kwargs["name"] = f"wf_{self.sanitized_id}"
450457

451458
if self.method in (EstimatorType.MAPPED, EstimatorType.PHASEDIFF):
452459
from .workflows.fit.fieldmap import init_fmap_wf

sdcflows/tests/test_fieldmaps.py

+28
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def test_FieldmapEstimation(dsA_dir, inputfiles, method, nsources, raises):
111111
assert fe.method == method
112112
assert len(fe.sources) == nsources
113113
assert fe.bids_id is not None and fe.bids_id.startswith("auto_")
114+
assert fe.bids_id == fe.sanitized_id # Auto-generated IDs are sanitized
114115

115116
# Attempt to change bids_id
116117
with pytest.raises(ValueError):
@@ -243,6 +244,33 @@ def test_FieldmapEstimationIdentifier(monkeypatch, dsA_dir):
243244

244245
fm.clear_registry()
245246

247+
fe = fm.FieldmapEstimation(
248+
[
249+
fm.FieldmapFile(
250+
dsA_dir / "sub-01" / "fmap/sub-01_fieldmap.nii.gz",
251+
metadata={
252+
"Units": "Hz",
253+
"B0FieldIdentifier": "fmap-with^special#chars",
254+
"IntendedFor": ["file1.nii.gz", "file2.nii.gz"],
255+
},
256+
),
257+
fm.FieldmapFile(
258+
dsA_dir / "sub-01" / "fmap/sub-01_magnitude.nii.gz",
259+
metadata={"Units": "Hz", "B0FieldIdentifier": "fmap-with^special#chars"},
260+
),
261+
]
262+
)
263+
assert fe.bids_id == "fmap-with^special#chars"
264+
assert fe.sanitized_id == "fmap_with_special_chars"
265+
# The unsanitized ID is used for lookups
266+
assert fm.get_identifier("file1.nii.gz") == ("fmap-with^special#chars",)
267+
assert fm.get_identifier("file2.nii.gz") == ("fmap-with^special#chars",)
268+
269+
wf = fe.get_workflow()
270+
assert wf.name == "wf_fmap_with_special_chars"
271+
272+
fm.clear_registry()
273+
246274

247275
def test_type_setter():
248276
"""Cover the _type_setter routine."""

sdcflows/workflows/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def init_fmap_preproc_wf(
124124
output_dir=str(output_dir),
125125
write_coeff=True,
126126
bids_fmap_id=estimator.bids_id,
127-
name=f"fmap_derivatives_wf_{estimator.bids_id}",
127+
name=f"fmap_derivatives_wf_{estimator.sanitized_id}",
128128
)
129129
fmap_derivatives_wf.inputs.inputnode.source_files = source_files
130130
fmap_derivatives_wf.inputs.inputnode.fmap_meta = [
@@ -135,15 +135,15 @@ def init_fmap_preproc_wf(
135135
output_dir=str(output_dir),
136136
fmap_type=str(estimator.method).rpartition(".")[-1].lower(),
137137
bids_fmap_id=estimator.bids_id,
138-
name=f"fmap_reports_wf_{estimator.bids_id}",
138+
name=f"fmap_reports_wf_{estimator.sanitized_id}",
139139
)
140140
fmap_reports_wf.inputs.inputnode.source_files = source_files
141141

142142
if estimator.method not in (EstimatorType.MAPPED, EstimatorType.PHASEDIFF):
143143
fields = INPUT_FIELDS[estimator.method]
144144
inputnode = pe.Node(
145145
niu.IdentityInterface(fields=fields),
146-
name=f"in_{estimator.bids_id}",
146+
name=f"in_{estimator.sanitized_id}",
147147
)
148148
# fmt:off
149149
workflow.connect([

sdcflows/workflows/fit/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def init_sdcflows_wf():
6161
output_dir=config.execution.output_dir,
6262
bids_fmap_id=estim.bids_id,
6363
write_coeff=True,
64-
name=f"fmap_derivatives_{estim.bids_id}",
64+
name=f"fmap_derivatives_{estim.sanitized_id}",
6565
)
6666

6767
source_paths = [
@@ -76,7 +76,7 @@ def init_sdcflows_wf():
7676
fmap_type=estim.method,
7777
output_dir=config.execution.output_dir,
7878
bids_fmap_id=estim.bids_id,
79-
name=f"fmap_reports_{estim.bids_id}",
79+
name=f"fmap_reports_{estim.sanitized_id}",
8080
)
8181
reportlets_wf.inputs.inputnode.source_files = source_paths
8282

sdcflows/workflows/outputs.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
# https://www.nipreps.org/community/licensing/
2222
#
2323
"""Writing out outputs."""
24+
import re
25+
2426
from nipype.pipeline import engine as pe
2527
from nipype.interfaces import utility as niu
2628
from niworkflows.interfaces.bids import DerivativesDataSink as _DDS
@@ -77,7 +79,7 @@ def init_fmap_reports_wf(
7779

7880
custom_entities = custom_entities or {}
7981
if bids_fmap_id:
80-
custom_entities["fmapid"] = bids_fmap_id.replace("_", "")
82+
custom_entities["fmapid"] = re.sub(r'[^a-zA-Z0-9]', '', bids_fmap_id)
8183

8284
workflow = pe.Workflow(name=name)
8385
inputnode = pe.Node(
@@ -156,7 +158,7 @@ def init_fmap_derivatives_wf(
156158
"""
157159
custom_entities = custom_entities or {}
158160
if bids_fmap_id:
159-
custom_entities["fmapid"] = bids_fmap_id.replace("_", "")
161+
custom_entities["fmapid"] = re.sub(r'[^a-zA-Z0-9]', '', bids_fmap_id)
160162

161163
workflow = pe.Workflow(name=name)
162164
inputnode = pe.Node(

0 commit comments

Comments
 (0)