Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalization of sidecarchanges using ids #213

Merged
merged 2 commits into from
Jun 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 30 additions & 37 deletions dcm2bids/acquisition.py
Original file line number Diff line number Diff line change
@@ -30,16 +30,13 @@ def __init__(
id=None,
srcSidecar=None,
sidecarChanges=None,
intendedFor=None,
IntendedFor=None,
**kwargs
):
self.logger = logging.getLogger(__name__)

self._modalityLabel = ""
self._customEntities = ""
self._id = ""
self._intendedFor = None

self.participant = participant
self.dataType = dataType
@@ -52,11 +49,6 @@ def __init__(
else:
self.sidecarChanges = sidecarChanges

if intendedFor is None:
self.intendedFor = IntendedFor
else:
self.intendedFor = intendedFor

if id is None:
self.id = None
else:
@@ -148,10 +140,10 @@ def dstRoot(self):
)

@property
def dstIntendedFor(self):
def dstId(self):
"""
Return:
The destination root inside the BIDS structure for intendedFor
The destination root inside the BIDS structure for descriptions with id
"""
return opj(
self.participant.session,
@@ -200,18 +192,7 @@ def setDstFile(self):

self.dstFile = new_name

@property
def intendedFor(self):
return self._intendedFor

@intendedFor.setter
def intendedFor(self, value):
if isinstance(value, list):
self._intendedFor = value
else:
self._intendedFor = [value]

def dstSidecarData(self, intendedForList):
def dstSidecarData(self, idList):
"""
"""
data = self.srcSidecar.origData
@@ -221,23 +202,35 @@ def dstSidecarData(self, intendedForList):
if 'TaskName' in self.srcSidecar.data:
data["TaskName"] = self.srcSidecar.data["TaskName"]

# intendedFor key
if self.intendedFor != [None]:
intendedValue = []

for index in self.intendedFor:
if index in intendedForList:
intendedValue = intendedValue + [intendedForList[index]]
else:
logging.warning(f"No id found for IntendedFor value '{index}'.")
logging.warning(f"No sidecar changes for field IntendedFor will be made for json file {self.dstFile}.json with this id.")
logging.warning("Check: https://unfmontreal.github.io/Dcm2Bids/docs/how-to/create-config-file/#id-and-intendedFor.\n")

data["IntendedFor"] = [item for sublist in intendedValue for item in sublist]

# sidecarChanges
for key, value in self.sidecarChanges.items():
data[key] = value
values = []

if not isinstance(value, list):
value = [value]

for val in value:
if isinstance(val, str):
if val not in idList and key in DEFAULT.keyWithPathSidecarChanges:
logging.warning(f"No id found for {key} value '{val}'.")
logging.warning(f"No sidecar changes for field {key} will be made for json file {self.dstFile}.json with this id.")
logging.warning(f"No sidecar changes for field {key} "
f"will be made "
f"for json file {self.dstFile}.json with this id.")
else:
values.append(idList.get(val, val))

# handle if nested list vs str
flat_value_list = []
for item in values:
if isinstance(item, list):
flat_value_list += item
else:
flat_value_list.append(item)
if len(flat_value_list) == 1:
flat_value_list = flat_value_list[0]

data[key] = flat_value_list

return data

18 changes: 9 additions & 9 deletions dcm2bids/dcm2bids_gen.py
Original file line number Diff line number Diff line change
@@ -137,9 +137,9 @@ def run(self):

self.logger.info("moving acquisitions into BIDS folder")

intendedForList = {}
idList = {}
for acq in parser.acquisitions:
intendedForList = self.move(acq, intendedForList)
idList = self.move(acq, idList)

if self.bids_validate:
try:
@@ -152,7 +152,7 @@ def run(self):
"The bids-validator may not been installed on your computer. "
"Please check: https://github.com/bids-standard/bids-validator#quickstart.")

def move(self, acquisition, intendedForList):
def move(self, acquisition, idList):
"""Move an acquisition to BIDS format"""
for srcFile in glob(acquisition.srcRoot + ".*"):
ext = Path(srcFile).suffixes
@@ -175,12 +175,12 @@ def move(self, acquisition, intendedForList):
self.logger.info("Use --clobber option to overwrite")
continue

# Populate intendedFor
# Populate idList
if '.nii' in ext:
if acquisition.id in intendedForList:
intendedForList[acquisition.id].append(acquisition.dstIntendedFor + "".join(ext))
if acquisition.id in idList:
idList[acquisition.id].append(acquisition.dstId + "".join(ext))
else:
intendedForList[acquisition.id] = [acquisition.dstIntendedFor + "".join(ext)]
idList[acquisition.id] = [acquisition.dstId + "".join(ext)]

if (self.config.get("defaceTpl") and acquisition.dataType == "anat" and ".nii" in ext):
try:
@@ -194,12 +194,12 @@ def move(self, acquisition, intendedForList):
run_shell_command(cmd)

elif ".json" in ext:
data = acquisition.dstSidecarData(intendedForList)
data = acquisition.dstSidecarData(idList)
save_json(dstFile, data)
os.remove(srcFile)

# just move
else:
os.rename(srcFile, dstFile)

