Skip to content

Commit

Permalink
Merge pull request #66 from jjjermiah/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
jjjermiah committed Jan 30, 2024
2 parents 5880609 + 6d6da1b commit b05f2fa
Show file tree
Hide file tree
Showing 7 changed files with 467 additions and 37 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ It is made available via PyPI and can be installed using pip:
pip install nbiatoolkit
```

## CLI Usage

### getCollections
nbia-toolkit also provides a command line interface (CLI) to query the NBIA database for some queries.
``` bash
> getCollections --prefix NSCLC
NSCLC Radiogenomics
NSCLC-Radiomics
NSCLC-Radiomics-Genomics
NSCLC-Radiomics-Interobserver1

> getCollections --prefix TCGA | head -5
TCGA-BLCA
TCGA-BRCA
TCGA-CESC
TCGA-COAD
TCGA-ESCA
```


## Contributing

Interested in contributing? Check out the contributing guidelines. Please note that this project is released with a Code of Conduct. By contributing to this project, you agree to abide by its terms.
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ readme = "README.md"
[tool.poetry.scripts]
DICOMSorter = "nbiatoolkit.dicomsort:DICOMSorter_cli"
NBIAToolkit = "nbiatoolkit:version"
getCollections = "nbiatoolkit.nbia_cli:getCollections_cli"
getPatients = "nbiatoolkit.nbia_cli:getPatients_cli"
getBodyPartCounts = "nbiatoolkit.nbia_cli:getBodyPartCounts_cli"
downloadSingleSeries = "nbiatoolkit.nbia_cli:downloadSingleSeries_cli"
getSeries = "nbiatoolkit.nbia_cli:getSeries_cli"

[tool.poetry.dependencies]
python = ">=3.11 || 3.12"
Expand Down
3 changes: 2 additions & 1 deletion src/nbiatoolkit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@


# import the modules
from .nbia import NBIAClient, version
from .nbia import NBIAClient
from .nbia_cli import version, getCollections_cli
from .auth import OAuth2
from .logger.logger import setup_logger
from .utils.nbia_endpoints import NBIA_ENDPOINTS
Expand Down
8 changes: 7 additions & 1 deletion src/nbiatoolkit/dicomsort/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@
from .dicomsort import DICOMSorter, DICOMSorter_cli
from .helper_functions import parseDICOMKeysFromFormat, sanitizeFileName, truncateUID

__all__ = ["DICOMSorter", "parseDICOMKeysFromFormat", "sanitizeFileName", "truncateUID", "DICOMSorter_cli"]
__all__ = [
"DICOMSorter",
"parseDICOMKeysFromFormat",
"sanitizeFileName",
"truncateUID",
"DICOMSorter_cli",
]
52 changes: 35 additions & 17 deletions src/nbiatoolkit/dicomsort/dicomsort.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm


class DICOMSorter:
def __init__(
self,
Expand Down Expand Up @@ -80,7 +81,7 @@ def sortSingleDICOMFile(
if os.path.exists(targetFilename) and not overwrite:
print(f"Source File: {filePath}\n")
print(f"File {targetFilename} already exists. ")
sys.exit(
raise ValueError(
"Pattern is probably not unique or overwrite is set to False. Exiting."
)

Expand All @@ -95,20 +96,25 @@ def sortSingleDICOMFile(

return True

def sortDICOMFiles(self, option: str = "copy", overwrite: bool = False, nParallel: int = 1) -> 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))

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

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

return all(result)
def _get_dicom_files(self) -> 'list[str]':

def _get_dicom_files(self) -> "list[str]":
dicom_file_paths = []
# Iterate over all files in the source directory
for root, dirs, files in os.walk(self.sourceDir):
Expand All @@ -119,67 +125,77 @@ def _get_dicom_files(self) -> 'list[str]':

return dicom_file_paths


# 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",
parser.add_argument(
"sourceDir",
metavar="sourceDir",
type=str,
help="The source directory containing DICOM files.",
)

parser.add_argument("destinationDir",
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",
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.',
help="The target pattern for sorting DICOM files. Default is %%PatientName/%%SeriesNumber-%%SeriesInstanceUID/%%InstanceNumber.dcm.",
)

parser.add_argument("--truncateUID",
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",
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",
parser.add_argument(
"--overwrite",
dest="overwrite",
action="store_true",
help="Overwrite existing files. Default is False.",
)

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

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(
Expand All @@ -190,4 +206,6 @@ def DICOMSorter_cli():
sanitizeFilename=args.sanitizeFilename,
)

sorter.sortDICOMFiles(option="copy", overwrite=args.overwrite, nParallel=int(args.nParallel))
sorter.sortDICOMFiles(
option="copy", overwrite=args.overwrite, nParallel=int(args.nParallel)
)
30 changes: 12 additions & 18 deletions src/nbiatoolkit/nbia.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,11 @@
import zipfile
from tqdm import tqdm
from pyfiglet import Figlet
import os

# set __version__ variable
__version__ = "0.12.1"

def version():

f = Figlet(font='slant')
print(f.renderText('NBIAToolkit'))
print("Version: {}".format(__version__))
return



class NBIAClient:
"""
Expand Down Expand Up @@ -115,7 +108,9 @@ def getCollections(self, prefix: str = "") -> Union[list[str], None]:
collections.append(name)
return collections

def getModalityValues(self, Collection: str = "", BodyPartExamined: str = "") -> Union[list[str], None]:
def getModalityValues(
self, Collection: str = "", BodyPartExamined: str = ""
) -> Union[list[str], None]:
PARAMS = self.parsePARAMS(locals())

response = self.query_api(
Expand Down Expand Up @@ -152,14 +147,8 @@ def getPatients(self, Collection: str = "") -> Union[list[dict[str, str]], None]
), "PatientId must be a string, but received: %s" % type(
patient["PatientId"]
)
patientList.append(
{
"PatientId": patient["PatientId"],
"PatientName": patient["PatientName"],
"PatientSex": patient["PatientSex"],
"Collection": patient["Collection"],
}
)

patientList.append(patient)

return patientList

Expand Down Expand Up @@ -271,6 +260,12 @@ def downloadSeries(

with cf.ThreadPoolExecutor(max_workers=nParallel) as executor:
futures = []

try:
os.makedirs(downloadDir)
except FileExistsError:
pass

for seriesUID in SeriesInstanceUID:
future = executor.submit(
self._downloadSingleSeries,
Expand Down Expand Up @@ -357,7 +352,6 @@ def parsePARAMS(self, params: dict) -> dict:
return PARAMS



# main
if __name__ == "__main__":
from pprint import pprint
Expand Down

0 comments on commit b05f2fa

Please sign in to comment.