def test_both_callbacks(self): data_sets = [] messages = [] def store_callback(data_set): data_sets.append(data_set) def get_callback(message): messages.append(message) get = odil.GetSCU(self.association) get.set_affected_sop_class( odil.registry.PatientRootQueryRetrieveInformationModelGET) get.get(self.query, store_callback, get_callback) self.assertEqual(len(data_sets), 1) self.assertSequenceEqual( data_sets[0].as_string("SOPInstanceUID"), [b"2.25.95090344942250266709587559073467305647"]) self.assertEqual(len(messages), 2) self.assertEqual(messages[0].get_number_of_remaining_sub_operations(), 0) self.assertEqual(messages[0].get_number_of_completed_sub_operations(), 1) self.assertEqual(messages[0].get_number_of_failed_sub_operations(), 0) self.assertEqual(messages[0].get_number_of_warning_sub_operations(), 0) self.assertFalse(messages[1].has_number_of_remaining_sub_operations()) self.assertEqual(messages[1].has_number_of_completed_sub_operations(), 1) self.assertEqual(messages[1].get_number_of_failed_sub_operations(), 0) self.assertEqual(messages[1].get_number_of_warning_sub_operations(), 0)
def test_without_callback(self): get = odil.GetSCU(self.association) get.set_affected_sop_class(odil.registry.PatientRootQueryRetrieveInformationModelGet) data_sets = get.get(self.query) self.assertEqual(len(data_sets), 1) self.assertSequenceEqual( data_sets[0].as_string("SOPInstanceUID"), [b"2.25.95090344942250266709587559073467305647"])
def test_only_store_callback(self): data_sets = [] def store_callback(data_set): data_sets.append(data_set) get = odil.GetSCU(self.association) get.set_affected_sop_class( odil.registry.PatientRootQueryRetrieveInformationModelGET) get.get(self.query, store_callback) self.assertEqual(len(data_sets), 1) self.assertSequenceEqual( data_sets[0].as_string("SOPInstanceUID"), ["2.25.95090344942250266709587559073467305647"])
def get(host, port, calling_ae_title, called_ae_title, level, keys, directory, iso_9660, layout, dicomdir, patient_key, study_key, series_key, image_key): if dicomdir and not iso_9660: raise Exception("Cannot create a DICOMDIR without ISO-9660 filenames") query = odil.DataSet() for key in keys: key, value = key.split("=", 1) value = value.split("\\") tag = getattr(odil.registry, key) vr = odil.registry.public_dictionary[tag].vr if vr in ["DS", "FL", "FD"]: value = [float(x) for x in value] elif vr in ["IS", "SL", "SS", "UL", "US"]: value = [int(x) for x in value] query.add(tag, value) get_syntax = getattr( odil.registry, "{}RootQueryRetrieveInformationModelGet".format(level.capitalize())) transfer_syntaxes = [ odil.registry.ImplicitVRLittleEndian, odil.registry.ExplicitVRLittleEndian ] get_pc = odil.AssociationParameters.PresentationContext( 1, get_syntax, transfer_syntaxes, odil.AssociationParameters.PresentationContext.Role.SCU) abstract_syntaxes = find_abstract_syntaxes(host, port, calling_ae_title, called_ae_title, level, keys) if not abstract_syntaxes: # Negotiate ALL storage syntaxes. Is there a better way to do this? abstract_syntaxes = [ entry.key() for entry in odil.registry.uids_dictionary if entry.data().name.endswith("Storage") ] if len(abstract_syntaxes) > 126: raise Exception("Too many storage syntaxes") storage_pcs = [ odil.AssociationParameters.PresentationContext( 2 * (i + 1) + 1, uid, transfer_syntaxes, odil.AssociationParameters.PresentationContext.Role.SCP) for i, uid in enumerate(abstract_syntaxes) ] association = odil.Association() association.set_peer_host(host) association.set_peer_port(port) association.update_parameters()\ .set_calling_ae_title(calling_ae_title)\ .set_called_ae_title(called_ae_title) \ .set_presentation_contexts([get_pc]+storage_pcs) association.associate() logging.info("Association established") get = odil.GetSCU(association) get.set_affected_sop_class(get_syntax) class Callback(object): def __init__(self, directory): self.directory = directory self.completed = 0 self.remaining = 0 self.failed = 0 self.warning = 0 self.stored = {} self.files = [] self._study_ids = {} self._series_ids = {} def store(self, data_set): specific_character_set = odil.Value.Strings() if "SpecificCharacterSet" in data_set: specific_character_set = data_set.as_string( "SpecificCharacterSet") as_unicode = lambda x: odil.as_unicode(x, specific_character_set) if layout == "flat": directory = "" elif layout == "tree": # Patient directory: <PatientName> or <PatientID>. patient_directory = None if "PatientName" in data_set and data_set.as_string( "PatientName"): patient_directory = data_set.as_string("PatientName")[0] else: patient_directory = data_set.as_string("PatientID")[0] patient_directory = as_unicode(patient_directory) # Study directory: <StudyID>_<StudyDescription>, both parts are # optional. If both tags are missing or empty, default to a # numeric index based on StudyInstanceUID. study_directory = [] if "StudyID" in data_set and data_set.as_string("StudyID"): study_directory.append(data_set.as_string("StudyID")[0]) if ("StudyDescription" in data_set and data_set.as_string("StudyDescription")): study_directory.append( data_set.as_string("StudyDescription")[0]) if not study_directory: study_instance_uid = data_set.as_string( "StudyInstanceUID")[0] study_directory.append( self._study_ids.setdefault(study_instance_uid, 1 + len(self._study_ids))) study_directory = "_".join( as_unicode(x) for x in study_directory) # Study directory: <SeriesNumber>_<SeriesDescription>, both # parts are optional. If both tags are missing or empty, default # to a numeric index based on SeriesInstanceUID. series_directory = [] if "SeriesNumber" in data_set and data_set.as_int( "SeriesNumber"): series_directory.append( str(data_set.as_int("SeriesNumber")[0])) if ("SeriesDescription" in data_set and data_set.as_string("SeriesDescription")): series_directory.append( data_set.as_string("SeriesDescription")[0]) if not series_directory: series_instance_uid = data_set.as_string( "series_instance_uid")[0] series_directory.append( self._series_ids.setdefault(series_instance_uid, 1 + len(self._series_ids))) series_directory = "_".join( as_unicode(x) for x in series_directory) if iso_9660: patient_directory = to_iso_9660(patient_directory) study_directory = to_iso_9660(study_directory) series_directory = to_iso_9660(series_directory) directory = os.path.join(patient_directory, study_directory, series_directory) if not os.path.isdir(os.path.join(self.directory, directory)): os.makedirs(os.path.join(self.directory, directory)) else: raise NotImplementedError() self.stored.setdefault(directory, 0) if iso_9660: filename = "IM{:06d}".format(1 + self.stored[directory]) else: filename = as_unicode(data_set.as_string("SOPInstanceUID")[0]) with odil.open(os.path.join(self.directory, directory, filename), "wb") as fd: odil.Writer.write_file(data_set, fd) self.stored[directory] += 1 self.files.append(os.path.join(directory, filename)) def get(self, message): for type_ in ["completed", "remaining", "failed", "warning"]: base = "number_of_{}_sub_operations".format(type_) if getattr(message, "has_{}".format(base))(): setattr(self, type_, getattr(message, "get_{}".format(base))()) logging.info( "Remaining: {}, completed: {}, failed: {}, warning: {}".format( self.remaining, self.completed, self.failed, self.warning)) if not os.path.isdir(directory): os.makedirs(directory) if len(os.listdir(directory)): logging.warning("{} is not empty".format(directory)) callback = Callback(directory) get.get(query, callback.store, callback.get) print("Completed: {}, remaining: {}, failed: {}, warning: {}".format( callback.completed, callback.remaining, callback.failed, callback.warning)) association.release() logging.info("Association released") if dicomdir: logging.info("Creating DICOMDIR") create_dicomdir([os.path.join(directory, x) for x in callback.files], directory, patient_key, study_key, series_key, image_key)