return intendedForList
return idList
9 changes: 4 additions & 5 deletions dcm2bids/sidecar.py
Original file line number Diff line number Diff line change
@@ -217,8 +217,8 @@ def build_acquisitions(self, participant):
Returns:
A list of acquisition objects
"""
acquisitions_id = []
acquisitions = []
acquisitions_intendedFor = []

self.logger.info("Sidecars pairing:")
for sidecar, valid_descriptions in self.graph.items():
@@ -233,8 +233,8 @@ def build_acquisitions(self, participant):
srcSidecar=sidecar, **desc)
acq.setDstFile()

if acq.intendedFor != [None]:
acquisitions_intendedFor.append(acq)
if acq.id:
acquisitions_id.append(acq)
else:
acquisitions.append(acq)

@@ -252,7 +252,7 @@ def build_acquisitions(self, participant):
**desc)
self.logger.warning(" -> %s", acq.suffix)

self.acquisitions = acquisitions + acquisitions_intendedFor
self.acquisitions = acquisitions_id + acquisitions

return self.acquisitions

@@ -290,7 +290,6 @@ def searchDcmTagEntity(self, sidecar, desc):

if "customEntities" in desc.keys():
entities = set(concatenated_matches.keys()).union(set(descWithTask["customEntities"]))
#entities_left = set(concatenated_matches.keys()).symmetric_difference(set(descWithTask["customEntities"]))

if self.auto_extract_entities:
auto_acq = '_'.join([descWithTask['dataType'], descWithTask["modalityLabel"]])
4 changes: 3 additions & 1 deletion dcm2bids/utils/utils.py
Original file line number Diff line number Diff line change
@@ -69,6 +69,8 @@ class DEFAULT(object):
"run", "mod", "echo", "flip", "inv", "mt", "part",
"recording"]

keyWithPathSidecarChanges = ['IntendedFor', 'Sources']

# misc
tmpDirName = "tmp_dcm2bids"
helperDir = "helper"
@@ -119,7 +121,7 @@ def run_shell_command(commandLine, log=True):
"""
if log:
logger = logging.getLogger(__name__)
logger.info("Running: %s", " ".join(commandLine))
logger.info("Running: %s", " ".join(str(item) for item in commandLine))
return check_output(commandLine)


12 changes: 9 additions & 3 deletions tests/data/config_test.json
Original file line number Diff line number Diff line change
@@ -46,7 +46,9 @@
"EchoNumber": 1,
"EchoTime": 0.00492
},
"intendedFor": ["id_dwi", "T1", "dummy"]
"sidecarChanges": {
"IntendedFor": ["id_dwi", "T1", "dummy"]
}
},
{
"dataType": "fmap",
@@ -57,7 +59,9 @@
"EchoTime": 0.00738,
"ImageType": ["ORIGINAL", "PRIMARY", "M", "ND"]
},
"IntendedFor": "id_dwi"
"sidecarChanges": {
"IntendedFor": "id_dwi"
}
},
{
"dataType": "dwi",
@@ -66,7 +70,9 @@
"criteria": {
"SeriesDescription": "DTI_FA"
},
"IntendedFor": ["id_dwi", "T1"]
"sidecarChanges": {
"IntendedFor": ["id_dwi", "T1"]
}
},
{
"dataType": "dwi",
8 changes: 6 additions & 2 deletions tests/data/config_test_auto_extract.json
Original file line number Diff line number Diff line change
@@ -39,7 +39,9 @@
"criteria": {
"SeriesDescription": "gre_field_mapping"
},
"intendedFor": ["id_dwi", "T1", "dummy"]
"sidecarChanges": {
"IntendedFor": ["id_dwi", "T1", "dummy"]
}
},
{
"dataType": "dwi",
@@ -48,7 +50,9 @@
"criteria": {
"SeriesDescription": "DTI_FA"
},
"IntendedFor": ["id_dwi", "T1"]
"sidecarChanges": {
"IntendedFor": ["id_dwi", "T1"]
}
}
]
}
10 changes: 6 additions & 4 deletions tests/test_dcm2bids.py
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ def test_dcm2bids():
app = Dcm2BidsGen(TEST_DATA_DIR, "01",
os.path.join(TEST_DATA_DIR, "config_test.json"),
bidsDir.name)
app.run()
app.run()

fmapFile = os.path.join(bidsDir.name, "sub-01", "fmap", "sub-01_echo-492_fmap.json")
data = load_json(fmapFile)
@@ -65,7 +65,7 @@ def test_dcm2bids():
fmapFile = os.path.join(bidsDir.name, "sub-01", "fmap", "sub-01_echo-738_fmap.json")
data = load_json(fmapFile)
fmapMtime = os.stat(fmapFile).st_mtime
assert data["IntendedFor"] == [os.path.join("dwi", "sub-01_dwi.nii.gz")]
assert data["IntendedFor"] == os.path.join("dwi", "sub-01_dwi.nii.gz")

data = load_json(
os.path.join(
@@ -204,7 +204,7 @@ def test_caseSensitive_false():
fmapFile = os.path.join(bidsDir.name, "sub-01", "fmap", "sub-01_echo-738_fmap.json")
data = load_json(fmapFile)
fmapMtime = os.stat(fmapFile).st_mtime
assert data["IntendedFor"] == [os.path.join("dwi", "sub-01_dwi.nii.gz")]
assert data["IntendedFor"] == os.path.join("dwi", "sub-01_dwi.nii.gz")

data = load_json(
os.path.join(
@@ -255,7 +255,9 @@ def test_dcm2bids_auto_extract():
assert data["IntendedFor"] == [os.path.join("dwi", "sub-01_dwi.nii.gz"),
os.path.join("anat", "sub-01_T1w.nii")]

func_task = os.path.join(bidsDir.name, "sub-01", "func", "sub-01_task-rest_acq-highres_bold.json")
func_task = os.path.join(bidsDir.name, "sub-01",
"func",
"sub-01_task-rest_acq-highres_bold.json")
data = load_json(func_task)

assert os.path.exists(func_task)