From e949438c5d23c32ac8cdebf3d59cde045d64d572 Mon Sep 17 00:00:00 2001 From: dbouget Date: Wed, 2 Oct 2024 16:41:34 +0200 Subject: [PATCH] Integration tests for data loading + CI attempt[skip ci] --- .github/workflows/build_ubuntu.yml | 7 + .../MRISeriesLayerWidget.py | 9 +- .../TumorCharacteristicsWidget.py | 2 +- .../CustomQDialog/ImportFoldersQDialog.py | 1 + integration_tests/__init__.py | 0 integration_tests/empty_patient_test.py | 231 ++++++++++++++++++ integration_tests/patient_loading_test.py | 132 ++++++++++ tests/patient_creation_tests.py | 50 ---- .../InvestigationTimestampStructure.py | 9 +- .../PatientParametersStructure.py | 5 +- utils/data_structures/ReportingStructure.py | 62 ++--- .../StudyParametersStructure.py | 23 +- 12 files changed, 430 insertions(+), 101 deletions(-) create mode 100644 integration_tests/__init__.py create mode 100644 integration_tests/empty_patient_test.py create mode 100644 integration_tests/patient_loading_test.py delete mode 100644 tests/patient_creation_tests.py diff --git a/.github/workflows/build_ubuntu.yml b/.github/workflows/build_ubuntu.yml index 55b1373..e29b96f 100644 --- a/.github/workflows/build_ubuntu.yml +++ b/.github/workflows/build_ubuntu.yml @@ -102,6 +102,13 @@ jobs: export QT_QPA_PLATFORM="offscreen" cd ${{github.workspace}}/tests && python software_launch_test.py + - name: Integration tests + env: + DISPLAY: ':1' + run: | + pip install pytest + pytest ${{github.workspace}}/integration_tests + - name: Make installer run: | mkdir -p assets/Raidionics_ubuntu/usr/local/bin diff --git a/gui/SinglePatientComponent/LayersInteractorSidePanel/MRIVolumesInteractor/MRISeriesLayerWidget.py b/gui/SinglePatientComponent/LayersInteractorSidePanel/MRIVolumesInteractor/MRISeriesLayerWidget.py index e8446f8..b933bf7 100644 --- a/gui/SinglePatientComponent/LayersInteractorSidePanel/MRIVolumesInteractor/MRISeriesLayerWidget.py +++ b/gui/SinglePatientComponent/LayersInteractorSidePanel/MRIVolumesInteractor/MRISeriesLayerWidget.py @@ -107,7 +107,7 @@ def __set_layout_dimensions(self): # logging.debug("MRISeriesLayerWidget size set to {}.\n".format(self.size())) def __set_connections(self): - self.display_name_lineedit.textEdited.connect(self.on_name_change) + self.display_name_lineedit.returnPressed.connect(self.on_name_change) self.display_toggle_radiobutton.toggled.connect(self.on_visibility_toggled) self.options_pushbutton.clicked.connect(self.on_options_clicked) self.sequence_type_combobox.currentTextChanged.connect(self.on_sequence_type_changed) @@ -343,10 +343,11 @@ def on_visibility_toggled(self, state): self.contrast_adjuster_pushbutton.setEnabled(self.display_toggle_radiobutton.isChecked()) logging.info("[MRISeriesLayerWidget] Visibility toggled to {} for {}".format(state, self.uid)) - def on_name_change(self, text): + def on_name_change(self): # @TODO. Should there be a check that the name is available? - SoftwareConfigResources.getInstance().get_active_patient().get_mri_by_uid(self.uid).display_name = text - self.display_name_changed.emit(self.uid, text) + new_name = self.display_name_lineedit.text() + SoftwareConfigResources.getInstance().get_active_patient().get_mri_by_uid(self.uid).display_name = new_name + self.display_name_changed.emit(self.uid, new_name) def on_sequence_type_changed(self, text) -> None: SoftwareConfigResources.getInstance().get_active_patient().get_mri_by_uid(self.uid).set_sequence_type(text) diff --git a/gui/SinglePatientComponent/PatientResultsSidePanel/TumorCharacteristicsWidget.py b/gui/SinglePatientComponent/PatientResultsSidePanel/TumorCharacteristicsWidget.py index c94a8ed..b1909f6 100644 --- a/gui/SinglePatientComponent/PatientResultsSidePanel/TumorCharacteristicsWidget.py +++ b/gui/SinglePatientComponent/PatientResultsSidePanel/TumorCharacteristicsWidget.py @@ -793,7 +793,7 @@ def populate_from_report(self) -> None: # BrainGrid structures self.braingridstructures_collapsiblegroupbox.clear_content_layout() - if UserPreferencesStructure.getInstance().compute_braingrid_structures: + if 'BrainGrid' in list(report_json['Main']['Total'].keys()): #UserPreferencesStructure.getInstance().compute_braingrid_structures: lay = QHBoxLayout() label_header = QLabel("Infiltration count:") label_header.setStyleSheet(""" diff --git a/gui/UtilsWidgets/CustomQDialog/ImportFoldersQDialog.py b/gui/UtilsWidgets/CustomQDialog/ImportFoldersQDialog.py index 0bd7dbd..bae1a01 100644 --- a/gui/UtilsWidgets/CustomQDialog/ImportFoldersQDialog.py +++ b/gui/UtilsWidgets/CustomQDialog/ImportFoldersQDialog.py @@ -192,6 +192,7 @@ def __on_exit_accept_clicked(self) -> None: self.load_progressbar.setValue(0) for input_folderpath in input_folderpaths: + logging.debug("[ImportFoldersQDialog] Parsing the current folder input with value: {}".format(input_folderpath)) try: folders_in_path = [] diff --git a/integration_tests/__init__.py b/integration_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/integration_tests/empty_patient_test.py b/integration_tests/empty_patient_test.py new file mode 100644 index 0000000..3b0b3ec --- /dev/null +++ b/integration_tests/empty_patient_test.py @@ -0,0 +1,231 @@ +import os +import shutil +from time import sleep + +import requests +import zipfile + +import pytest +from PySide6.QtCore import Qt + +from gui.RaidionicsMainWindow import RaidionicsMainWindow +from gui.UtilsWidgets.CustomQDialog.ImportDataQDialog import ImportDataQDialog +from utils.software_config import SoftwareConfigResources +from utils.data_structures.UserPreferencesStructure import UserPreferencesStructure + +def_loc = UserPreferencesStructure.getInstance().user_home_location + +@pytest.fixture +def test_location(): + test_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'integrationtests') + UserPreferencesStructure.getInstance().user_home_location = test_loc + if os.path.exists(test_loc): + shutil.rmtree(test_loc) + os.makedirs(test_loc) + return test_loc + +@pytest.fixture +def test_data_folder(): + test_data_url = 'https://github.com/raidionics/Raidionics-models/releases/download/1.2.0/Samples-Raidionics-ApprovedExample-v1.2.zip' + test_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'integrationtests') + test_data_dir = os.path.join(test_dir, 'ApprovedExample') + if os.path.exists(test_data_dir) and len(os.listdir(test_data_dir)) > 0: + return test_data_dir + + archive_dl_dest = os.path.join(test_dir, 'raidionics_resources.zip') + headers = {} + response = requests.get(test_data_url, headers=headers, stream=True) + response.raise_for_status() + if response.status_code == requests.codes.ok: + with open(archive_dl_dest, "wb") as f: + for chunk in response.iter_content(chunk_size=1048576): + f.write(chunk) + with zipfile.ZipFile(archive_dl_dest, 'r') as zip_ref: + zip_ref.extractall(test_dir) + return test_data_dir + + +@pytest.fixture +def window(): + """ + + """ + window = RaidionicsMainWindow() + return window + + +def test_empty_patient_creation(qtbot, test_location, window): + """ + Creation of a new empty patient. + """ + qtbot.addWidget(window) + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + window.single_patient_widget.results_panel.add_empty_patient_action.trigger() + assert len(SoftwareConfigResources.getInstance().patients_parameters) == 1 + +def test_empty_patient_renaming(qtbot, test_location, window): + """ + Creation of a new empty patient followed by renaming. + """ + qtbot.addWidget(window) + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + window.single_patient_widget.results_panel.add_empty_patient_action.trigger() + window.single_patient_widget.results_panel.patient_results_widgets[list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].patient_name_lineedit.setText("Patient1") + qtbot.keyClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].patient_name_lineedit, Qt.Key_Enter) + assert SoftwareConfigResources.getInstance().get_active_patient().display_name == "Patient1" + + qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) + +def test_empty_patient_timestamp_creation_and_renaming(qtbot, test_location, window): + """ + Creation of a new timestamp for an empty patient and renaming.| + """ + qtbot.addWidget(window) + + # Entering the single patient widget view + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + + # Clicking on the Import patient > Empty patient button + window.single_patient_widget.results_panel.add_empty_patient_action.trigger() + qtbot.mouseClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamp_add_pushbutton, Qt.MouseButton.LeftButton) + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].timestamp_name_lineedit.setText("PreOp") + qtbot.keyClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].timestamp_name_lineedit, Qt.Key_Enter) + assert SoftwareConfigResources.getInstance().get_active_patient()._investigation_timestamps[SoftwareConfigResources.getInstance().get_active_patient().get_active_investigation_timestamp_uid()].display_name == "PreOp" + + qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) + +def test_empty_patient_timestamp_selection_and_removal(qtbot, test_location, window): + """ + Creation of multiple timestamps for an empty patient, renaming, swapping between them, and removing them. + """ + qtbot.addWidget(window) + + # Entering the single patient widget view + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + + # Clicking on the Import patient > Empty patient button + window.single_patient_widget.results_panel.add_empty_patient_action.trigger() + + # Adding a new timestamp and renaming to PreOp + qtbot.mouseClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamp_add_pushbutton, Qt.MouseButton.LeftButton) + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].timestamp_name_lineedit.setText("PreOp") + qtbot.keyClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].timestamp_name_lineedit, Qt.Key_Enter) + + # Adding a new timestamp and renaming to PostOp (will automatically display the new timestamp) + qtbot.mouseClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamp_add_pushbutton, Qt.MouseButton.LeftButton) + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[1]].timestamp_name_lineedit.setText("PostOp") + qtbot.keyClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[1]].timestamp_name_lineedit, Qt.Key_Enter) + + # Setting the first timestamp in view (i.e., PreOp) + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamp_selector_combobox.setCurrentIndex(0) + assert SoftwareConfigResources.getInstance().get_active_patient()._investigation_timestamps[ + SoftwareConfigResources.getInstance().get_active_patient().get_active_investigation_timestamp_uid()].display_name == "PreOp" + + # Deleting the current timestamp (i.e., PreOp) + qtbot.mouseClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamp_remove_pushbutton, Qt.MouseButton.LeftButton) + assert SoftwareConfigResources.getInstance().get_active_patient()._investigation_timestamps[ + SoftwareConfigResources.getInstance().get_active_patient().get_active_investigation_timestamp_uid()].display_name == "PostOp" + + # Deleting the current timestamp (i.e., PostOp) + qtbot.mouseClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamp_remove_pushbutton, Qt.MouseButton.LeftButton) + assert len(list(SoftwareConfigResources.getInstance().get_active_patient()._investigation_timestamps.keys())) == 0 + + # Saving the latest modifications to the patient on disk by pressing the disk icon + qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) + +def test_empty_patient_timestamp_data_inclusion(qtbot, test_location, test_data_folder, window): + """ + Creation of a new timestamp for an empty patient and importing two radiological volumes. + """ + qtbot.addWidget(window) + + # Entering the single patient widget view + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + + # Clicking on the Import patient > Empty patient button + window.single_patient_widget.results_panel.add_empty_patient_action.trigger() + + # Adding a new timestamp and renaming to PreOp + qtbot.mouseClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamp_add_pushbutton, Qt.MouseButton.LeftButton) + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].timestamp_name_lineedit.setText("PreOp") + qtbot.keyClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].timestamp_name_lineedit, Qt.Key_Enter) + + # Importing two radiological volumes to the current timestamp (e.g., PreOp) + t1_sample_mri_filename = os.path.join(test_data_folder, 'Raw', 'Case27-T1.nii.gz') + flair_sample_mri_filename = os.path.join(test_data_folder, 'Raw', 'Case27-FLAIR.nii.gz') + window.single_patient_widget.import_data_dialog.setup_interface_from_files([t1_sample_mri_filename, flair_sample_mri_filename]) + window.single_patient_widget.import_data_dialog.__on_exit_accept_clicked() + assert len(list(SoftwareConfigResources.getInstance().get_active_patient().mri_volumes.keys())) == 2 + + # Using the ComboBox to change the radiological volume sequence from T1-w to T1-CE + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].volumes_collapsiblegroupbox.volumes_widget.keys())[0]].sequence_type_combobox.setCurrentIndex(1) + + # Changing the display name for the T1-CE MRI volume to case27-t1c + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget.keys())[0]].display_name_lineedit.setText("case27-t1c") + qtbot.keyClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget.keys())[0]].display_name_lineedit, Qt.Key_Enter) + + # Changing the display name for the FLAIR MRI volume to case27-flair + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget.keys())[1]].display_name_lineedit.setText("case27-flair") + qtbot.keyClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget.keys())[1]].display_name_lineedit, Qt.Key_Enter) + + t1c_display_name = window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[0]].volumes_collapsiblegroupbox.volumes_widget.keys())[0]].display_name_lineedit.text() + assert t1c_display_name == "case27-t1c" + assert SoftwareConfigResources.getInstance().get_active_patient().mri_volumes[SoftwareConfigResources.getInstance().get_active_patient().get_mri_by_display_name(t1c_display_name)].get_sequence_type_str() == "T1-CE" + + # Setting the FLAIR input volume visible + qtbot.mouseClick(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget.keys())[1]].display_toggle_radiobutton, Qt.MouseButton.LeftButton) + assert window.single_patient_widget.center_panel.display_area_widget.displayed_image_uid == SoftwareConfigResources.getInstance().get_active_patient().mri_volumes[SoftwareConfigResources.getInstance().get_active_patient().get_mri_by_display_name("case27-flair")].unique_id + + # Removing the FLAIR radiological input from the timestamp + window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget[ + list(window.single_patient_widget.layers_panel.timestamp_layer_widget.timestamps_widget.keys())[ + 0]].volumes_collapsiblegroupbox.volumes_widget.keys())[1]].delete_layer_action.trigger() + assert window.single_patient_widget.center_panel.display_area_widget.displayed_image_uid == SoftwareConfigResources.getInstance().get_active_patient().mri_volumes[SoftwareConfigResources.getInstance().get_active_patient().get_mri_by_display_name("case27-t1c")].unique_id + + # Saving the latest modifications to the patient on disk by pressing the disk icon + qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) + +def test_cleanup(): + UserPreferencesStructure.getInstance().user_home_location = def_loc + test_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'integrationtests') + if os.path.exists(test_loc): + shutil.rmtree(test_loc) diff --git a/integration_tests/patient_loading_test.py b/integration_tests/patient_loading_test.py new file mode 100644 index 0000000..1cf97ec --- /dev/null +++ b/integration_tests/patient_loading_test.py @@ -0,0 +1,132 @@ +import os +import shutil +from time import sleep + +import requests +import zipfile + +import pytest +from PySide6.QtCore import Qt + +from gui.RaidionicsMainWindow import RaidionicsMainWindow +from gui.UtilsWidgets.CustomQDialog.ImportDataQDialog import ImportDataQDialog +from gui.UtilsWidgets.CustomQDialog.ImportFoldersQDialog import ImportFolderLineWidget +from utils.software_config import SoftwareConfigResources +from utils.data_structures.UserPreferencesStructure import UserPreferencesStructure + + +def_loc = UserPreferencesStructure.getInstance().user_home_location + +@pytest.fixture +def test_location(): + test_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'integrationtests') + UserPreferencesStructure.getInstance().user_home_location = test_loc + if os.path.exists(test_loc): + shutil.rmtree(test_loc) + os.makedirs(test_loc) + return test_loc + +@pytest.fixture +def test_data_folder(): + test_data_url = 'https://github.com/raidionics/Raidionics-models/releases/download/1.2.0/Samples-Raidionics-ApprovedExample-v1.2.zip' + test_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'integrationtests') + test_data_dir = os.path.join(test_dir, 'ApprovedExample') + if os.path.exists(test_data_dir) and len(os.listdir(test_data_dir)) > 0: + return test_data_dir + + archive_dl_dest = os.path.join(test_dir, 'raidionics_resources.zip') + headers = {} + response = requests.get(test_data_url, headers=headers, stream=True) + response.raise_for_status() + if response.status_code == requests.codes.ok: + with open(archive_dl_dest, "wb") as f: + for chunk in response.iter_content(chunk_size=1048576): + f.write(chunk) + with zipfile.ZipFile(archive_dl_dest, 'r') as zip_ref: + zip_ref.extractall(test_dir) + return test_data_dir + + +@pytest.fixture +def window(): + """ + + """ + window = RaidionicsMainWindow() + return window + + +def test_patient_loading_from_files(qtbot, test_location, test_data_folder, window): + """ + + """ + qtbot.addWidget(window) + # Entering the single patient widget view + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + + # Importing MRI files from Import patient > Other data type (*.nii) + # window.single_patient_widget.results_panel.add_other_data_action.trigger() <= Cannot use the actual pushbutton action as it would open the QDialog... + window.single_patient_widget.results_panel.on_add_new_empty_patient() + t1_sample_mri_filename = os.path.join(test_data_folder, 'Raw', 'Case27-T1.nii.gz') + flair_sample_mri_filename = os.path.join(test_data_folder, 'Raw', 'Case27-FLAIR.nii.gz') + window.single_patient_widget.import_data_dialog.setup_interface_from_files([t1_sample_mri_filename, flair_sample_mri_filename]) + window.single_patient_widget.import_data_dialog.__on_exit_accept_clicked() + assert len(list(SoftwareConfigResources.getInstance().get_active_patient().mri_volumes.keys())) == 2 + + # Saving the latest modifications to the patient on disk by pressing the disk icon + qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) + +def test_patient_loading_from_folder(qtbot, test_location, test_data_folder, window): + """ + + """ + qtbot.addWidget(window) + # Entering the single patient widget view + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + + # Importing MRI files from Import patient > Other data type (*.nii) + # window.single_patient_widget.results_panel.add_folder_data_action.trigger() <= Cannot use the actual pushbutton action as it would open the QDialog... + window.single_patient_widget.results_panel.on_add_new_empty_patient() + sample_folder = os.path.join(test_data_folder, 'Raw') + + window.single_patient_widget.import_folder_dialog.reset() + window.single_patient_widget.import_folder_dialog.set_parsing_mode("single") + window.single_patient_widget.import_folder_dialog.set_target_type("regular") + wid = ImportFolderLineWidget() + wid.filepath_lineedit.setText(sample_folder) + window.single_patient_widget.import_folder_dialog.import_scrollarea_layout.insertWidget(window.single_patient_widget.import_folder_dialog.import_scrollarea_layout.count() - 1, wid) + window.single_patient_widget.import_folder_dialog.__on_exit_accept_clicked() + assert len(list(SoftwareConfigResources.getInstance().get_patient_by_display_name("Raw").mri_volumes.keys())) == 2 + + # Saving the latest modifications to the patient on disk by pressing the disk icon + qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) + + +def test_patient_loading_from_raidionics(qtbot, test_location, test_data_folder, window): + """ + + """ + qtbot.addWidget(window) + # Entering the single patient widget view + qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) + + # Importing MRI files from Import patient > Other data type (*.nii) + # window.single_patient_widget.results_panel.add_raidionics_patient_action.trigger() <= Cannot use the actual pushbutton action as it would open the QDialog... + raidionics_filename = os.path.join(test_data_folder, 'Raidionics', "patients", "patient_1", "patient_1_scene.raidionics") + window.single_patient_widget.import_data_dialog.reset() + window.single_patient_widget.import_data_dialog.set_parsing_filter("patient") + window.single_patient_widget.import_data_dialog.setup_interface_from_files([raidionics_filename]) + window.single_patient_widget.import_data_dialog.__on_exit_accept_clicked() + assert len(list(SoftwareConfigResources.getInstance().get_patient_by_display_name("Patient 1").mri_volumes.keys())) == 2 + + # Saving the latest modifications to the patient on disk by pressing the disk icon + qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ + list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) + +def test_cleanup(): + UserPreferencesStructure.getInstance().user_home_location = def_loc + test_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'integrationtests') + if os.path.exists(test_loc): + shutil.rmtree(test_loc) diff --git a/tests/patient_creation_tests.py b/tests/patient_creation_tests.py deleted file mode 100644 index 5924d8e..0000000 --- a/tests/patient_creation_tests.py +++ /dev/null @@ -1,50 +0,0 @@ -import os -import shutil - -from PySide6.QtCore import Qt - -from gui.RaidionicsMainWindow import RaidionicsMainWindow -from utils.software_config import SoftwareConfigResources -from utils.data_structures.UserPreferencesStructure import UserPreferencesStructure - -def_loc = UserPreferencesStructure.getInstance().user_home_location -test_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'integrationtests') -UserPreferencesStructure.getInstance().user_home_location = test_loc -if os.path.exists(test_loc): - shutil.rmtree(test_loc) -os.makedirs(test_loc) - - -def test_empty_patient_creation(qtbot): - """ - Creation of a new empty patient. - """ - window = RaidionicsMainWindow() - window.show() - qtbot.addWidget(window) - qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) - window.single_patient_widget.results_panel.add_empty_patient_action.trigger() - assert len(SoftwareConfigResources.getInstance().patients_parameters) == 1 - -def test_empty_patient_renaming(qtbot): - """ - Creation of a new empty patient followed by renaming. - """ - window = RaidionicsMainWindow() - window.show() - qtbot.addWidget(window) - # window.__on_single_patient_clicked() - qtbot.mouseClick(window.welcome_widget.left_panel_single_patient_pushbutton, Qt.MouseButton.LeftButton) - # window.single_patient_widget.results_panel.on_add_new_empty_patient() - window.single_patient_widget.results_panel.add_empty_patient_action.trigger() - window.single_patient_widget.results_panel.patient_results_widgets[list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].patient_name_lineedit.setText("Patient1") - qtbot.keyClick(window.single_patient_widget.results_panel.patient_results_widgets[ - list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].patient_name_lineedit, Qt.Key_Enter) - qtbot.mouseClick(window.single_patient_widget.results_panel.patient_results_widgets[ - list(window.single_patient_widget.results_panel.patient_results_widgets.keys())[0]].save_patient_pushbutton, Qt.MouseButton.LeftButton) - assert SoftwareConfigResources.getInstance().get_active_patient().display_name == "Patient1" - -def test_cleanup(): - UserPreferencesStructure.getInstance().user_home_location = def_loc - if os.path.exists(test_loc): - shutil.rmtree(test_loc) \ No newline at end of file diff --git a/utils/data_structures/InvestigationTimestampStructure.py b/utils/data_structures/InvestigationTimestampStructure.py index 11db2b9..4df41e6 100644 --- a/utils/data_structures/InvestigationTimestampStructure.py +++ b/utils/data_structures/InvestigationTimestampStructure.py @@ -105,17 +105,16 @@ def display_name(self, text: str) -> None: self._display_name = text new_folder_name = self._display_name.strip().replace(" ", "") if os.path.exists(os.path.join(self._output_patient_folder, new_folder_name)): - # @TODO. Should return an error message, but then should be made into a set_display_name method.... - return + msg = 'A timestamp with requested name already exists in the destination folder.
' + \ + 'Requested name: {}.
'.format(new_folder_name) + \ + 'Destination folder: {}.'.format(os.path.dirname(self._output_patient_folder)) + raise ValueError(msg) if os.path.exists(os.path.join(self._output_patient_folder, self._folder_name)): shutil.move(src=os.path.join(self._output_patient_folder, self._folder_name), dst=os.path.join(self._output_patient_folder, new_folder_name)) logging.debug( "Unsaved changes - Investigation timestamp folder name changed from {} to {}".format(self._folder_name, new_folder_name)) - else: - raise ValueError("Folder not existing on disk at: {}".format(os.path.join(self._output_patient_folder, - self._folder_name))) self._folder_name = new_folder_name self._unsaved_changes = True except Exception as e: diff --git a/utils/data_structures/PatientParametersStructure.py b/utils/data_structures/PatientParametersStructure.py index cdf511a..5dac4b0 100644 --- a/utils/data_structures/PatientParametersStructure.py +++ b/utils/data_structures/PatientParametersStructure.py @@ -394,9 +394,8 @@ def import_data(self, filename: str, investigation_ts: str = None, investigation Returns ------- - data_uid, error_message: [str, str] - A tuple [str, str] containing first the new internal unique identifier for the loaded volume, and second - a potential error message. + data_uid: str + A string containing the new internal unique identifier for the loaded volume. """ data_uid = None diff --git a/utils/data_structures/ReportingStructure.py b/utils/data_structures/ReportingStructure.py index c094a10..480444e 100644 --- a/utils/data_structures/ReportingStructure.py +++ b/utils/data_structures/ReportingStructure.py @@ -45,21 +45,24 @@ class ReportingStructure: def __init__(self, uid: str, report_filename: str, output_patient_folder: str, inv_ts_uid: str, inv_ts_folder_name: str = None, reload_params: dict = None) -> None: - self.__reset() - self._unique_id = uid - self._report_filename = report_filename - self._output_patient_folder = output_patient_folder - self._timestamp_uid = inv_ts_uid - if inv_ts_folder_name: - self._timestamp_folder_name = inv_ts_folder_name - - with open(self._report_filename, 'r') as infile: - self._report_content = json.load(infile) - - if reload_params: - self.__reload_from_disk(reload_params) - else: - self.__init_from_scratch() + try: + self.__reset() + self._unique_id = uid + self._report_filename = report_filename + self._output_patient_folder = output_patient_folder + self._timestamp_uid = inv_ts_uid + if inv_ts_folder_name: + self._timestamp_folder_name = inv_ts_folder_name + + with open(self._report_filename, 'r') as infile: + self._report_content = json.load(infile) + + if reload_params: + self.__reload_from_disk(reload_params) + else: + self.__init_from_scratch() + except Exception as e: + raise RuntimeError(e) def __reset(self): """ @@ -189,20 +192,23 @@ def __reload_from_disk(self, params: dict): """ """ - self._timestamp_uid = params['investigation_timestamp_uid'] - if self._timestamp_uid: - self._timestamp_folder_name = params['report_filename'].split('/')[0] - if os.name == 'nt': - self._timestamp_folder_name = list(PurePath(params['report_filename']).parts)[0] + try: + self._timestamp_uid = params['investigation_timestamp_uid'] + if self._timestamp_uid: + self._timestamp_folder_name = params['report_filename'].split('/')[0] + if os.name == 'nt': + self._timestamp_folder_name = list(PurePath(params['report_filename']).parts)[0] - if 'parent_mri_uid' in list(params.keys()): - self._parent_mri_uid = params['parent_mri_uid'] + if 'parent_mri_uid' in list(params.keys()): + self._parent_mri_uid = params['parent_mri_uid'] - if 'task' in list(params.keys()): - self.set_reporting_type(params['task']) + if 'task' in list(params.keys()): + self.set_reporting_type(params['task']) - if 'report_filename_csv' in list(params.keys()): - self._report_filename_csv = os.path.join(self._output_patient_folder, params["report_filename_csv"]) + if 'report_filename_csv' in list(params.keys()): + self._report_filename_csv = os.path.join(self._output_patient_folder, params["report_filename_csv"]) + except Exception as e: + raise RuntimeError("""Reloading the reporting structure from disk failed for {} with {}.""".format(self.report_filename, e)) def save(self) -> dict: """ @@ -227,5 +233,5 @@ def save(self) -> dict: report_params['report_filename_csv'] = os.path.relpath(self._report_filename_csv, base_patient_folder) self._unsaved_changes = False return report_params - except Exception: - logging.error("[Software error] ReportingStructure saving failed with:\n {}".format(traceback.format_exc())) + except Exception as e: + logging.error("ReportingStructure saving failed with: {}".format(e)) diff --git a/utils/data_structures/StudyParametersStructure.py b/utils/data_structures/StudyParametersStructure.py index 825a798..3c6798b 100644 --- a/utils/data_structures/StudyParametersStructure.py +++ b/utils/data_structures/StudyParametersStructure.py @@ -38,17 +38,20 @@ def __init__(self, uid: str = "-1", dest_location: str = None, study_filename: s """ """ - self.__reset() - self._unique_id = uid.replace(" ", '_').strip() + try: + self.__reset() + self._unique_id = uid.replace(" ", '_').strip() - if study_filename: - # Empty init, self.import_study() must be called after the instance creation call. - pass - else: - if not dest_location: - logging.warning("Home folder location for new study creation is None.") - dest_location = os.path.join(os.path.expanduser('~'), '.raidionics') - self.__init_from_scratch(dest_location) + if study_filename: + # Empty init, self.import_study() must be called after the instance creation call. + pass + else: + if not dest_location: + logging.warning("Home folder location for new study creation is None.") + dest_location = os.path.join(os.path.expanduser('~'), '.raidionics') + self.__init_from_scratch(dest_location) + except Exception as e: + raise RuntimeError(e) def __reset(self): """