Skip to content

Commit aaccd9b

Browse files
committed
perf: stop renaming M0 scans; keep aslcontext rename and update M0 IntendedFor; add regression test
1 parent 0be1b9a commit aaccd9b

File tree

2 files changed

+85
-13
lines changed

2 files changed

+85
-13
lines changed

cubids/cubids.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -692,19 +692,10 @@ def change_filename(self, filepath, entities):
692692
new_context = new_path.replace(new_scan_end, "_aslcontext.tsv")
693693
self.new_filenames.append(new_context)
694694

695-
old_m0scan = filepath.replace(scan_end, "_m0scan.nii.gz")
696-
if Path(old_m0scan).exists():
697-
self.old_filenames.append(old_m0scan)
698-
new_scan_end = "_" + suffix + old_ext
699-
new_m0scan = new_path.replace(new_scan_end, "_m0scan.nii.gz")
700-
self.new_filenames.append(new_m0scan)
701-
702-
old_mjson = filepath.replace(scan_end, "_m0scan.json")
703-
if Path(old_mjson).exists():
704-
self.old_filenames.append(old_mjson)
705-
new_scan_end = "_" + suffix + old_ext
706-
new_mjson = new_path.replace(new_scan_end, "_m0scan.json")
707-
self.new_filenames.append(new_mjson)
695+
# Do NOT rename M0 scans or their JSON sidecars. M0 files should
696+
# retain their original filenames to preserve independent variability.
697+
# The IntendedFor field in M0 JSONs will be updated below to point
698+
# to the newly renamed ASL files.
708699

709700
old_labeling = filepath.replace(scan_end, "_asllabeling.jpg")
710701
if Path(old_labeling).exists():

cubids/tests/test_perf_m0.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""Tests for ASL/M0 renaming behavior.
2+
3+
Ensures that when ASL scans are renamed with variant acquisition labels:
4+
- aslcontext files are renamed to match the ASL scan
5+
- M0 files (nii/json) are NOT renamed
6+
- M0 JSON IntendedFor entries are updated to point to the new ASL path
7+
"""
8+
9+
import json
10+
from pathlib import Path
11+
12+
from cubids.cubids import CuBIDS
13+
14+
15+
def _write(path: Path, content: str = ""):
16+
path.parent.mkdir(parents=True, exist_ok=True)
17+
path.write_text(content)
18+
19+
20+
def test_m0_not_renamed_but_aslcontext_is_and_intendedfor_updated(tmp_path):
21+
bids_root = tmp_path / "bids"
22+
sub = "sub-01"
23+
ses = "ses-01"
24+
25+
# Create minimal perf files
26+
perf_dir = bids_root / sub / ses / "perf"
27+
perf_dir.mkdir(parents=True, exist_ok=True)
28+
29+
asl_base = perf_dir / f"{sub}_{ses}_asl.nii.gz"
30+
asl_json = perf_dir / f"{sub}_{ses}_asl.json"
31+
m0_base = perf_dir / f"{sub}_{ses}_m0scan.nii.gz"
32+
m0_json = perf_dir / f"{sub}_{ses}_m0scan.json"
33+
aslcontext = perf_dir / f"{sub}_{ses}_aslcontext.tsv"
34+
35+
# Touch NIfTIs (empty is fine for this test) and sidecars
36+
asl_base.write_bytes(b"")
37+
m0_base.write_bytes(b"")
38+
39+
_write(asl_json, json.dumps({}))
40+
41+
# M0 IntendedFor should reference the ASL time series (participant-relative path)
42+
intended_for_rel = f"{ses}/perf/{sub}_{ses}_asl.nii.gz"
43+
_write(m0_json, json.dumps({"IntendedFor": [intended_for_rel]}))
44+
45+
_write(aslcontext, "label\ncontrol\nlabel\ncontrol\n")
46+
47+
c = CuBIDS(str(bids_root))
48+
49+
# Rename the ASL scan by adding a variant acquisition
50+
entities = {"suffix": "asl", "acquisition": "VARIANTTest"}
51+
c.change_filename(str(asl_base), entities)
52+
53+
# Old/new filenames prepared for ASL and aslcontext, but NOT for M0
54+
assert str(asl_base) in c.old_filenames
55+
assert any(fn.endswith("_asl.json") for fn in c.old_filenames)
56+
assert any(fn.endswith("_aslcontext.tsv") for fn in c.old_filenames)
57+
58+
assert not any(fn.endswith("_m0scan.nii.gz") for fn in c.old_filenames)
59+
assert not any(fn.endswith("_m0scan.json") for fn in c.old_filenames)
60+
61+
# Compute expected new ASL path and aslcontext path
62+
expected_new_asl = perf_dir / f"{sub}_{ses}_acq-VARIANTTest_asl.nii.gz"
63+
expected_new_aslcontext = perf_dir / f"{sub}_{ses}_acq-VARIANTTest_aslcontext.tsv"
64+
65+
assert str(expected_new_asl) in c.new_filenames
66+
assert str(expected_new_aslcontext) in c.new_filenames
67+
68+
# M0 files remain with original names
69+
assert m0_base.exists()
70+
assert m0_json.exists()
71+
72+
# But M0 IntendedFor should now point to the new ASL relative path
73+
with open(m0_json, "r") as f:
74+
m0_meta = json.load(f)
75+
76+
new_rel = f"{ses}/perf/{sub}_{ses}_acq-VARIANTTest_asl.nii.gz"
77+
assert "IntendedFor" in m0_meta
78+
assert new_rel in m0_meta["IntendedFor"]
79+
# Ensure old reference removed
80+
assert intended_for_rel not in m0_meta["IntendedFor"]
81+

0 commit comments

Comments
 (0)