Skip to content

Commit

Permalink
Merge pull request #207 from arnaudbore/add_feature_intended_for
Browse files Browse the repository at this point in the history
Upgrade feature Intended for
  • Loading branch information
arnaudbore authored Jun 12, 2023
2 parents 803848e + 9e8eb90 commit d823293
Show file tree
Hide file tree
Showing 12 changed files with 72 additions and 68 deletions.
53 changes: 28 additions & 25 deletions dcm2bids/acquisition.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def __init__(
participant,
dataType,
modalityLabel,
indexSidecar=None,
customLabels="",
id=None,
srcSidecar=None,
sidecarChanges=None,
intendedFor=None,
Expand All @@ -38,8 +38,8 @@ def __init__(

self._modalityLabel = ""
self._customLabels = ""
self._id = ""
self._intendedFor = None
self._indexSidecar = None

self.participant = participant
self.dataType = dataType
Expand All @@ -57,6 +57,11 @@ def __init__(
else:
self.intendedFor = intendedFor

if id is None:
self.id = None
else:
self.id = id

self.dstFile = ''

def __eq__(self, other):
Expand All @@ -79,6 +84,18 @@ def modalityLabel(self, modalityLabel):
""" Prepend '_' if necessary"""
self._modalityLabel = self.prepend(modalityLabel)

@property
def id(self):
"""
Returns:
A string '_<id>'
"""
return self._id

@id.setter
def id(self, value):
self._id = value

@property
def customLabels(self):
"""
Expand Down Expand Up @@ -191,23 +208,7 @@ def intendedFor(self, value):
else:
self._intendedFor = [value]

@property
def indexSidecar(self):
"""
Returns:
A int '_<indexSidecar>'
"""
return self._indexSidecar

@indexSidecar.setter
def indexSidecar(self, value):
"""
Returns:
A int '_<indexSidecar>'
"""
self._indexSidecar = value

def dstSidecarData(self, descriptions, intendedForList):
def dstSidecarData(self, intendedForList):
"""
"""
data = self.srcSidecar.origData
Expand All @@ -218,12 +219,14 @@ def dstSidecarData(self, descriptions, intendedForList):
intendedValue = []

for index in self.intendedFor:
intendedValue = intendedValue + intendedForList[index]

if len(intendedValue) == 1:
data["IntendedFor"] = intendedValue[0]
else:
data["IntendedFor"] = intendedValue
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():
Expand Down
5 changes: 1 addition & 4 deletions dcm2bids/cli/dcm2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import argparse

from dcm2bids.dcm2bids_gen import Dcm2BidsGen
from dcm2bids.utils.tools import check_latest
from dcm2bids.utils.utils import DEFAULT
from dcm2bids.version import __version__


def _build_arg_parser():
p = argparse.ArgumentParser(description=__doc__, epilog=DEFAULT.doc,
formatter_class=argparse.RawTextHelpFormatter)
Expand Down Expand Up @@ -71,9 +71,6 @@ def main():
parser = _build_arg_parser()
args = parser.parse_args()

check_latest()
check_latest("dcm2niix")

app = Dcm2BidsGen(**vars(args))
return app.run()

Expand Down
28 changes: 13 additions & 15 deletions dcm2bids/dcm2bids_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
Reorganising NIfTI files from dcm2niix into the Brain Imaging Data Structure
"""

import argparse
import logging
import os
from pathlib import Path
Expand All @@ -22,6 +21,7 @@
from dcm2bids.utils.tools import check_latest, dcm2niix_version
from dcm2bids.version import __version__


class Dcm2BidsGen(object):
""" Object to handle dcm2bids execution steps
Expand Down Expand Up @@ -75,7 +75,6 @@ def __init__(
self.logger.info("BIDS directory: %s", os.path.realpath(output_dir))
self.logger.info("Validate BIDS: %s", self.bids_validate)


@property
def dicomDirs(self):
"""List of DICOMs directories"""
Expand Down Expand Up @@ -133,7 +132,7 @@ def run(self):

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

intendedForList = [[] for i in range(len(parser.descriptions))]
intendedForList = {}
for acq in parser.acquisitions:
acq.setDstFile()
intendedForList = self.move(acq, intendedForList)
Expand All @@ -147,12 +146,11 @@ def run(self):
except:
self.logger.info("The bids-validator does not seem to work properly. "
"The bids-validator may not been installed on your computer. "
f"Please check: https://github.com/bids-standard/bids-validator#quickstart.")
"Please check: https://github.com/bids-standard/bids-validator#quickstart.")

def move(self, acquisition, intendedForList):
"""Move an acquisition to BIDS format"""
for srcFile in glob(acquisition.srcRoot + ".*"):

ext = Path(srcFile).suffixes
ext = [curr_ext for curr_ext in ext if curr_ext in ['.nii', '.gz',
'.json',
Expand All @@ -173,8 +171,15 @@ def move(self, acquisition, intendedForList):
self.logger.info("Use --clobber option to overwrite")
continue

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

# it's an anat nifti file and the user using a deface script
if (self.config.get("defaceTpl") and acquisition.dataType == "func" and ".nii" in ext):
if (self.config.get("defaceTpl") and acquisition.dataType == "anat" and ".nii" in ext):
try:
os.remove(dstFile)
except FileNotFoundError:
Expand All @@ -185,20 +190,13 @@ def move(self, acquisition, intendedForList):
cmd = [w.replace('dstFile', dstFile) for w in defaceTpl]
run_shell_command(cmd)

intendedForList[acquisition.indexSidecar].append(acquisition.dstIntendedFor + "".join(ext))

elif ".json" in ext:
data = acquisition.dstSidecarData(self.config["descriptions"],
intendedForList)
data = acquisition.dstSidecarData(intendedForList)
save_json(dstFile, data)
os.remove(srcFile)

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

intendedFile = acquisition.dstIntendedFor + ".nii.gz"
if intendedFile not in intendedForList[acquisition.indexSidecar]:
intendedForList[acquisition.indexSidecar].append(intendedFile)

return intendedForList
return intendedForList
3 changes: 1 addition & 2 deletions dcm2bids/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from os.path import join as opj

from dcm2bids.utils.utils import DEFAULT
from dcm2bids.version import __version__


class Participant(object):
Expand Down Expand Up @@ -92,4 +91,4 @@ def hasSession(self):
Returns:
Boolean
"""
return self.session.strip() != DEFAULT.session
return self.session.strip() != DEFAULT.session
2 changes: 0 additions & 2 deletions dcm2bids/sidecar.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ def build_acquisitions(self, participant):
desc = valid_descriptions[0]
acq = Acquisition(participant,
srcSidecar=sidecar, **desc)
acq.indexSidecar = self.descriptions.index(desc)
acq.setDstFile()

if acq.intendedFor != [None]:
Expand All @@ -245,7 +244,6 @@ def build_acquisitions(self, participant):
self.logger.warning("Several Pairing <- %s", sidecarName)
for desc in valid_descriptions:
acq = Acquisition(participant,
indexSidecar=self.descriptions.index(desc),
**desc)
self.logger.warning(" -> %s", acq.suffix)

Expand Down
1 change: 1 addition & 0 deletions dcm2bids/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class DEFAULT(object):
doc = "Documentation at https://unfmontreal.github.io/Dcm2Bids/"

link_bids_validator = "https://github.com/bids-standard/bids-validator#quickstart"
link_doc_intended_for = "https://unfmontreal.github.io/Dcm2Bids/docs/tutorial/first-steps/#populating-the-config-file"

# cli dcm2bids
cliSession = ""
Expand Down
19 changes: 10 additions & 9 deletions docs/how-to/create-config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
}
},
{
"id": "task-rest",
"dataType": "func",
"modalityLabel": "bold",
"customLabels": "task-rest",
Expand All @@ -28,12 +29,13 @@
{
"dataType": "fmap",
"modalityLabel": "fmap",
"intendedFor": 1,
"intendedFor": "task_rest",
"criteria": {
"ProtocolName": "*field_mapping*"
}
},
{
"id": "id_task_learning",
"dataType": "func",
"modalityLabel": "bold",
"customLabels": "task-learning",
Expand All @@ -50,7 +52,7 @@
"criteria": {
"SeriesDescription": "fmap_task-learning"
},
"IntendedFor": 2,
"IntendedFor": "id_task_learning",
"sidecarChanges": {
"TaskName": "learning"
}
Expand Down Expand Up @@ -146,15 +148,14 @@ WARNING:dcm2bids.structure:✅ Filename was reordered according to BIDS entity t

Optional field to change or add information in a sidecar.

## intendedFor
## id and intendedFor

Optional field to add an `IntendedFor` entry in the sidecar of a fieldmap. Just
put the index or a list of indices of the description(s) that's intended for.
Optional field to add an `IntendedFor` entry in the sidecar of a fieldmap.
You will need to set an id to the corresponding description and put the same id in the `IntendedFor` field.

Python index begins at `0` so in the example, **`1`** means it is intended for
`task-rest_bold` and **`2`** is intended for `task-learning` which will be
renamed to only `learning` because of the
`"sidecarChanges": { "TaskName": "learning" }` field.
Fo example, **`task_rest`** means it is intended for `task-rest_bold`
and **`id_task_learning`** is intended for `task-learning` which will be
renamed to only `learning` because of the `"sidecarChanges": { "TaskName": "learning" }` field.

## Multiple config files

Expand Down
13 changes: 7 additions & 6 deletions docs/tutorial/first-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ good unique identifier.
=== "Command"

```sh
cat code/dcm2bids_config.json
cat tmp_dcm2bids/helper/004_In_DCM2NIIX_regression_test_20180918114023.json
```

=== "Output"
Expand Down Expand Up @@ -807,11 +807,11 @@ task name:
"modalityLabel": "bold",
"customLabels": "task-rest",
"criteria": {
"SeriesDescription": "Axial EPI-FMRI (Interleaved I to S)*",
"sidecarChanges": {
"SeriesDescription": "Axial EPI-FMRI (Interleaved I to S)*"
},
"sidecarChanges": {
"TaskName": "rest"
}
}
}
]
}
Expand Down Expand Up @@ -887,6 +887,7 @@ file with the appropriate info.
{
"descriptions": [
{
"id": "id_task-rest",
"dataType": "func",
"modalityLabel": "bold",
"customLabels": "task-rest",
Expand All @@ -904,7 +905,7 @@ file with the appropriate info.
"criteria": {
"SeriesDescription": "EPI PE=AP*"
},
"intendedFor": 0
"intendedFor": "id_task-rest"
},
{
"dataType": "fmap",
Expand All @@ -913,7 +914,7 @@ file with the appropriate info.
"criteria": {
"SeriesDescription": "EPI PE=PA*"
},
"intendedFor": 0
"intendedFor": "id_task-rest"
}
]
}
Expand Down
Loading

0 comments on commit d823293

Please sign in to comment.