Repository: blairconrad/dicognito

Issue:
Private creator 0031,0020 breaks anonymization
Anonymizing a dataset containing a value for 0031,0020, which would typically be a [Private Creator Data Element](https://dicom.nema.org/dicom/2013/output/chtml/part05/sect_7.8.html#sect_7.8.1), results in dicognito erroring out with 

```
Error occurred while converting <_io.BytesIO object at 0x0000022DD4CEA160>. Aborting.
Traceback (most recent call last):
  File "E:\Dev\dicognito\.venv\dicognito\Lib\site-packages\pydicom\tag.py", line 28, in tag_in_exception
    yield
  File "E:\Dev\dicognito\.venv\dicognito\Lib\site-packages\pydicom\dataset.py", line 2474, in walk
    callback(self, data_element)  # self = this Dataset
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\Dev\dicognito\src\dicognito\anonymizer.py", line 151, in _anonymize_element
    if handler(dataset, data_element):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\Dev\dicognito\src\dicognito\idanonymizer.py", line 67, in __call__
    if self._anonymize_mitra_global_patient_id(dataset, data_element):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\Dev\dicognito\src\dicognito\idanonymizer.py", line 97, in _anonymize_mitra_global_patient_id
    dataset[(mitra_linked_attributes_group << 16) + private_tag_group].value
    ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\Dev\dicognito\.venv\dicognito\Lib\site-packages\pydicom\dataset.py", line 988, in __getitem__
    elem = self._dict[tag]
           ~~~~~~~~~~^^^^^
KeyError: (0031, 0000)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "E:\Dev\dicognito\src\dicognito\__main__.py", line 76, in main
    anonymizer.anonymize(dataset)
  File "E:\Dev\dicognito\src\dicognito\anonymizer.py", line 134, in anonymize
    dataset.walk(self._anonymize_element)
  File "E:\Dev\dicognito\.venv\dicognito\Lib\site-packages\pydicom\dataset.py", line 2472, in walk
    with tag_in_exception(tag):
  File "D:\Users\amidu\AppData\Local\Programs\Python\Python311\Lib\contextlib.py", line 155, in __exit__
    self.gen.throw(typ, value, traceback)
  File "E:\Dev\dicognito\.venv\dicognito\Lib\site-packages\pydicom\tag.py", line 32, in tag_in_exception
    raise type(exc)(msg) from exc
KeyError: 'With tag (0031, 0020) got exception: (0031, 0000)\nTraceback (most recent call last):\n  File "E:\\Dev\\dicognito\\.venv\\dicognito\\Lib\\site-packages\\pydicom\\tag.py", line 28, in tag_in_exception\n    yield\n  File "E:\\Dev\\dicognito\\.venv\\dicognito\\Lib\\site-packages\\pydicom\\dataset.py", line 2474, in walk\n    callback(self, data_element)  # self = this Dataset\n    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "E:\\Dev\\dicognito\\src\\dicognito\\anonymizer.py", line 151, in _anonymize_element\n    if handler(dataset, data_element):\n       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "E:\\Dev\\dicognito\\src\\dicognito\\idanonymizer.py", line 67, in __call__\n    if self._anonymize_mitra_global_patient_id(dataset, data_element):\n       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "E:\\Dev\\dicognito\\src\\dicognito\\idanonymizer.py", line 97, in _anonymize_mitra_global_patient_id\n    dataset[(mitra_linked_attributes_group << 16) + private_tag_group].value\n    ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "E:\\Dev\\dicognito\\.venv\\dicognito\\Lib\\site-packages\\pydicom\\dataset.py", line 988, in __getitem__\n    elem = self._dict[tag]\n           ~~~~~~~~~~^^^^^\nKeyError: (0031, 0000)\n'
```

It shouldn't error out.


Comments:

Patch:
diff --git a/src/dicognito/idanonymizer.py b/src/dicognito/idanonymizer.py
index f2597f0..cb979a6 100644
--- a/src/dicognito/idanonymizer.py
+++ b/src/dicognito/idanonymizer.py
@@ -90,13 +90,11 @@ class IDAnonymizer(ElementAnonymizer):
         mitra_global_patient_id_element = 0x0020
         if (
             data_element.tag.group == mitra_linked_attributes_group
-            and data_element.tag.element % mitra_global_patient_id_element == 0
+            and data_element.tag.element & 0x00FF == mitra_global_patient_id_element
         ):
             private_tag_group = data_element.tag.element >> 8
-            if (
-                dataset[(mitra_linked_attributes_group << 16) + private_tag_group].value
-                == "MITRA LINKED ATTRIBUTES 1.0"
-            ):
+            element = dataset.get((mitra_linked_attributes_group, private_tag_group), None)
+            if element and element.value == "MITRA LINKED ATTRIBUTES 1.0":
                 # For pydicom 2.2.0 and above (at least to 2.2.2) the Mitra global patient ID tag
                 # can be misidentified as VR IS, instead of its proper LO. This causes
                 # the anonymize action to fail because most values can't be converted.
diff --git a/src/dicognito/release_notes.md b/src/dicognito/release_notes.md
index 96a1b65..d10e1ac 100644
--- a/src/dicognito/release_notes.md
+++ b/src/dicognito/release_notes.md
@@ -3,6 +3,7 @@
 
 ### Fixed
 - Version table format has bad separator ([#147](https://github.com/blairconrad/dicognito/issues/147))
+- Private creator 0031,0020 breaks anonymization ([#157](https://github.com/blairconrad/dicognito/issues/157))
 
 ## 0.16.0a1
 


Test patch:
diff --git a/tests/test_anonymize_private_tags.py b/tests/test_anonymize_private_tags.py
index 042f46f..a2c20d8 100644
--- a/tests/test_anonymize_private_tags.py
+++ b/tests/test_anonymize_private_tags.py
@@ -1,4 +1,5 @@
 from dicognito.anonymizer import Anonymizer
+from pydicom import Dataset
 
 from .data_for_tests import load_dcm
 
@@ -16,3 +17,29 @@ def test_mitra_global_patient_id_is_updated():
         actual = block[0x20].value
 
         assert actual != "GPIYMBB54"
+
+
+def test_0031_0040_is_not_updated():
+    with Dataset() as dataset:
+        dataset.ensure_file_meta()
+        dataset.add_new(0x00310040, "LO", "Some value")
+        expected = dataset[0x0031, 0x0040]
+
+        anonymizer = Anonymizer()
+        anonymizer.anonymize(dataset)
+
+        actual = dataset[0x0031, 0x0040]
+        assert actual == expected
+
+
+def test_private_creator_0031_0020_is_not_updated():
+    with Dataset() as dataset:
+        dataset.ensure_file_meta()
+        dataset.add_new(0x00310020, "LO", "Another value")
+        expected = dataset[0x0031, 0x0020]
+
+        anonymizer = Anonymizer()
+        anonymizer.anonymize(dataset)
+
+        actual = dataset[0x0031, 0x0020]
+        assert actual == expected