Repository: blairconrad/dicognito

Issue:
Fails on multi-valued dates and times
Example:

```
(0018, 1200) Date of Last Calibration            DA: ['19900101', '19900101']
(0018, 1201) Time of Last Calibration            TM: ['010000.000000', '010000.000000']
```

gives 
```
Traceback (most recent call last):
  File "c:\program files\python37\lib\site-packages\pydicom\tag.py", line 30, in tag_in_exception
    yield
  File "c:\program files\python37\lib\site-packages\pydicom\dataset.py", line 1354, in walk
    callback(self, data_element)  # self = this Dataset
  File "c:\program files\python37\lib\site-packages\dicognito\anonymizer.py", line 114, in _anonymize_element
    if handler(dataset, data_element):
  File "c:\program files\python37\lib\site-packages\dicognito\datetimeanonymizer.py", line 42, in __call__
    self._anonymize_date_and_time(dataset, data_element)
  File "c:\program files\python37\lib\site-packages\dicognito\datetimeanonymizer.py", line 51, in _anonymize_date_and_time
    old_date = datetime.datetime.strptime(date_value, date_format).date()
TypeError: strptime() argument 1 must be str, not MultiValue
```

Discussion:

Patch:
diff --git a/src/dicognito/datetimeanonymizer.py b/src/dicognito/datetimeanonymizer.py
index 59639a0..ba7ad8e 100644
--- a/src/dicognito/datetimeanonymizer.py
+++ b/src/dicognito/datetimeanonymizer.py
@@ -1,4 +1,5 @@
 import datetime
+import pydicom
 
 
 class DateTimeAnonymizer:
@@ -46,32 +47,65 @@ class DateTimeAnonymizer:
 
     def _anonymize_date_and_time(self, dataset, data_element):
         date_value = data_element.value
-        date_format = "%Y%m%d"[: len(date_value) - 2]
-
-        old_date = datetime.datetime.strptime(date_value, date_format).date()
+        if isinstance(data_element.value, pydicom.multival.MultiValue):
+            dates = list([v for v in data_element.value])
+        else:
+            dates = [data_element.value]
 
-        old_hours = datetime.time()
-        time_value = ""
+        times = []
         time_name = data_element.keyword[:-4] + "Time"
         if time_name in dataset:
             time_value = dataset.data_element(time_name).value
             if time_value:
-                old_hours = datetime.datetime.strptime(time_value[:2], "%H").time()
+                if isinstance(time_value, pydicom.multival.MultiValue):
+                    times = list([v for v in time_value])
+                else:
+                    times = [time_value]
+
+        new_dates = []
+        new_times = []
+        for i in range(len(dates)):
+            date_value = dates[i]
+            date_format = "%Y%m%d"[: len(date_value) - 2]
+            old_date = datetime.datetime.strptime(date_value, date_format).date()
+
+            time_value = ""
+            old_hours = datetime.time()
+            if i < len(times):
+                time_value = times[i]
+                if time_value:
+                    old_hours = datetime.datetime.strptime(time_value[:2], "%H").time()
+                else:
+                    old_hours = datetime.time()
+
+            old_datetime = datetime.datetime.combine(old_date, old_hours)
+            new_datetime = old_datetime + self.offset
+
+            new_dates.append(new_datetime.strftime(date_format))
+            new_times.append(new_datetime.strftime("%H") + time_value[2:])
+
+        new_dates = "\\".join(new_dates)
+        new_times = "\\".join(new_times)
+
+        data_element.value = new_dates
+        if times:
+            dataset.data_element(time_name).value = new_times
 
-        old_datetime = datetime.datetime.combine(old_date, old_hours)
-        new_datetime = old_datetime + self.offset
+    def _anonymize_datetime(self, dataset, data_element):
+        if isinstance(data_element.value, pydicom.multival.MultiValue):
+            datetimes = list([v for v in data_element.value])
+        else:
+            datetimes = [data_element.value]
 
