diff --git a/.gitignore b/.gitignore index 846dc44..67e5eb4 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,7 @@ dmypy.json # Pycharm .idea + + +# VSCode +.vscode diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..7d742bb --- /dev/null +++ b/conftest.py @@ -0,0 +1,14 @@ +import os +import pytest + +# For debugging in IDE's don't catch raised exceptions and let the IDE +# break at it +if os.getenv("_PYTEST_RAISE", "0") != "0": + + @pytest.hookimpl(tryfirst=True) + def pytest_exception_interact(call): + raise call.excinfo.value + + @pytest.hookimpl(tryfirst=True) + def pytest_internalerror(excinfo): + raise excinfo.value diff --git a/pydra/tasks/dcm2niix/tests/test_utils.py b/pydra/tasks/dcm2niix/tests/test_utils.py new file mode 100644 index 0000000..abe9ca1 --- /dev/null +++ b/pydra/tasks/dcm2niix/tests/test_utils.py @@ -0,0 +1,11 @@ +from pydra.tasks.dcm2niix.utils import Dcm2Niix + + +def test_dcm2niix(): + task = Dcm2Niix() + task.inputs.in_dir = "test-data/test_dicoms" + task.inputs.out_dir = "test-data" + task.inputs.compress = "y" + assert ( + task.cmdline == "dcm2niix -o test-data -f out_file -z y test-data/test_dicoms" + ) diff --git a/pydra/tasks/dcm2niix/utils.py b/pydra/tasks/dcm2niix/utils.py index 5cbe616..c643b33 100644 --- a/pydra/tasks/dcm2niix/utils.py +++ b/pydra/tasks/dcm2niix/utils.py @@ -1,8 +1,34 @@ -import typing as ty +import os.path from pydra import ShellCommandTask from pydra.engine.specs import ShellSpec, ShellOutSpec, File, Directory, SpecInfo +def dcm2niix_out_file(out_dir, filename, echo, compress): + # Append echo number of NIfTI echo to select is provided + if echo: + echo_suffix = f"_e{echo}" + else: + echo_suffix = "" + + out_file = f"{out_dir}/{filename}{echo_suffix}.nii" + + # If compressed, append the zip extension + if compress in ("y", "o", "i"): + out_file += ".gz" + + return os.path.abspath(out_file) + + +def dcm2niix_out_json(out_dir, filename, echo): + # Append echo number of NIfTI echo to select is provided + if echo: + echo_suffix = f"_e{echo}" + else: + echo_suffix = "" + + return os.path.abspath(f"{out_dir}/{filename}{echo_suffix}.json") + + input_fields = [ ( "in_dir", @@ -16,7 +42,7 @@ ), ( "out_dir", - str, + Directory, { "argstr": "-o '{out_dir}'", "help_string": "output directory", @@ -29,6 +55,18 @@ "out_file", {"argstr": "-f '{filename}'", "help_string": "The output name for the file"}, ), + ( + "echo", + int, + { + "argstr": "", + "help_string": ( + "The echo number to extract from the DICOM dataset. When multiple " + "echoes are discovered in the dataset then dcm2niix will create " + "separate files for each echo with the suffix '_e.nii'" + ), + }, + ), ( "compress", str, @@ -279,7 +317,8 @@ File, { "help_string": "output NIfTI image", - "output_file_template": "{out_dir}/{filename}.nii.gz", + "callable": dcm2niix_out_file, + "mandatory": True, }, ), ( @@ -287,7 +326,8 @@ File, { "help_string": "output BIDS side-car JSON", - "output_file_template": "{out_dir}/{filename}.json", + # "requires": [("bids", 'y')], FIXME: should be either 'y' or 'o' + "callable": dcm2niix_out_json, }, ), ( @@ -319,10 +359,10 @@ class Dcm2Niix(ShellCommandTask): ------- >>> task = Dcm2Niix() >>> task.inputs.in_dir = "test-data/test_dicoms" - >>> task.inputs.out_dir = "test-data/output" + >>> task.inputs.out_dir = "test-data" >>> task.inputs.compress = "y" >>> task.cmdline - "dcm2niix -o 'test-data/output' -f 'out_file' -z y 'test-data/test_dicoms'" + 'dcm2niix -o test-data -f out_file -z y test-data/test_dicoms' """ input_spec = Dcm2NiixInputSpec diff --git a/setup.cfg b/setup.cfg index 8245b24..9eef945 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ classifiers = [options] python_requires = >=3.7 install_requires = - pydra >= 0.6.2 + pydra >= 0.19 packages = find_namespace: [options.packages.find]