Skip to content

Commit 47927d0

Browse files
authored
Add content date/time, series date/time, instance creation date/time to base class (#376)
1 parent 30c9a70 commit 47927d0

2 files changed

Lines changed: 158 additions & 3 deletions

File tree

src/highdicom/base.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ def __init__(
6060
device_serial_number: str | None = None,
6161
institution_name: str | None = None,
6262
institutional_department_name: str | None = None,
63+
content_date: str | datetime.date | None = None,
64+
content_time: str | datetime.time | None = None,
65+
series_date: str | datetime.date | None = None,
66+
series_time: str | datetime.time | None = None,
6367
):
6468
"""
6569
Parameters
@@ -121,6 +125,18 @@ def __init__(
121125
institutional_department_name: Union[str, None], optional
122126
Name of the department of the person or device that creates the
123127
SR document instance.
128+
content_date: str | datetime.date | None, optional
129+
Date the content of this instance was created. If not specified,
130+
the current date will be used.
131+
content_time: str | datetime.time | None, optional
132+
Time the content of this instance was created. If not specified,
133+
the current time will be used.
134+
series_date: str | datetime.date | None, optional
135+
Date the series was started. This should be the same for all
136+
instances in a series.
137+
series_time: str | datetime.time | None, optional
138+
Time the series was started. This should be the same for all
139+
instances in a series.
124140
125141
Note
126142
----
@@ -207,7 +223,11 @@ def __init__(
207223
_check_long_string(device_serial_number)
208224
self.DeviceSerialNumber = device_serial_number
209225
if software_versions is not None:
210-
_check_long_string(software_versions)
226+
if not isinstance(software_versions, str):
227+
for v in software_versions:
228+
_check_long_string(v)
229+
else:
230+
_check_long_string(software_versions)
211231
self.SoftwareVersions = software_versions
212232
if institution_name is not None:
213233
_check_long_string(institution_name)
@@ -227,11 +247,59 @@ def __init__(
227247
)
228248
self.InstanceNumber = instance_number
229249

250+
now = datetime.datetime.now()
251+
self.InstanceCreationDate = DA(now.date())
252+
self.InstanceCreationTime = TM(now.time())
253+
230254
# Content Date and Content Time are not present in all IODs
231255
if is_attribute_in_iod('ContentDate', sop_class_uid):
232-
self.ContentDate = DA(datetime.datetime.now().date())
256+
if content_date is None:
257+
if content_time is not None:
258+
raise TypeError(
259+
"'content_time' may not be specified without "
260+
"'content_date'."
261+
)
262+
content_date = now.date()
263+
content_date = DA(content_date)
264+
self.ContentDate = content_date
265+
elif content_date is not None:
266+
raise TypeError(
267+
f"'content_date' should not be specified for SOP Class UID: "
268+
f"{sop_class_uid}"
269+
)
233270
if is_attribute_in_iod('ContentTime', sop_class_uid):
234-
self.ContentTime = TM(datetime.datetime.now().time())
271+
if content_time is None:
272+
content_time = now.time()
273+
content_time = TM(content_time)
274+
self.ContentTime = content_time
275+
elif content_time is not None:
276+
raise TypeError(
277+
f"'content_time' should not be specified for SOP Class UID: "
278+
f"{sop_class_uid}"
279+
)
280+
281+
if series_date is not None:
282+
series_date = DA(series_date)
283+
if content_date is not None:
284+
if series_date > content_date:
285+
raise ValueError(
286+
"'series_date' must not be later than 'content_date'."
287+
)
288+
self.SeriesDate = series_date
289+
if series_time is not None:
290+
series_time = TM(series_time)
291+
if series_date is None:
292+
raise TypeError(
293+
"'series_time' may not be specified without "
294+
"'series_date'."
295+
)
296+
if content_time is not None:
297+
if series_time > content_time:
298+
raise ValueError(
299+
"'series_time' must not be later than content time."
300+
)
301+
self.SeriesTime = series_time
302+
235303
if content_qualification is not None:
236304
content_qualification = ContentQualificationValues(
237305
content_qualification

tests/test_base.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
import re
23
import pytest
34
import unittest
@@ -59,6 +60,92 @@ def test_type_3_attributes(self):
5960
)
6061
assert instance.SoftwareVersions is not None
6162
assert instance.ManufacturerModelName is not None
63+
assert hasattr(instance, 'ContentDate')
64+
assert hasattr(instance, 'ContentTime')
65+
assert not hasattr(instance, 'SeriesDate')
66+
assert not hasattr(instance, 'SeriesTime')
67+
68+
def test_content_time_without_date(self):
69+
msg = (
70+
"'content_time' may not be specified without "
71+
"'content_date'."
72+
)
73+
with pytest.raises(TypeError, match=msg):
74+
SOPClass(
75+
study_instance_uid=UID(),
76+
series_instance_uid=UID(),
77+
series_number=1,
78+
sop_instance_uid=UID(),
79+
sop_class_uid='1.2.840.10008.5.1.4.1.1.88.33',
80+
instance_number=1,
81+
modality='SR',
82+
manufacturer='highdicom',
83+
manufacturer_model_name='foo-bar',
84+
software_versions='v1.0.0',
85+
transfer_syntax_uid=ExplicitVRLittleEndian,
86+
content_time=datetime.time(12, 34, 56),
87+
)
88+
89+
def test_series_datetime(self):
90+
instance = SOPClass(
91+
study_instance_uid=UID(),
92+
series_instance_uid=UID(),
93+
series_number=1,
94+
sop_instance_uid=UID(),
95+
sop_class_uid='1.2.840.10008.5.1.4.1.1.88.33',
96+
instance_number=1,
97+
modality='SR',
98+
manufacturer='highdicom',
99+
manufacturer_model_name='foo-bar',
100+
software_versions='v1.0.0',
101+
transfer_syntax_uid=ExplicitVRLittleEndian,
102+
series_date=datetime.date(2000, 12, 1),
103+
series_time=datetime.time(12, 34, 56),
104+
)
105+
assert hasattr(instance, 'SeriesDate')
106+
assert hasattr(instance, 'SeriesTime')
107+
108+
def test_series_date_without_time(self):
109+
msg = (
110+
"'series_time' may not be specified without "
111+
"'series_date'."
112+
)
113+
with pytest.raises(TypeError, match=msg):
114+
SOPClass(
115+
study_instance_uid=UID(),
116+
series_instance_uid=UID(),
117+
series_number=1,
118+
sop_instance_uid=UID(),
119+
sop_class_uid='1.2.840.10008.5.1.4.1.1.88.33',
120+
instance_number=1,
121+
modality='SR',
122+
manufacturer='highdicom',
123+
manufacturer_model_name='foo-bar',
124+
software_versions='v1.0.0',
125+
transfer_syntax_uid=ExplicitVRLittleEndian,
126+
series_time=datetime.time(12, 34, 56),
127+
)
128+
129+
def test_series_date_after_content(self):
130+
msg = (
131+
"'series_date' must not be later than 'content_date'."
132+
)
133+
with pytest.raises(ValueError, match=msg):
134+
SOPClass(
135+
study_instance_uid=UID(),
136+
series_instance_uid=UID(),
137+
series_number=1,
138+
sop_instance_uid=UID(),
139+
sop_class_uid='1.2.840.10008.5.1.4.1.1.88.33',
140+
instance_number=1,
141+
modality='SR',
142+
manufacturer='highdicom',
143+
manufacturer_model_name='foo-bar',
144+
software_versions='v1.0.0',
145+
transfer_syntax_uid=ExplicitVRLittleEndian,
146+
content_date=datetime.date(2000, 12, 1),
147+
series_date=datetime.date(2000, 12, 2),
148+
)
62149

63150
def test_big_endian(self):
64151
with pytest.raises(ValueError):

0 commit comments

Comments
 (0)