Repository: pydicom/pynetdicom

Issue:
Add new 2019e SOP Classes, changes to UPS 
Update pynetdicom to match 2019e version of the DICOM Standard

Discussion:

Patch:
diff --git a/docs/changelog/v1.5.0.rst b/docs/changelog/v1.5.0.rst
index 313fcb339..7cef687f0 100644
--- a/docs/changelog/v1.5.0.rst
+++ b/docs/changelog/v1.5.0.rst
@@ -29,6 +29,7 @@ Enhancements
   :mod:`~pynetdicom.sop_class`.
 * Added support for the DIMSE intervention event handlers aborting or releasing
   the association during service class operation.
+* Add new SOP classes up to 2019e version of the standard (:issue:`424`)
 
 Changes
 .......
diff --git a/docs/service_classes/storage_service_class.rst b/docs/service_classes/storage_service_class.rst
index ce6389d30..fda7568ce 100644
--- a/docs/service_classes/storage_service_class.rst
+++ b/docs/service_classes/storage_service_class.rst
@@ -237,6 +237,10 @@ Supported SOP Classes
 +----------------------------------+-------------------------------------------------------------------+---------+
 | 1.2.840.10008.5.1.4.1.1.88.73    | PatientRadiationDoseSRStorage                                     | A.35.18 |
 +----------------------------------+-------------------------------------------------------------------+---------+
+| 1.2.840.10008.5.1.4.1.1.88.74    | PlannedImagingAgentAdministrationSRStorage                        | A.35.19 |
++----------------------------------+-------------------------------------------------------------------+---------+
+| 1.2.840.10008.5.1.4.1.1.88.75    | PerformedImagingAgestAdministrationSRStorage                      | A.35.20 |
++----------------------------------+-------------------------------------------------------------------+---------+
 | 1.2.840.10008.5.1.4.1.1.90.1     | ContentAssessmentResultsStorage                                   | A.81    |
 +----------------------------------+-------------------------------------------------------------------+---------+
 | 1.2.840.10008.5.1.4.1.1.104.1    | EncapsulatedPDFStorage                                            | A.45.1  |
@@ -273,6 +277,14 @@ Supported SOP Classes
 +----------------------------------+-------------------------------------------------------------------+---------+
 | 1.2.840.10008.5.1.4.1.1.481.9    | RTIonBeamsTreatmentRecordStorage                                  | A.50    |
 +----------------------------------+-------------------------------------------------------------------+---------+
+| 1.2.840.10008.5.1.4.1.1.481.10   | RTPhysicianIntentStorage                                          | A.86.1  |
++----------------------------------+-------------------------------------------------------------------+---------+
+| 1.2.840.10008.5.1.4.1.1.481.11   | RTSegmentAnnotationStorage                                        | A.86.1  |
++----------------------------------+-------------------------------------------------------------------+---------+
+| 1.2.840.10008.5.1.4.1.1.481.12   | RTRadiationSetStorage                                             | A.86.1  |
++----------------------------------+-------------------------------------------------------------------+---------+
+| 1.2.840.10008.5.1.4.1.1.481.13   | CArmPhotonElectronRadiationStorage                                | A.86.1  |
++----------------------------------+-------------------------------------------------------------------+---------+
 | 1.2.840.10008.5.1.4.34.7         | RTBeamsDeliveryInstructionStorage                                 | A.64    |
 +----------------------------------+-------------------------------------------------------------------+---------+
 | 1.2.840.10008.5.1.4.34.10        | RTBrachyApplicationSetupDeliveryInstructionsStorage               | A.79    |
diff --git a/docs/service_classes/ups.rst b/docs/service_classes/ups.rst
index 1adbb4ef9..f77c0ae37 100644
--- a/docs/service_classes/ups.rst
+++ b/docs/service_classes/ups.rst
@@ -24,6 +24,8 @@ Supported SOP Classes
 +----------------------------+------------------------------------------------+
 | 1.2.840.10008.5.1.4.34.6.4 | UnifiedProcedureStepEventSOPClass              |
 +----------------------------+------------------------------------------------+