-        data_element.value = new_datetime.strftime(date_format)
-        if time_value:
-            dataset.data_element(time_name).value = new_datetime.strftime("%H") + time_value[2:]
+        new_datetimes = []
+        for datetime_value in datetimes:
+            datetime_format = "%Y%m%d%H"[: len(datetime_value) - 2]
 
-    def _anonymize_datetime(self, dataset, data_element):
-        datetime_value = data_element.value
-        datetime_format = "%Y%m%d%H"[: len(datetime_value) - 2]
+            old_datetime = datetime.datetime.strptime(datetime_value[:10], datetime_format)
+            new_datetime = old_datetime + self.offset
 
-        old_datetime = datetime.datetime.strptime(datetime_value[:10], datetime_format)
-        new_datetime = old_datetime + self.offset
+            new_datetime_value = new_datetime.strftime(datetime_format)
+            new_datetime_value += datetime_value[len(new_datetime_value) :]
+            new_datetimes.append(new_datetime_value)
 
-        new_datetime_value = new_datetime.strftime(datetime_format)
-        new_datetime_value += datetime_value[len(new_datetime_value) :]
-        data_element.value = new_datetime_value
+        data_element.value = "\\".join(new_datetimes)
diff --git a/src/dicognito/release_notes.md b/src/dicognito/release_notes.md
index 0d2cec9..1ee3fec 100644
--- a/src/dicognito/release_notes.md
+++ b/src/dicognito/release_notes.md
@@ -7,6 +7,10 @@
 
 - Anonymize placer- and filler-order numbers ([#58](https://github.com/blairconrad/dicognito/issues/58))
 
+### Fixed
+
+- Fails on multi-valued dates and times ([#61](https://github.com/blairconrad/dicognito/issues/61))
+
 ## 0.7.1
 
 ### Fixed


Test patch:
diff --git a/tests/test_anonymize_single_instance.py b/tests/test_anonymize_single_instance.py
index c7bb461..74b7aef 100644
--- a/tests/test_anonymize_single_instance.py
+++ b/tests/test_anonymize_single_instance.py
@@ -422,6 +422,36 @@ def test_date_gets_anonymized_when_time_has_various_lengths(birth_time):
     assert len(new_time_string) == len(birth_time)
 
 
+def test_multivalued_date_with_no_time_pair_gets_anonymized():
+    with load_test_instance() as dataset:
+        dataset.DateOfLastCalibration = original_date = ["20010401", "20010402"]
+
+        anonymizer = Anonymizer()
+        anonymizer.anonymize(dataset)
+
+        new_date_string = dataset.DateOfLastCalibration
+
+    assert new_date_string != original_date
+    assert len(new_date_string) == len(original_date)
+
+
+def test_multivalued_date_and_time_pair_gets_anonymized():
+    with load_test_instance() as dataset:
+        dataset.DateOfLastCalibration = original_date = ["20010401", "20010402"]
+        dataset.TimeOfLastCalibration = original_time = ["120000", "135959"]
+
+        anonymizer = Anonymizer()
+        anonymizer.anonymize(dataset)
+
+        new_date_string = dataset.DateOfLastCalibration
+        new_time_string = dataset.TimeOfLastCalibration
+
+    assert new_date_string != original_date
+    assert len(new_date_string) == len(original_date)
+    assert new_time_string[2:] == original_time[2:]
+    assert len(new_time_string) == len(original_time)
+
+
 @pytest.mark.parametrize(
     "datetime_name",
     [
@@ -479,6 +509,19 @@ def test_datetime_of_various_lengths_gets_anonymized(acquisition_datetime):
     assert len(new_datetime_string) == len(acquisition_datetime)
 
 
+def test_multivalued_datetime_gets_anonymized():
+    with load_test_instance() as dataset:
+        dataset.AcquisitionDateTime = original_datetime = ["19741103121558", "19721004161558"]
+
+        anonymizer = Anonymizer()
+        anonymizer.anonymize(dataset)
+
+        new_datetime = dataset.AcquisitionDateTime
+
+    assert new_datetime != original_datetime
+    assert len(new_datetime) == len(original_datetime)
+
+
 def test_no_sex_still_changes_patient_name():
     with load_test_instance() as dataset:
         del dataset.PatientSex