def __init__(self): self.batch_dir = Path.cwd().joinpath('test', 'batchtestdata') self.dicom_structure = DICOMDirectorySearch.get_dicom_structure( self.batch_dir, self.DummyProgressWindow, self.DummyProgressWindow) self.iso_levels = self.get_iso_levels() self.timestamp = BatchProcessingController.create_timestamp() self.application = QApplication()
def test_batch_pyrad2pyradsr(test_object): """ Test that a DICOM file 'PyRadiomics-SR.dcm' is created from Pyrad2Pyrad-SR. :param test_object: test_object function, for accessing the shared TestObject object. """ # Loop through patient datasets for patient in test_object.get_patients(): # Get the files for the patient cur_patient_files = \ BatchProcessingController.get_patient_files(patient) # Create and setup the batch process process = BatchProcessPyRad2PyRadSR(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files) # Start the process process.start() # Get dataset directory directory = process.patient_dict_container.path # Get Pyradiomics SR, assert it exists file_name = 'Pyradiomics-SR.dcm' path = Path(directory).joinpath(file_name) assert os.path.exists(str(path)) # Delete Pyradiomics SR os.remove(path)
def test_batch_pyrad2csv(test_object): """ Test asserts creation of CSV as result of PyRad2CSV conversion. :param test_object: test_object function, for accessing the shared TestObject object. """ # Loop through patient datasets for patient in test_object.get_patients(): cur_patient_files = BatchProcessingController.get_patient_files( patient) # Create and setup the Batch Process process = BatchProcessPyRad2CSV(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files, test_object.batch_dir) # Target filename filename = 'Pyradiomics_' + test_object.timestamp + '.csv' # Set the filename process.set_filename(filename) # Start the process process.start() # Assert the resulting .csv file exists assert os.path.isfile( Path.joinpath(test_object.batch_dir, 'CSV', filename))
def test_batch_dvh2csv(test_object): """ Test asserts creation of CSV as result of DVH2CSV conversion. :param test_object: test_object function, for accessing the shared TestObject object. """ # Loop through patient datasets for patient in test_object.get_patients(): cur_patient_files = BatchProcessingController.get_patient_files( patient) # Create and setup the Batch Process process = BatchProcessDVH2CSV(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files, str(test_object.batch_dir)) # Target filename filename = 'DVHs_' + test_object.timestamp + '.csv' # Set the filename process.set_filename(filename) # Start the process process.start() # Assert the resulting .csv file exists assert os.path.isfile( Path.joinpath(test_object.batch_dir, 'CSV', filename)) # Assert that there is DVH data in the RT Dose rtdose = process.patient_dict_container.dataset['rtdose'] assert len(rtdose.DVHSequence) > 0
def test_batch_suv2roi(test_object): """ Test that at least 1 new ROI is created from SUV2ROI. :param test_object: test_object function, for accessing the shared TestObject object. """ # Loop through patient datasets for patient in test_object.get_patients(): # Get the files for the patient cur_patient_files = BatchProcessingController.get_patient_files( patient) # Create and setup the Batch Process patient_weight = 70000 process = BatchProcessSUV2ROI(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files, patient_weight) # Start the process result = process.start() if not result: return # Get rtss rtss = process.patient_dict_container.dataset['rtss'] # Get ROIS from rtss rois = [] for roi in rtss.StructureSetROISequence: rois.append(roi.ROIName) # Assert rtss contains new rois difference = set(test_object.iso_levels) - set(rois) assert len(difference) > 0
def test_batch_iso2roi(test_object): """ Test that at least 1 new ROI is created from ISO2ROI. :param test_object: test_object function, for accessing the shared TestObject object. """ # Loop through patient datasets for patient in test_object.get_patients(): # Get the files for the patient cur_patient_files = BatchProcessingController.get_patient_files( patient) # Create and setup the Batch Process process = BatchProcessISO2ROI(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files) # Start the process result = process.start() # Get rtss if not result: return rtss = process.patient_dict_container.dataset['rtss'] # Get ROIS from rtss rois = [] for roi in rtss.StructureSetROISequence: rois.append(roi.ROIName) # Assert rtss contains new rois difference = set(test_object.iso_levels) - set(rois) assert len(difference) > 0 # An rtss.dcm is being created during this batch test - this is # because the existing RTSTRUCT in DICOM-RT-02 does not match the # rest of the dataset. We need to delete this, or future tests # will fail. rtss_path = test_object.batch_dir.joinpath("DICOM-RT-02", "rtss.dcm") os.remove(rtss_path)
def test_batch_roi_name_to_fma_id(test_object): """ Test asserts an ROI changes name and one is deleted. :param test_object: test_object function, for accessing the shared TestObject object. """ # Get RTSS file path, count number of ROIs rtss_path = None for root, dirs, files in os.walk(test_object.batch_dir, topdown=True): for name in files: try: ds = dcmread(os.path.join(root, name)) if ds.SOPClassUID == '1.2.840.10008.5.1.4.1.1.481.3': rtss_path = os.path.join(root, name) break except (InvalidDicomError, FileNotFoundError): pass # Assert rtss exists assert rtss_path is not None assert os.path.exists(rtss_path) for patient in test_object.get_patients(): # Get the files for the patient cur_patient_files = BatchProcessingController.get_patient_files( patient) # Create and setup the Batch Process process = BatchProcessROIName2FMAID(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files) # Start the process status = process.start() assert status # Assert no ROIs called Lungs exists ds = dcmread(rtss_path) for roi in ds.StructureSetROISequence: assert roi.ROIName != 'Lungs'
def setup_ui(self, batch_window_instance): """ Sets up the UI for the batch processing window. """ # Get the appropriate stylesheet if platform.system() == 'Darwin': self.stylesheet_path = "res/stylesheet.qss" else: self.stylesheet_path = "res/stylesheet-win-linux.qss" self.stylesheet = open(resource_path(self.stylesheet_path)).read() # Create class variables self.file_path = "Select file path..." # Label font label_font = QtGui.QFont() label_font.setPixelSize(14) # Set the window icon window_icon = QtGui.QIcon() window_icon.addPixmap( QtGui.QPixmap(resource_path("res/images/icon.ico")), QtGui.QIcon.Normal, QtGui.QIcon.Off) # Set window properties batch_window_instance.setObjectName("BatchWindowInstance") batch_window_instance.setWindowIcon(window_icon) batch_window_instance.setWindowTitle("Batch Processing") batch_window_instance.resize(840, 530) # == Directory widgets # Directory label dir_label_text = "Select directory to perform batch processing on:" self.dir_info_label = QtWidgets.QLabel(dir_label_text) self.dir_info_label.setFont(label_font) # Directory line edit self.directory_input = QtWidgets.QLineEdit() self.directory_input.setText(self.file_path) self.directory_input.textChanged.connect(self.line_edit_changed) self.directory_input.setStyleSheet(self.stylesheet) # Label to display file search status self.search_progress_label = QtWidgets.QLabel("No directory is " "currently selected.") self.search_progress_label.setFont(label_font) # Browse button self.browse_button = QtWidgets.QPushButton("Change") self.browse_button.setObjectName("NormalButton") self.browse_button.setStyleSheet(self.stylesheet) # == Tab widgets # Tab widget self.tab_widget = CheckableTabWidget() self.tab_widget.tabBar().setObjectName("batch-tabs") self.tab_widget.setStyleSheet(self.stylesheet) # Tabs self.iso2roi_tab = ISO2ROIOptions() self.suv2roi_tab = SUV2ROIOptions() self.dvh2csv_tab = DVH2CSVOptions() self.pyrad2csv_tab = PyRad2CSVOptions() self.pyrad2pyradSR_tab = Pyrad2PyradSROptions() self.csv2clinicaldatasr_tab = CSV2ClinicalDataSROptions() self.clinicaldatasr2csv_tab = ClinicalDataSR2CSVOptions() self.batchnamecleaning_tab = ROINameCleaningOptions() self.batchname2fma_tab = ROIName2FMAIDOptions() # Add tabs to tab widget self.tab_widget.addTab(self.iso2roi_tab, "ISO2ROI") self.tab_widget.addTab(self.suv2roi_tab, "SUV2ROI") self.tab_widget.addTab(self.dvh2csv_tab, "DVH2CSV") self.tab_widget.addTab(self.pyrad2csv_tab, "PyRad2CSV") self.tab_widget.addTab(self.pyrad2pyradSR_tab, "Pyrad2Pyrad-SR") self.tab_widget.addTab(self.csv2clinicaldatasr_tab, "CSV2ClinicalData-SR") self.tab_widget.addTab(self.clinicaldatasr2csv_tab, "ClinicalData-SR2CSV") self.tab_widget.addTab(self.batchnamecleaning_tab, "ROI Name Cleaning") self.tab_widget.addTab(self.batchname2fma_tab, "ROI Name to FMA ID") # == Bottom widgets # Info text info_text = "Batch Processing will be performed on datasets in the " info_text += "selected directory." self.info_label = QtWidgets.QLabel(info_text) self.info_label.setFont(label_font) # Back button self.back_button = QtWidgets.QPushButton("Exit") self.back_button.setObjectName("BatchExitButton") self.back_button.setMaximumWidth(80) self.back_button.setStyleSheet(self.stylesheet) self.back_button.setProperty("QPushButtonClass", "fail-button") # Begin button self.begin_button = QtWidgets.QPushButton("Begin") self.begin_button.setObjectName("BeginButton") self.begin_button.setMaximumWidth(100) self.begin_button.setStyleSheet(self.stylesheet) self.begin_button.setProperty("QPushButtonClass", "success-button") self.begin_button.setEnabled(False) # == Set layout # Create layouts self.layout = QtWidgets.QVBoxLayout() self.directory_layout = QtWidgets.QGridLayout() self.middle_layout = QtWidgets.QVBoxLayout() self.bottom_layout = QtWidgets.QGridLayout() # Add top text self.layout.addWidget(self.dir_info_label) # Add directory widgets self.directory_layout.addWidget(self.directory_input) self.directory_layout.addWidget(self.browse_button, 0, 1) self.directory_layout.addWidget(self.search_progress_label, 1, 0) self.layout.addLayout(self.directory_layout) # Add middle widgets (patient count, tabs) self.middle_layout.addWidget(self.tab_widget) self.layout.addLayout(self.middle_layout) # Add bottom widgets (buttons) self.bottom_layout.addWidget(self.info_label, 0, 0, 2, 4) self.bottom_layout.addWidget(self.back_button, 2, 2, 1, 1) self.bottom_layout.addWidget(self.begin_button, 2, 3, 1, 1) self.layout.addLayout(self.bottom_layout) # Connect buttons to functions self.browse_button.clicked.connect(self.show_file_browser) self.begin_button.clicked.connect(self.confirm_button_clicked) self.back_button.clicked.connect( lambda: QtCore.QCoreApplication.exit(0)) # Set window layout batch_window_instance.setLayout(self.layout) # Create batch processing controller, enable the processing self.batch_processing_controller = BatchProcessingController()
class UIBatchProcessingWindow(object): """ This class contains the user interface for the batch processing window. """ def setup_ui(self, batch_window_instance): """ Sets up the UI for the batch processing window. """ # Get the appropriate stylesheet if platform.system() == 'Darwin': self.stylesheet_path = "res/stylesheet.qss" else: self.stylesheet_path = "res/stylesheet-win-linux.qss" self.stylesheet = open(resource_path(self.stylesheet_path)).read() # Create class variables self.file_path = "Select file path..." # Label font label_font = QtGui.QFont() label_font.setPixelSize(14) # Set the window icon window_icon = QtGui.QIcon() window_icon.addPixmap( QtGui.QPixmap(resource_path("res/images/icon.ico")), QtGui.QIcon.Normal, QtGui.QIcon.Off) # Set window properties batch_window_instance.setObjectName("BatchWindowInstance") batch_window_instance.setWindowIcon(window_icon) batch_window_instance.setWindowTitle("Batch Processing") batch_window_instance.resize(840, 530) # == Directory widgets # Directory label dir_label_text = "Select directory to perform batch processing on:" self.dir_info_label = QtWidgets.QLabel(dir_label_text) self.dir_info_label.setFont(label_font) # Directory line edit self.directory_input = QtWidgets.QLineEdit() self.directory_input.setText(self.file_path) self.directory_input.textChanged.connect(self.line_edit_changed) self.directory_input.setStyleSheet(self.stylesheet) # Label to display file search status self.search_progress_label = QtWidgets.QLabel("No directory is " "currently selected.") self.search_progress_label.setFont(label_font) # Browse button self.browse_button = QtWidgets.QPushButton("Change") self.browse_button.setObjectName("NormalButton") self.browse_button.setStyleSheet(self.stylesheet) # == Tab widgets # Tab widget self.tab_widget = CheckableTabWidget() self.tab_widget.tabBar().setObjectName("batch-tabs") self.tab_widget.setStyleSheet(self.stylesheet) # Tabs self.iso2roi_tab = ISO2ROIOptions() self.suv2roi_tab = SUV2ROIOptions() self.dvh2csv_tab = DVH2CSVOptions() self.pyrad2csv_tab = PyRad2CSVOptions() self.pyrad2pyradSR_tab = Pyrad2PyradSROptions() self.csv2clinicaldatasr_tab = CSV2ClinicalDataSROptions() self.clinicaldatasr2csv_tab = ClinicalDataSR2CSVOptions() self.batchnamecleaning_tab = ROINameCleaningOptions() self.batchname2fma_tab = ROIName2FMAIDOptions() # Add tabs to tab widget self.tab_widget.addTab(self.iso2roi_tab, "ISO2ROI") self.tab_widget.addTab(self.suv2roi_tab, "SUV2ROI") self.tab_widget.addTab(self.dvh2csv_tab, "DVH2CSV") self.tab_widget.addTab(self.pyrad2csv_tab, "PyRad2CSV") self.tab_widget.addTab(self.pyrad2pyradSR_tab, "Pyrad2Pyrad-SR") self.tab_widget.addTab(self.csv2clinicaldatasr_tab, "CSV2ClinicalData-SR") self.tab_widget.addTab(self.clinicaldatasr2csv_tab, "ClinicalData-SR2CSV") self.tab_widget.addTab(self.batchnamecleaning_tab, "ROI Name Cleaning") self.tab_widget.addTab(self.batchname2fma_tab, "ROI Name to FMA ID") # == Bottom widgets # Info text info_text = "Batch Processing will be performed on datasets in the " info_text += "selected directory." self.info_label = QtWidgets.QLabel(info_text) self.info_label.setFont(label_font) # Back button self.back_button = QtWidgets.QPushButton("Exit") self.back_button.setObjectName("BatchExitButton") self.back_button.setMaximumWidth(80) self.back_button.setStyleSheet(self.stylesheet) self.back_button.setProperty("QPushButtonClass", "fail-button") # Begin button self.begin_button = QtWidgets.QPushButton("Begin") self.begin_button.setObjectName("BeginButton") self.begin_button.setMaximumWidth(100) self.begin_button.setStyleSheet(self.stylesheet) self.begin_button.setProperty("QPushButtonClass", "success-button") self.begin_button.setEnabled(False) # == Set layout # Create layouts self.layout = QtWidgets.QVBoxLayout() self.directory_layout = QtWidgets.QGridLayout() self.middle_layout = QtWidgets.QVBoxLayout() self.bottom_layout = QtWidgets.QGridLayout() # Add top text self.layout.addWidget(self.dir_info_label) # Add directory widgets self.directory_layout.addWidget(self.directory_input) self.directory_layout.addWidget(self.browse_button, 0, 1) self.directory_layout.addWidget(self.search_progress_label, 1, 0) self.layout.addLayout(self.directory_layout) # Add middle widgets (patient count, tabs) self.middle_layout.addWidget(self.tab_widget) self.layout.addLayout(self.middle_layout) # Add bottom widgets (buttons) self.bottom_layout.addWidget(self.info_label, 0, 0, 2, 4) self.bottom_layout.addWidget(self.back_button, 2, 2, 1, 1) self.bottom_layout.addWidget(self.begin_button, 2, 3, 1, 1) self.layout.addLayout(self.bottom_layout) # Connect buttons to functions self.browse_button.clicked.connect(self.show_file_browser) self.begin_button.clicked.connect(self.confirm_button_clicked) self.back_button.clicked.connect( lambda: QtCore.QCoreApplication.exit(0)) # Set window layout batch_window_instance.setLayout(self.layout) # Create batch processing controller, enable the processing self.batch_processing_controller = BatchProcessingController() def show_file_browser(self): """ Show the file browser for selecting a folder for the Onko default directory. """ # Open a file dialog and return chosen directory folder = \ QtWidgets.QFileDialog.getExistingDirectory(None, 'Choose Directory ..', '') # If chosen directory is nothing (user clicked cancel) set to # user home if folder == "": folder = expanduser("~") # Update file path self.file_path = folder # Update directory text self.directory_input.setText(self.file_path) def line_edit_changed(self): """ When the line edit box is changed, update related fields, start searching the directory. """ self.file_path = self.directory_input.text() self.dvh2csv_tab.set_dvh_output_location(self.file_path, False) self.pyrad2csv_tab.set_pyrad_output_location(self.file_path, False) self.begin_button.setEnabled(False) self.batch_processing_controller.set_dicom_structure(None) self.batch_processing_controller.load_patient_files( self.file_path, self.search_progress, self.search_completed) def search_progress(self, progress_update): """ Changed the progress label :param progress_update: current progress of file search """ self.search_progress_label.setText("Loading selected directory... ( " "%s files searched)" % progress_update) def search_completed(self, dicom_structure): """ Called when patient files are loaded :param dicom_structure: DICOMStructure """ if dicom_structure: self.batch_processing_controller.set_dicom_structure( dicom_structure) self.begin_button.setEnabled(True) self.search_progress_label.setText("%s patients found." % len(dicom_structure.patients)) # Update tables self.suv2roi_tab.populate_table(dicom_structure) # Update the batch name cleaning table batch_directory = self.directory_input.text() self.batchnamecleaning_tab.populate_table(dicom_structure, batch_directory) else: self.search_progress_label.setText("No patients were found.") self.batch_processing_controller.set_dicom_structure(None) def confirm_button_clicked(self): """ Executes when the confirm button is clicked. """ processes = [ 'iso2roi', 'suv2roi', 'dvh2csv', 'pyrad2csv', 'pyrad2pyrad-sr', 'csv2clinicaldata-sr', 'clinicaldata-sr2csv', 'roinamecleaning', 'roiname2fmaid' ] selected_processes = [] suv2roi_weights = self.suv2roi_tab.get_patient_weights() # Return if SUV2ROI weights is None. Alert user weights are incorrect. if suv2roi_weights is None: self.show_invalid_weight_dialog() return # Get the selected processes for i in range(self.tab_widget.count()): if self.tab_widget.isChecked(i): selected_processes.append(processes[i]) # Save the changed settings self.iso2roi_tab.save_isodoses() file_directories = { "batch_path": self.file_path, "dvh_output_path": self.dvh2csv_tab.get_dvh_output_location(), "pyrad_output_path": self.pyrad2csv_tab.get_pyrad_output_location(), 'clinical_data_input_path': self.csv2clinicaldatasr_tab.get_csv_input_location(), 'clinical_data_output_path': self.clinicaldatasr2csv_tab.get_csv_output_location() } # Setup the batch processing controller self.batch_processing_controller.set_file_paths(file_directories) self.batch_processing_controller.set_processes(selected_processes) self.batch_processing_controller.set_suv2roi_weights(suv2roi_weights) # Set batch ROI name cleaning options if selected if 'roinamecleaning' in selected_processes: # Get ROIs, datasets, options name_cleaning_options = {} roi_name_table = self.batchnamecleaning_tab.table_roi for i in range(roi_name_table.rowCount()): # Get current ROI name and what to do with it roi_name = roi_name_table.item(i, 0).text() option = roi_name_table.cellWidget(i, 1).currentIndex() # Get new name text if isinstance(roi_name_table.cellWidget(i, 2), ROINameCleaningPrefixEntryField): new_name = roi_name[0:3] \ + roi_name_table.cellWidget(i, 2).text() # Remove any whitespace, replace with underscores new_name = '_'.join(new_name.split()) else: new_name = roi_name_table.cellWidget(i, 2).currentText() # Get the dataset(s) the ROI is in dataset_list = [] dataset_combo_box = roi_name_table.cellWidget(i, 3) rtss_path = self.directory_input.text() for index in range(dataset_combo_box.count()): dataset_list.append(rtss_path + dataset_combo_box.itemText(index)) if roi_name not in name_cleaning_options.keys(): name_cleaning_options[roi_name] = [] for item in dataset_list: name_cleaning_options[roi_name].append( [option, new_name, item]) # Set batch name cleaning parameters in the batch processing # controller. self.batch_processing_controller.set_name_cleaning_options( name_cleaning_options) # Enable processing self.batch_processing_controller.start_processing() def show_invalid_weight_dialog(self): """ Shows a dialog informing the user that an entered weight in the SUV2ROI tab is invalid (either negative or not a number). """ button_reply = \ QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning, "Invalid Patient Weight", "Please enter a valid patient weight.", QtWidgets.QMessageBox.StandardButton.Ok, self) button_reply.button( QtWidgets.QMessageBox.StandardButton.Ok).setStyleSheet( self.stylesheet) button_reply.exec_()
def test_batch_clinicaldatasr2csv(test_object): """ Test asserts a CSV is created with dummy clinical data. Test deletes CSV after running. :param test_object: test_object function, for accessing the shared TestObject object. """ # Get patient ID for dummy SR patient_id = None for root, dirs, files in os.walk(test_object.batch_dir, topdown=True): if patient_id is not None: break for name in files: try: ds = dcmread(os.path.join(root, name)) if hasattr(ds, 'PatientID') \ and ds.SOPClassUID == '1.2.840.10008.5.1.4.1.1.2': patient_id = ds.PatientID break except (InvalidDicomError, FileNotFoundError): pass # Assert a patient ID was found assert patient_id is not None # Create dummy SR dcm_path = test_object.batch_dir.joinpath("Clinical-Data-SR.dcm") text_data = "MD5Hash: " + patient_id + "\n" text_data += "Age: 20\nNationality: Australian\n" dicom_sr = DICOMStructuredReport.generate_dicom_sr(dcm_path, ds, text_data, "CLINICAL-DATA") dicom_sr.save_as(dcm_path) # Assert dummy SR was created assert os.path.exists(dcm_path) # Reload patient (to get newly created SR) patients = DICOMDirectorySearch.get_dicom_structure( test_object.batch_dir, test_object.DummyProgressWindow, test_object.DummyProgressWindow) # Loop through each patient for patient in patients.patients.values(): # Get current patient files cur_patient_files = BatchProcessingController.get_patient_files( patient) # Create Batch Clinical Data SR to CSV object process = \ BatchProcessClinicalDataSR2CSV(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files, test_object.batch_dir) # Start the process, assert it finished successfully status = process.start() assert status # Assert CSV exists csv_path = test_object.batch_dir.joinpath("ClinicalData.csv") assert os.path.exists(csv_path) # Assert data is correct in CSV with open(csv_path, newline="") as stream: data = list(csv.reader(stream)) stream.close() assert data[0] == ["MD5Hash", "Age", "Nationality"] assert data[1] == [patient_id, "20", "Australian"] # Delete dummy CSV and SR os.remove(csv_path) os.remove(dcm_path)
def test_batch_csv2clinicaldatasr(test_object): """ Test asserts an SR is created with dummy clinical data. Test deletes SR after running. :param test_object: test_object function, for accessing the shared TestObject object. """ # Get patient ID for dummy CSV patient_id = None for root, dirs, files in os.walk(test_object.batch_dir, topdown=True): if patient_id is not None: break for name in files: try: ds = dcmread(os.path.join(root, name)) if hasattr(ds, 'PatientID'): patient_id = ds.PatientID break except (InvalidDicomError, FileNotFoundError): pass assert patient_id is not None # Create dummy CSV csv_path = test_object.batch_dir.joinpath("dummy_cd.csv") data = [['MD5Hash', 'Age', 'Nationality'], [patient_id, '20', 'Australian']] with open(csv_path, "w", newline="") as f: writer = csv.writer(f) writer.writerow(data[0]) writer.writerow(data[1]) f.close() # Loop through each patient for patient in test_object.get_patients(): # Get current patient files cur_patient_files = BatchProcessingController.get_patient_files( patient) # Create Batch CSV to Clinical Data SR object process = \ BatchProcessCSV2ClinicalDataSR(test_object.DummyProgressWindow, test_object.DummyProgressWindow, cur_patient_files, csv_path) # Start the process, assert it finished successfully status = process.start() assert status # Assert SR exists sr_path = test_object.batch_dir.joinpath("DICOM-RT-02", "Clinical-Data-SR.dcm") assert os.path.exists(sr_path) # Assert data is correct in SR text_data = "MD5Hash: " + patient_id + "\n" text_data += "Age: 20\nNationality: Australian\n" sr_ds = dcmread(sr_path) assert sr_ds.SeriesDescription == "CLINICAL-DATA" sr_text_data = sr_ds.ContentSequence[0].TextValue assert text_data == sr_text_data # Delete dummy CSV and SR os.remove(csv_path) os.remove(sr_path)