Skip to content

Commit

Permalink
Integration tests for data loading + CI attempt[skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
dbouget committed Oct 2, 2024
1 parent f0c2c97 commit e949438
Show file tree
Hide file tree
Showing 12 changed files with 430 additions and 101 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/build_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("""
Expand Down
1 change: 1 addition & 0 deletions gui/UtilsWidgets/CustomQDialog/ImportFoldersQDialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand Down
Empty file added integration_tests/__init__.py
Empty file.
231 changes: 231 additions & 0 deletions integration_tests/empty_patient_test.py

Large diffs are not rendered by default.

132 changes: 132 additions & 0 deletions integration_tests/patient_loading_test.py
Original file line number Diff line number Diff line change
@@ -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)
50 changes: 0 additions & 50 deletions tests/patient_creation_tests.py

This file was deleted.

9 changes: 4 additions & 5 deletions utils/data_structures/InvestigationTimestampStructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.<br>' + \
'Requested name: {}.<br>'.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:
Expand Down
5 changes: 2 additions & 3 deletions utils/data_structures/PatientParametersStructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
62 changes: 34 additions & 28 deletions utils/data_structures/ReportingStructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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:
"""
Expand All @@ -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))
23 changes: 13 additions & 10 deletions utils/data_structures/StudyParametersStructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down

0 comments on commit e949438

Please sign in to comment.