Skip to content

Commit 7e51e20

Browse files
authored
Merge pull request #13 from jjjermiah/development
refactor dicomsorter class and add some tests
2 parents 931eb4a + b5cf783 commit 7e51e20

File tree

4 files changed

+164
-2
lines changed

4 files changed

+164
-2
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import re
2+
from typing import Tuple, List
3+
def parseDICOMKeysFromFormat(targetPattern: str) -> Tuple[str, List[str]]:
4+
"""
5+
Parse the target pattern to create a format string with named placeholders
6+
and extract a list of DICOM keys.
7+
8+
The target pattern is a string with placeholders matching '%<DICOMKey>'.
9+
This method converts placeholders into a format string with named placeholders
10+
and creates a list of DICOM keys contained within the placeholders.
11+
12+
Returns:
13+
Tuple[str, List[str]]: A tuple containing the format string and a list of DICOM keys.
14+
15+
Example usage:
16+
fmt, keys = parseDICOMKeysFromFormat(targetPattern)
17+
print(fmt) # "%(PatientID)s-%(StudyDate)s"
18+
print(keys) # ['PatientID', 'StudyDate']
19+
"""
20+
21+
# Compile the regex pattern for efficiency
22+
dicom_key_pattern = re.compile(r'%([A-Za-z]+)')
23+
keys = dicom_key_pattern.findall(targetPattern)
24+
# Use the same compiled pattern for replacing
25+
formatted_pattern = dicom_key_pattern.sub(r'%(\1)s', targetPattern)
26+
27+
return formatted_pattern, keys
28+
29+
def sanitizeFileName(fileName: str) -> str:
30+
"""
31+
Sanitize the file name by replacing potentially dangerous characters.
32+
"""
33+
assert fileName is not None
34+
assert isinstance(fileName, str)
35+
# Define a pattern for disallowed filename characters and their replacements
36+
disallowed_characters_pattern = re.compile(r'[<>:"/\\|?*\x00-\x1f]')
37+
# Replace disallowed characters with an underscore
38+
sanitized_name = disallowed_characters_pattern.sub('_', fileName)
39+
40+
# replace spaces with underscores
41+
sanitized_name = sanitized_name.replace(" ", "_")
42+
43+
# Remove subsequent underscores
44+
sanitized_name = re.sub(r'(_{2,})', '_', sanitized_name)
45+
46+
return sanitized_name
47+
48+
49+
def truncateUID(uid: str, lastDigits: int = 5) -> str:
50+
"""
51+
Truncate the UID to the last n characters (includes periods & underscores).
52+
If the UID is shorter than n characters, the entire UID is returned.
53+
"""
54+
assert uid is not None
55+
assert isinstance(uid, str)
56+
assert isinstance(lastDigits, int)
57+
# Truncate the UID to the last n digits
58+
truncated_uid = uid[-lastDigits:]
59+
return truncated_uid

src/nbiatoolkit/dicomsort/test_helper_functions.py

Whitespace-only changes.