+| 1.2.840.10008.5.1.4.34.6.5 | UnifiedProcedureStepQuerySOPClass              |
++----------------------------+------------------------------------------------+
 
 
 DIMSE Services
@@ -64,6 +66,10 @@ DIMSE Services
 +---------------------------+-------------------------------+
 | N-EVENT-REPORT            | Mandatory/Mandatory           |
 +---------------------------+-------------------------------+
+| *Unified Procedure Step Query SOP Class*                  |
++---------------------------+-------------------------------+
+| C-FIND                    | Mandatory/Mandatory           |
++---------------------------+-------------------------------+
 
 
 .. _ups_statuses:
diff --git a/pynetdicom/sop_class.py b/pynetdicom/sop_class.py
index 854766f5d..fd209d605 100644
--- a/pynetdicom/sop_class.py
+++ b/pynetdicom/sop_class.py
@@ -128,6 +128,16 @@ class SOPClass(UID):
 
 def _generate_sop_classes(sop_class_dict):
     """Generate the SOP Classes."""
+
+    _2019e = (
+        '1.2.840.10008.5.1.4.1.1.88.74',
+        '1.2.840.10008.5.1.4.1.1.88.75',
+        '1.2.840.10008.5.1.4.1.1.481.10',
+        '1.2.840.10008.5.1.4.1.1.481.11',
+        '1.2.840.10008.5.1.4.1.1.481.12',
+        '1.2.840.10008.5.1.4.1.1.481.13',
+    )
+
     for name in sop_class_dict:
         uid = sop_class_dict[name]
         sop_class = SOPClass(uid)
@@ -135,6 +145,9 @@ def _generate_sop_classes(sop_class_dict):
         docstring = "``{}``".format(uid)
         if uid in ('1.2.840.10008.5.1.1.9', '1.2.840.10008.5.1.1.18'):
             docstring += "\n\n.. versionadded:: 1.4"
+        elif uid in _2019e:
+            docstring += "\n\n.. versionadded:: 1.5"
+
         sop_class.__doc__ = docstring
         globals()[name] = sop_class
 
