Skip to content

Commit

Permalink
feat(dicomsort): add command line script to build
Browse files Browse the repository at this point in the history
  • Loading branch information
jjjermiah committed Jan 29, 2024
1 parent ab50dac commit bb0e687
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 22 deletions.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ authors = ["Jermiah Joseph"]
license = "MIT"
readme = "README.md"

[tool.poetry.scripts]
DICOMSorter = "nbiatoolkit.dicomsort:DICOMSorter_cli"

[tool.poetry.dependencies]
python = ">=3.11 || 3.12"
requests = "2.31.0"
Expand Down
4 changes: 2 additions & 2 deletions src/nbiatoolkit/dicomsort/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# "validateMD5"
# ]

from .dicomsort import DICOMSorter
from .dicomsort import DICOMSorter, DICOMSorter_cli
from .helper_functions import parseDICOMKeysFromFormat, sanitizeFileName, truncateUID

__all__ = ["DICOMSorter", "parseDICOMKeysFromFormat", "sanitizeFileName", "truncateUID"]
__all__ = ["DICOMSorter", "parseDICOMKeysFromFormat", "sanitizeFileName", "truncateUID", "DICOMSorter_cli"]
104 changes: 84 additions & 20 deletions src/nbiatoolkit/dicomsort/dicomsort.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import re, os, sys, shutil
import pydicom
import argparse

from pydicom.errors import InvalidDicomError

from .helper_functions import parseDICOMKeysFromFormat, sanitizeFileName, truncateUID

from typing import Optional

from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm

class DICOMSorter:
def __init__(
Expand Down Expand Up @@ -92,16 +95,19 @@ def sortSingleDICOMFile(

return True

def sortDICOMFiles(self, option: str = "copy", overwrite: bool = False) -> bool:
def sortDICOMFiles(self, option: str = "copy", overwrite: bool = False, nParallel: int = 1) -> bool:
dicom_file_paths = self._get_dicom_files()
result = []
print("Running with {} parallel threads".format(nParallel))

results = [
self.sortSingleDICOMFile(file, option, overwrite)
for file in dicom_file_paths
]
with ThreadPoolExecutor(max_workers=nParallel) as executor:
futures = [executor.submit(self.sortSingleDICOMFile, filePath, option, overwrite) for filePath in dicom_file_paths]

return all(results)
for future in tqdm(as_completed(futures), total=len(futures)):
result.append(future.result())

return all(result)

def _get_dicom_files(self) -> 'list[str]':
dicom_file_paths = []
# Iterate over all files in the source directory
Expand All @@ -113,17 +119,75 @@ def _get_dicom_files(self) -> 'list[str]':

return dicom_file_paths


# Test case
# if __name__ == "__main__":

# sorter = DICOMSorter(
# sourceDir = sourceDir,
# destinationDir=destinationDir,
# targetPattern=pattern,
# truncateUID=True,
# sanitizeFilename=True,
# overwrite=True
# )

# sorter.sortDICOMFiles(option="move")
# Create command line interface

# Given a source directory, destination directory, and target pattern, sort DICOM files
# into the destination directory according to the target pattern.
# The target pattern is a string with placeholders matching '%<DICOMKey>'.

def DICOMSorter_cli():
parser = argparse.ArgumentParser(
description="Sort DICOM files into destination directory according to target pattern."
)

parser.add_argument("sourceDir",
metavar="sourceDir",
type=str,
help="The source directory containing DICOM files.",
)

parser.add_argument("destinationDir",
metavar="destinationDir",
type=str,
help="The destination directory to sort DICOM files into.",
)

# Default is %%PatientName/%%SeriesNumber-%%SeriesInstanceUID/%%InstanceNumber.dcm
parser.add_argument("--targetPattern",
dest="targetPattern",
default="%PatientName/%SeriesNumber-%SeriesInstanceUID/%InstanceNumber.dcm",
type=str,
help='The target pattern for sorting DICOM files. Default is %%PatientName/%%SeriesNumber-%%SeriesInstanceUID/%%InstanceNumber.dcm.',
)

parser.add_argument("--truncateUID",
dest="truncateUID",
action="store_true",
default=True,
help="Truncate the UID to the last n characters (includes periods & underscores). Default is True.",
)

parser.add_argument("--sanitizeFilename",
dest="sanitizeFilename",
action="store_true",
help="Sanitize the file name by replacing potentially dangerous characters. Default is True.",
)

parser.add_argument("--overwrite",
dest="overwrite",
action="store_true",
help="Overwrite existing files. Default is False.",
)

parser.add_argument("--nParallel",
dest="nParallel",
action="store",
type=int,
help="Number of parallel threads. Default is 1.",)

parser.set_defaults(truncateUID=True)
parser.set_defaults(sanitizeFilename=True)
parser.set_defaults(overwrite=False)
parser.set_defaults(nParallel=1)

args = parser.parse_args()

sorter = DICOMSorter(
sourceDir=args.sourceDir,
destinationDir=args.destinationDir,
targetPattern=args.targetPattern,
truncateUID=args.truncateUID,
sanitizeFilename=args.sanitizeFilename,
)

sorter.sortDICOMFiles(option="copy", overwrite=args.overwrite, nParallel=int(args.nParallel))

0 comments on commit bb0e687

Please sign in to comment.