tests/test_dicom_helpers.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from nbiatoolkit.dicomsort.helper_functions import parseDICOMKeysFromFormat,sanitizeFileName,truncateUID
2+
import pytest
3+
###############################################################################
4+
# parseDICOMKeysFromFormat
5+
6+
def test_parseDICOMKeysFromFormat():
7+
targetPattern = "%PatientID-%StudyDate"
8+
expected_format = "%(PatientID)s-%(StudyDate)s"
9+
expected_keys = ['PatientID', 'StudyDate']
10+
11+
format_string, keys = parseDICOMKeysFromFormat(targetPattern)
12+
13+
assert format_string == expected_format
14+
assert keys == expected_keys
15+
16+
def test_parseDICOMKeysFromFormat_no_keys():
17+
targetPattern = "some string without keys"
18+
expected_format = "some string without keys"
19+
expected_keys = []
20+
21+
format_string, keys = parseDICOMKeysFromFormat(targetPattern)
22+
23+
assert format_string == expected_format
24+
assert keys == expected_keys
25+
26+
def test_parseDICOMKeysFromFormat_multiple_keys():
27+
targetPattern = "%PatientID-%StudyDate-%SeriesNumber"
28+
expected_format = "%(PatientID)s-%(StudyDate)s-%(SeriesNumber)s"
29+
expected_keys = ['PatientID', 'StudyDate', 'SeriesNumber']
30+
31+
format_string, keys = parseDICOMKeysFromFormat(targetPattern)
32+
33+
assert format_string == expected_format
34+
assert keys == expected_keys
35+
36+
def test_parseDICOMKeysFromFormat_duplicate_keys():
37+
targetPattern = "%PatientID-%StudyDate-%PatientID"
38+
expected_format = "%(PatientID)s-%(StudyDate)s-%(PatientID)s"
39+
expected_keys = ['PatientID', 'StudyDate', 'PatientID']
40+
41+
format_string, keys = parseDICOMKeysFromFormat(targetPattern)
42+
43+
assert format_string == expected_format
44+
assert keys == expected_keys
45+
46+
###############################################################################
47+
# sanitizeFileName
48+
49+
def test_sanitizeFileName_no_special_characters():
50+
fileName = "test_file_name"
51+
sanitized_name = sanitizeFileName(fileName)
52+
assert sanitized_name == fileName
53+
54+
def test_sanitizeFileName_with_special_characters():
55+
fileName = "file<name>:with?special*characters"
56+
sanitized_name = sanitizeFileName(fileName)
57+
assert sanitized_name == "file_name_with_special_characters"
58+
59+
def test_sanitizeFileName_with_spaces():
60+
fileName = "file name with spaces"
61+
sanitized_name = sanitizeFileName(fileName)
62+
assert sanitized_name == "file_name_with_spaces"
63+
64+
def test_sanitizeFileName_empty_string():
65+
fileName = ""
66+
sanitized_name = sanitizeFileName(fileName)
67+
assert sanitized_name == ""
68+
69+
def test_sanitizeFileName_assertions():
70+
with pytest.raises(AssertionError):
71+
sanitizeFileName(None)
72+
with pytest.raises(AssertionError):
73+
sanitizeFileName(123)
74+
75+
###############################################################################
76+
# truncateUID
77+
78+
@pytest.fixture(scope="session")
79+
def uid():
80+
uid = "1.3.6.1.4.1.14519.5.2.1.215314536760363548451614931725770729635"
81+
return uid
82+
83+
84+
def test_truncateUID_with_valid_inputs(uid):
85+
lastDigits = 5
86+
expected_output = "29635"
87+
assert truncateUID(uid, lastDigits) == expected_output
88+
89+
def test_truncateUID_with_lastDigits_greater_than_length_of_UID(uid):
90+
lastDigits = 100
91+
expected_output = uid
92+
assert truncateUID(uid, lastDigits) == expected_output
93+
94+
def test_truncateUID_with_invalid_input_types(uid):
95+
lastDigits = "5"
96+
with pytest.raises(AssertionError):
97+
truncateUID(uid, lastDigits)
98+
99+
def test_truncateUID_with_None_input(uid):
100+
lastDigits = None
101+
with pytest.raises(AssertionError):
102+
truncateUID(uid, lastDigits)
103+

tests/test_nbia.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def test_getPatients(nbia_patients):
6565
assert isinstance(nbia_patients[0], str)
6666
assert len(nbia_patients[0]) > 0
6767

68-
@pytest.mark.getSeries
68+
6969
def test_getSeries(nbia_client, nbia_collections, nbia_patients):
7070
seriesList = nbia_client.getSeries(
7171
Collection=nbia_collections[0],
@@ -77,7 +77,7 @@ def test_getSeries(nbia_client, nbia_collections, nbia_patients):
7777
assert len(seriesList) > 0
7878
assert isinstance(seriesList[0], dict)
7979

80-
@pytest.mark.getSeries
80+
8181
def test_fail_getSeries(nbia_client, nbia_collections, nbia_patients):
8282
with pytest.raises(Exception):
8383
seriesList = nbia_client.getSeries(

0 commit comments

Comments
 (0)