@@ -361,6 +374,8 @@ _STORAGE_CLASSES = {
     'AcquisitionContextSRStorage' : '1.2.840.10008.5.1.4.1.1.88.71',  # A.35.16
     'SimplifiedAdultEchoSRStorage' : '1.2.840.10008.5.1.4.1.1.88.72',  # A.35.17
     'PatientRadiationDoseSRStorage' : '1.2.840.10008.5.1.4.1.1.88.73',  # A.35.18
+    'PlannedImagingAgentAdministrationSRStorage' : '1.2.840.10008.5.1.4.1.1.88.74',  # A.35.19
+    'PerformedImagingAgestAdministrationSRStorage' : '1.2.840.10008.5.1.4.1.1.88.75',  # A.35.20
     'ContentAssessmentResultsStorage' : '1.2.840.10008.5.1.4.1.1.90.1',  # A.81
     'EncapsulatedPDFStorage' : '1.2.840.10008.5.1.4.1.1.104.1',  # A.45.1
     'EncapsulatedCDAStorage' : '1.2.840.10008.5.1.4.1.1.104.2',  # A.45.2
@@ -379,6 +394,10 @@ _STORAGE_CLASSES = {
     'RTTreatmentSummaryRecordStorage' : '1.2.840.10008.5.1.4.1.1.481.7',  # A.31
     'RTIonPlanStorage' : '1.2.840.10008.5.1.4.1.1.481.8',  # A.49
     'RTIonBeamsTreatmentRecordStorage' : '1.2.840.10008.5.1.4.1.1.481.9',  # A.50
+    'RTPhysicianIntentStorage' : '1.2.840.10008.5.1.4.1.1.481.10',  # A.86.1.2
+    'RTSegmentAnnotationStorage' : '1.2.840.10008.5.1.4.1.1.481.11',  # A.86.1.3
+    'RTRadiationSetStorage' : '1.2.840.10008.5.1.4.1.1.481.12',  # A.86.1.4
+    'CArmPhotonElectronRadiationStorage' : '1.2.840.10008.5.1.4.1.1.481.13',  # A.86.1.5
     'RTBeamsDeliveryInstructionStorage' : '1.2.840.10008.5.1.4.34.7',  # A.64
     'RTBrachyApplicationSetupDeliveryInstructionsStorage' : '1.2.840.10008.5.1.4.34.10',  # A.79
 }
@@ -394,6 +413,7 @@ _UNIFIED_PROCEDURE_STEP_CLASSES = {
     'UnifiedProcedureStepWatchSOPClass' : '1.2.840.10008.5.1.4.34.6.2',
     'UnifiedProcedureStepPullSOPClass' : '1.2.840.10008.5.1.4.34.6.3',
     'UnifiedProcedureStepEventSOPClass' : '1.2.840.10008.5.1.4.34.6.4',
+    'UnifiedProcedureStepQuerySOPClass' : '1.2.840.10008.5.1.4.34.6.5',
 }
 _VERIFICATION_CLASSES = {
     'VerificationSOPClass' : '1.2.840.10008.1.1',
diff --git a/setup.py b/setup.py
index a98619052..808081c88 100644
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,9 @@ setup(
     author_email = "scaramallion@users.noreply.github.com",
     url = "https://github.com/pydicom/pynetdicom",
     license = "MIT",
-    keywords = "dicom python medicalimaging radiotherapy oncology pydicom imaging",
+    keywords = (
+        "dicom python medicalimaging radiotherapy oncology pydicom imaging"
+    ),
     project_urls={
         'Documentation' : 'https://pydicom.github.io/pynetdicom/'
     },
@@ -45,5 +47,5 @@ setup(
         "Topic :: Scientific/Engineering :: Medical Science Apps.",
         "Topic :: Software Development :: Libraries",
     ],
-    install_requires = ["pydicom>=1.3.0"],
+    install_requires = ["pydicom>=1.4.0"],
 )


Test patch:
diff --git a/pynetdicom/tests/test_presentation.py b/pynetdicom/tests/test_presentation.py
index edc76e6f5..8cc303fe4 100644
--- a/pynetdicom/tests/test_presentation.py
+++ b/pynetdicom/tests/test_presentation.py
@@ -1857,8 +1857,8 @@ class TestServiceContexts(object):
             assert context.context_id is None
 
         assert contexts[0].abstract_syntax == '1.2.840.10008.5.1.4.1.1.1'
-        assert contexts[80].abstract_syntax == '1.2.840.10008.5.1.4.1.1.77.1.4.1'
-        assert contexts[-1].abstract_syntax == '1.2.840.10008.5.1.4.1.1.90.1'
+        assert contexts[80].abstract_syntax == '1.2.840.10008.5.1.4.1.1.77.1.2'
+        assert contexts[-1].abstract_syntax == '1.2.840.10008.5.1.4.1.1.9.2.1'
 
     def test_storage_commitement(self):
         """Tests with storage commitment presentation contexts"""
@@ -1886,7 +1886,7 @@ class TestServiceContexts(object):
     def test_unified_procedure(self):
         """Tests with unified procedure presentation contexts"""
         contexts = UnifiedProcedurePresentationContexts
-        assert len(contexts) == 4
+        assert len(contexts) == 5
 
         for context in contexts:
             assert context.transfer_syntax == DEFAULT_TRANSFER_SYNTAXES
@@ -1896,6 +1896,7 @@ class TestServiceContexts(object):
         assert contexts[1].abstract_syntax == '1.2.840.10008.5.1.4.34.6.2'
         assert contexts[2].abstract_syntax == '1.2.840.10008.5.1.4.34.6.3'
         assert contexts[3].abstract_syntax == '1.2.840.10008.5.1.4.34.6.4'
+        assert contexts[4].abstract_syntax == '1.2.840.10008.5.1.4.34.6.5'
 
     def test_verification(self):
         """Test the verification service presentation contexts."""