Mercurial > hg > orthanc-tests
changeset 939:0f5f43ac6876
new tests for C-find with private tags and sequences
| author | Alain Mazy <am@orthanc.team> |
|---|---|
| date | Thu, 02 Jul 2026 16:28:07 +0200 |
| parents | 6b5a76308918 |
| children | e6666a9679c3 3a440ee47213 |
| files | Database/ZeissOCT-cfind-query.dcm Database/ZeissOCT-cfind-query.txt Database/ZeissOCT.dcm GenerateConfigurationForTests.py Tests/Tests.py |
| diffstat | 5 files changed, 134 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Database/ZeissOCT-cfind-query.txt Thu Jul 02 16:28:07 2026 +0200 @@ -0,0 +1,116 @@ +# The ZeissOCT-cfind-query.dcm was generated with this code + + +from pydicom.dataset import Dataset +from pydicom.datadict import add_private_dict_entries + +from pynetdicom import AE +from pynetdicom.sop_class import StudyRootQueryRetrieveInformationModelFind + +# --------------------------------------------------------------------------- +# Custom private dictionary (same as Orthanc's ORTHANC__DICTIONARY / the +# dummy SCP), so the logged request/response shows resolved private tag +# names instead of "Private tag data" / "??" +# --------------------------------------------------------------------------- +add_private_dict_entries( + "99CZM_CapeCod_OctSettings", + { + 0x04051001: ("CS", "1", "PatternType"), + 0x0405101A: ("FL", "1", "SignalStrength"), + }, +) + +add_private_dict_entries( + "99CZM_CapeCod_OctGeneral", + { + 0x04071001: ("US", "1", "UndefinedPrivateTag1001"), + 0x04071002: ("US", "1", "UndefinedPrivateTag1002"), + 0x04071003: ("US", "1", "UndefinedPrivateTag1003"), + 0x04071005: ("SQ", "1", "OCTCubeSequence2"), + 0x040710A1: ("SQ", "1", "OCTCubeSequence"), + }, +) + +add_private_dict_entries( + "99CZM_FORUM", + { + 0x23011002: ("CS", "1", "Favorite"), + }, +) + +def build_query_dataset(): + """ + Builds the identifier dataset exactly as captured from the real + CIRRUS C-FIND request. + """ + ds = Dataset() + + ds.SpecificCharacterSet = "ISO_IR 192" + ds.QueryRetrieveLevel = "IMAGE" + + # Return keys (empty) + ds.ImageType = "" + ds.InstanceCreationDate = "" + ds.InstanceCreationTime = "" + ds.SOPClassUID = "" + ds.SOPInstanceUID = "" + ds.StudyDate = "" + ds.SeriesDate = "" + ds.AcquisitionDateTime = "" + ds.StudyTime = "" + ds.SeriesTime = "" + ds.AccessionNumber = "" + ds.Modality = "" + ds.ModalitiesInStudy = "" + ds.ReferringPhysicianName = "" + ds.StudyDescription = "" + ds.SeriesDescription = "" + ds.PerformingPhysicianName = "" + ds.AdmittingDiagnosesDescription = "" + ds.ManufacturerModelName = "" + + # ReferencedInstanceSequence — sequence containing one empty item + ds.ReferencedInstanceSequence = [Dataset()] + + # Matching keys (actual values from the captured request) + ds.PatientName = "" + ds.PatientID = "Zeiss*" + ds.IssuerOfPatientID = "" + ds.PatientBirthDate = "19790101-20300101" + ds.PatientSex = "" + + ds.add_new(0x00101000, "LO", "") # OtherPatientIDs + ds.add_new(0x00102160, "SH", "") # EthnicGroup + ds.PatientComments = "" + + ds.StudyInstanceUID = "" + ds.SeriesInstanceUID = "" + ds.StudyID = "" + ds.SeriesNumber = "" + ds.InstanceNumber = "" + ds.Laterality = "" + ds.ImageLaterality = "" + + ds.PerformedProcedureStepStartDate = "" + ds.PerformedProcedureStepStartTime = "" + + # RequestAttributesSequence — sequence containing one empty item + ds.RequestAttributesSequence = [Dataset()] + + ds.add_new(0x00420010, "ST", "") # DocumentTitle + + # --- Private tags: 99CZM_CapeCod_OctSettings block (group 0x0405) --- + block_0405 = ds.private_block(0x0405, "99CZM_CapeCod_OctSettings", create=True) + block_0405.add_new(0x01, "CS", "") # (0405,1001) PatternType + block_0405.add_new(0x1a, "FL", None) # (0405,101a) SignalStrength + + # --- Private tags: 99CZM_CapeCod_OctGeneral block (group 0x0407) --- + block_0407 = ds.private_block(0x0407, "99CZM_CapeCod_OctGeneral", create=True) + seq_tag = block_0407.get_tag(0xa1) + ds.add_new(seq_tag, "SQ", [Dataset()]) # (0407,10a1) OCTCubeSequence, 1 empty item + + # --- Private tags: 99CZM_FORUM block (group 0x2301) --- + block_2301 = ds.private_block(0x2301, "99CZM_FORUM", create=True) + block_2301.add_new(0x02, "CS", "") # (2301,1002) Favorite + ds.save_as('ZeissOCT-cfind-query.dcm', implicit_vr=False) + return ds
--- a/GenerateConfigurationForTests.py Wed Jul 01 16:42:19 2026 +0200 +++ b/GenerateConfigurationForTests.py Thu Jul 02 16:28:07 2026 +0200 @@ -180,6 +180,14 @@ '4321,1012' : [ 'LO', 'RadioButton3', 1, 1, 'RadioLogic' ], # For issue 140 '0009,1001' : [ 'DS', 'Abnormality score', 1, 1, 'Lunit' ], # For issue 168 '0009,0010' : [ 'LO', 'Private data element', 1, 1, 'Lunit' ], # For issue 168 + "0405,1001" : ["CS", "PatternType", 1, 1, "99CZM_CapeCod_OctSettings" ], # For ZeissOCT related tests + "0405,101a" : ["FL", "SignalStrength", 1, 1, "99CZM_CapeCod_OctSettings" ], + "0407,1001" : ["US", "UndefinedPrivateTag1001", 1, 1, "99CZM_CapeCod_OctGeneral" ], + "0407,1002" : ["US", "UndefinedPrivateTag1002", 1, 1, "99CZM_CapeCod_OctGeneral" ], + "0407,1003" : ["US", "UndefinedPrivateTag1003", 1, 1, "99CZM_CapeCod_OctGeneral" ], + "0407,1005" : ["SQ", "OCTCubeSequence2", 1, 1, "99CZM_CapeCod_OctGeneral" ], + "0407,10a1" : ["SQ", "OCTCubeSequence", 1, 1, "99CZM_CapeCod_OctGeneral" ], + "2301,1002" : ["CS", "Favorite", 1, 1, "99CZM_FORUM" ] } config['UserMetadata'] = {
--- a/Tests/Tests.py Wed Jul 01 16:42:19 2026 +0200 +++ b/Tests/Tests.py Thu Jul 02 16:28:07 2026 +0200 @@ -1956,6 +1956,16 @@ sequences = re.findall(r'\(0018,1010\)', i) self.assertEqual(1, len(sequences)) + + def test_incoming_findscu_private_tags_and_sequences(self): + UploadInstance(_REMOTE, 'ZeissOCT.dcm') + + if IsOrthancVersionAbove(_REMOTE, 1, 12, 12): # this query requests a private sequence and a private tag that is not present in the data + i = CallFindScu([ GetDatabasePath('ZeissOCT-cfind-query.dcm') ]) + self.assertIn('Find Response: 1', i) + sequences = re.findall(r'\(0407,1013\)', i) + self.assertEqual(128, len(sequences)) + def test_incoming_movescu(self): UploadInstance(_REMOTE, 'Multiframe.dcm')
