Skip to content

Commit

Permalink
Merge pull request #444 from effigies/fix/normalize-fmapids
Browse files Browse the repository at this point in the history
RF: Add sanitized_id field to FieldmapEstimation
  • Loading branch information
effigies committed Jun 6, 2024
2 parents 57316e4 + fb55b65 commit fa9e716
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 8 deletions.
9 changes: 8 additions & 1 deletion sdcflows/fieldmaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ class FieldmapEstimation:
bids_id = attr.ib(default=None, kw_only=True, type=str, on_setattr=_id_setter)
"""The unique ``B0FieldIdentifier`` field of this fieldmap."""

sanitized_id = attr.ib(init=False, repr=False)
"""Sanitized version of the bids_id with special characters replaced by underscores."""

_wf = attr.ib(init=False, default=None, repr=False)
"""Internal pointer to a workflow."""

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

# Provide a sanitized identifier that can be used in cases where
# special characters are not allowed.
self.sanitized_id = re.sub(r'[^a-zA-Z0-9]', '_', self.bids_id)

def paths(self):
"""Return a tuple of paths that are sorted."""
return tuple(sorted(str(f.path) for f in self.sources))
Expand All @@ -446,7 +453,7 @@ def get_workflow(self, set_inputs=True, **kwargs):
return self._wf

# Override workflow name
kwargs["name"] = f"wf_{self.bids_id}"
kwargs["name"] = f"wf_{self.sanitized_id}"

if self.method in (EstimatorType.MAPPED, EstimatorType.PHASEDIFF):
from .workflows.fit.fieldmap import init_fmap_wf
Expand Down
28 changes: 28 additions & 0 deletions sdcflows/tests/test_fieldmaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def test_FieldmapEstimation(dsA_dir, inputfiles, method, nsources, raises):
assert fe.method == method
assert len(fe.sources) == nsources
assert fe.bids_id is not None and fe.bids_id.startswith("auto_")
assert fe.bids_id == fe.sanitized_id # Auto-generated IDs are sanitized

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

fm.clear_registry()

fe = fm.FieldmapEstimation(
[
fm.FieldmapFile(
dsA_dir / "sub-01" / "fmap/sub-01_fieldmap.nii.gz",
metadata={
"Units": "Hz",
"B0FieldIdentifier": "fmap-with^special#chars",
"IntendedFor": ["file1.nii.gz", "file2.nii.gz"],
},
),
fm.FieldmapFile(
dsA_dir / "sub-01" / "fmap/sub-01_magnitude.nii.gz",
metadata={"Units": "Hz", "B0FieldIdentifier": "fmap-with^special#chars"},
),
]
)
assert fe.bids_id == "fmap-with^special#chars"
assert fe.sanitized_id == "fmap_with_special_chars"
# The unsanitized ID is used for lookups
assert fm.get_identifier("file1.nii.gz") == ("fmap-with^special#chars",)
assert fm.get_identifier("file2.nii.gz") == ("fmap-with^special#chars",)

wf = fe.get_workflow()
assert wf.name == "wf_fmap_with_special_chars"

fm.clear_registry()


def test_type_setter():
"""Cover the _type_setter routine."""
Expand Down
6 changes: 3 additions & 3 deletions sdcflows/workflows/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def init_fmap_preproc_wf(
output_dir=str(output_dir),
write_coeff=True,
bids_fmap_id=estimator.bids_id,
name=f"fmap_derivatives_wf_{estimator.bids_id}",
name=f"fmap_derivatives_wf_{estimator.sanitized_id}",
)
fmap_derivatives_wf.inputs.inputnode.source_files = source_files
fmap_derivatives_wf.inputs.inputnode.fmap_meta = [
Expand All @@ -135,15 +135,15 @@ def init_fmap_preproc_wf(
output_dir=str(output_dir),
fmap_type=str(estimator.method).rpartition(".")[-1].lower(),
bids_fmap_id=estimator.bids_id,
name=f"fmap_reports_wf_{estimator.bids_id}",
name=f"fmap_reports_wf_{estimator.sanitized_id}",
)
fmap_reports_wf.inputs.inputnode.source_files = source_files

if estimator.method not in (EstimatorType.MAPPED, EstimatorType.PHASEDIFF):
fields = INPUT_FIELDS[estimator.method]
inputnode = pe.Node(
niu.IdentityInterface(fields=fields),
name=f"in_{estimator.bids_id}",
name=f"in_{estimator.sanitized_id}",
)
# fmt:off
workflow.connect([
Expand Down
4 changes: 2 additions & 2 deletions sdcflows/workflows/fit/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def init_sdcflows_wf():
output_dir=config.execution.output_dir,
bids_fmap_id=estim.bids_id,
write_coeff=True,
name=f"fmap_derivatives_{estim.bids_id}",
name=f"fmap_derivatives_{estim.sanitized_id}",
)

source_paths = [
Expand All @@ -76,7 +76,7 @@ def init_sdcflows_wf():
fmap_type=estim.method,
output_dir=config.execution.output_dir,
bids_fmap_id=estim.bids_id,
name=f"fmap_reports_{estim.bids_id}",
name=f"fmap_reports_{estim.sanitized_id}",
)
reportlets_wf.inputs.inputnode.source_files = source_paths

Expand Down
6 changes: 4 additions & 2 deletions sdcflows/workflows/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
# https://www.nipreps.org/community/licensing/
#
"""Writing out outputs."""
import re

from nipype.pipeline import engine as pe
from nipype.interfaces import utility as niu
from niworkflows.interfaces.bids import DerivativesDataSink as _DDS
Expand Down Expand Up @@ -77,7 +79,7 @@ def init_fmap_reports_wf(

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

workflow = pe.Workflow(name=name)
inputnode = pe.Node(
Expand Down Expand Up @@ -156,7 +158,7 @@ def init_fmap_derivatives_wf(
"""
custom_entities = custom_entities or {}
if bids_fmap_id:
custom_entities["fmapid"] = bids_fmap_id.replace("_", "")
custom_entities["fmapid"] = re.sub(r'[^a-zA-Z0-9]', '', bids_fmap_id)

workflow = pe.Workflow(name=name)
inputnode = pe.Node(
Expand Down

0 comments on commit fa9e716

Please sign in to comment.