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

[WIP] Fix Entity Table Order #157

Merged
merged 14 commits into from
Apr 16, 2022
12 changes: 6 additions & 6 deletions dcm2bids/dcm2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,16 @@ def run(self):

intendedForList = [[] for i in range(len(parser.descriptions))]
for acq in parser.acquisitions:
acq.setDstFile()
intendedForList = self.move(acq, intendedForList)

check_latest()
check_latest("dcm2niix")

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

_, ext = splitext_(srcFile)
dstFile = os.path.join(self.bidsDir, acquisition.dstRoot + ext)

# os.makedirs(os.path.dirname(dstFile), exist_ok=True)
# python2 compatibility
if not os.path.exists(os.path.dirname(dstFile)):
os.makedirs(os.path.dirname(dstFile))

Expand Down Expand Up @@ -196,11 +192,12 @@ def move(self, acquisition, intendedForList):
os.rename(srcFile, dstFile)

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

return intendedForList


def get_arguments():
"""Load arguments for main"""
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -298,6 +295,9 @@ def main():
)
return 1

check_latest()
check_latest("dcm2niix")

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

Expand Down
11 changes: 6 additions & 5 deletions dcm2bids/sidecar.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def build_graph(self):
for sidecar, description in possibleLinks:
criteria = description.get("criteria", None)
if criteria and self.isLink(sidecar.data, criteria):
graph[sidecar].append(description)
graph[sidecar].append(description)

self.graph = graph

Expand Down Expand Up @@ -189,7 +189,7 @@ def compare(name, pattern):
result = []

for tag, pattern in iteritems(criteria):
name = data.get(tag, '')# or ''
name = data.get(tag, '')

if isinstance(name, list):
try:
Expand Down Expand Up @@ -217,7 +217,6 @@ def build_acquisitions(self, participant):

self.logger.info("Sidecars pairing:")
for sidecar, valid_descriptions in iteritems(self.graph):
#for sidecar, descriptions in iteritems(self.graph):
sidecarName = os.path.basename(sidecar.root)

# only one description for the sidecar
Expand All @@ -226,6 +225,7 @@ def build_acquisitions(self, participant):
acq = Acquisition(participant,
srcSidecar=sidecar, **desc)
acq.indexSidecar = self.descriptions.index(desc)
acq.setDstFile()

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

self.acquisitions = acquisitions + acquisitions_intendedFor

return acquisitions + acquisitions_intendedFor
return self.acquisitions

def find_runs(self):
"""
Expand Down
55 changes: 49 additions & 6 deletions dcm2bids/structure.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-

"""k"""

"""Participant class"""

import logging
from os.path import join as opj
from future.utils import iteritems
from .utils import DEFAULT
Expand Down Expand Up @@ -121,6 +121,8 @@ def __init__(
IntendedFor=None,
**kwargs
):
self.logger = logging.getLogger(__name__)

self._modalityLabel = ""
self._customLabels = ""
self._intendedFor = None
Expand All @@ -142,6 +144,8 @@ def __init__(
else:
self.intendedFor = intendedFor

self.dstFile = ''

def __eq__(self, other):
return (
self.dataType == other.dataType
Expand Down Expand Up @@ -207,7 +211,7 @@ def dstRoot(self):
return opj(
self.participant.directory,
self.dataType,
self.participant.prefix + self.suffix,
self.dstFile,
)

@property
Expand All @@ -219,9 +223,50 @@ def dstIntendedFor(self):
return opj(
self.participant.session,
self.dataType,
self.participant.prefix + self.suffix,
self.dstFile,
)

def setDstFile(self):
"""
Return:
The destination filename formatted following the v1.7.0 BIDS entity key table
https://bids-specification.readthedocs.io/en/v1.7.0/99-appendices/04-entity-table.html
"""
current_name = self.participant.prefix + self.suffix
new_name = ''
current_dict = dict(x.split("-") for x in current_name.split("_") if len(x.split('-')) == 2)
suffix_list = [x for x in current_name.split("_") if len(x.split('-')) == 1]

for current_key in DEFAULT.entityTableKeys:
if current_key in current_dict and new_name != '':
new_name += f"_{current_key}-{current_dict[current_key]}"
elif current_key in current_dict:
new_name = f"{current_key}-{current_dict[current_key]}"
current_dict.pop(current_key, None)

for current_key in current_dict:
new_name += f"_{current_key}-{current_dict[current_key]}"

if current_dict:
self.logger.warning("Entity \"{}\"".format(list(current_dict.keys())) +
" is not a valid BIDS entity.")

new_name += f"_{'_'.join(suffix_list)}" # Allow multiple single key (without value)

if len(suffix_list) != 1:
self.logger.warning("There was more than one suffix found "
f"({suffix_list}). this is not BIDS "
"compliant. Make sure you know what"
"you are doing.")

if current_name != new_name:
self.logger.warning(
f"""✅ Filename was reordered according to BIDS entity table order:
from: {current_name}
to: {new_name}""")

self.dstFile = new_name

@property
def intendedFor(self):
return self._intendedFor
Expand Down Expand Up @@ -249,14 +294,12 @@ def indexSidecar(self, value):
"""
self._indexSidecar = value


def dstSidecarData(self, descriptions, intendedForList):
"""
"""
data = self.srcSidecar.origData
data["Dcm2bidsVersion"] = __version__


# intendedFor key
if self.intendedFor != [None]:
intendedValue = []
Expand Down
6 changes: 6 additions & 0 deletions dcm2bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class DEFAULT(object):
runTpl = "_run-{:02d}"
caseSensitive = True

# Entity table:
# https://bids-specification.readthedocs.io/en/v1.7.0/99-appendices/04-entity-table.html
entityTableKeys = ["sub", "ses", "task", "acq", "ce", "rec", "dir",
"run", "mod", "echo", "flip", "inv", "mt", "part",
"recording"]

# misc
tmpDirName = "tmp_dcm2bids"
helperDir = "helper"
Expand Down
3 changes: 1 addition & 2 deletions tests/test_dcm2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ def test_caseSensitive_false():
app.run()

layout = BIDSLayout(bidsDir.name,
validate=False,
ignore='tmp_dcm2bids')
validate=False)

path_dwi = os.path.join(bidsDir.name,
"sub-01",
Expand Down
1 change: 1 addition & 0 deletions tests/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@
def test_acquisition_get_dst_path(name, session, modality, custom, expected):
participant = Participant(name, session)
acquisition = Acquisition(participant, "anat", modality, customLabels=custom)
acquisition.setDstFile()
assert acquisition.dstRoot == expected