diff --git a/src/ert/gui/ertwidgets/create_experiment_dialog.py b/src/ert/gui/ertwidgets/create_experiment_dialog.py new file mode 100644 index 00000000000..9844a53009e --- /dev/null +++ b/src/ert/gui/ertwidgets/create_experiment_dialog.py @@ -0,0 +1,79 @@ +from qtpy.QtCore import ( + Qt, + Signal, +) +from qtpy.QtWidgets import ( + QDialog, + QDialogButtonBox, + QGridLayout, + QLabel, + QLineEdit, +) + + +class CreateExperimentDialog(QDialog): + onDone = Signal(str, str) + + def __init__( + self, + title: str = "Create new experiment", + parent=None, + ): + QDialog.__init__(self, parent=parent) + self.setModal(True) + self.setWindowTitle(title) + self.setFixedSize(400, 100) + + layout = QGridLayout() + + experiment_label = QLabel("Experiment name:") + self._experiment_edit = QLineEdit() + self._experiment_edit.setPlaceholderText("My experiment") + + ensemble_label = QLabel("Ensemble name:") + self._ensemble_edit = QLineEdit() + self._ensemble_edit.setPlaceholderText("My ensemble") + + buttons = QDialogButtonBox( + QDialogButtonBox.Ok | QDialogButtonBox.Cancel, + Qt.Orientation.Horizontal, + self, + ) + buttons.accepted.connect(self.accept) + buttons.rejected.connect(self.reject) + self._ok_button = buttons.button(QDialogButtonBox.Ok) + assert self._ok_button + + self._ok_button.clicked.connect( + lambda: self.onDone.emit( + self._experiment_edit.text(), self._ensemble_edit.text() + ) + ) + self._ok_button.setEnabled(False) + + def enableOkButton(): + self._ok_button.setEnabled( + len(self._experiment_edit.text()) != 0 + and len(self._ensemble_edit.text()) != 0 + ) + + self._experiment_edit.textChanged.connect(enableOkButton) + self._ensemble_edit.textChanged.connect(enableOkButton) + + layout.addWidget(experiment_label, 0, 0) + layout.addWidget(self._experiment_edit, 0, 1) + layout.addWidget(ensemble_label, 1, 0) + layout.addWidget(self._ensemble_edit, 1, 1) + layout.addWidget(buttons, 2, 1) + + self.setLayout(layout) + + self._experiment_edit.setFocus() + + @property + def experiment_name(self) -> str: + return self._experiment_edit.text() + + @property + def ensemble_name(self) -> str: + return self._ensemble_edit.text() diff --git a/src/ert/gui/ertwidgets/models/storage_model.py b/src/ert/gui/ertwidgets/models/storage_model.py new file mode 100644 index 00000000000..79a4ba043c8 --- /dev/null +++ b/src/ert/gui/ertwidgets/models/storage_model.py @@ -0,0 +1,130 @@ +from typing import Any, List + +from qtpy.QtCore import ( + QAbstractItemModel, + QModelIndex, + Qt, + Slot, +) + +from ert.storage import EnsembleReader, ExperimentReader, StorageReader + + +class Ensemble: + def __init__(self, ensemble: EnsembleReader, parent: Any): + self._parent = parent + self._name = ensemble.name + self._id = ensemble.id + self._start_time = ensemble.started_at + + def row(self) -> int: + if self._parent: + return self._parent._children.index(self) + return 0 + + def data(self, index: QModelIndex, role=Qt.ItemDataRole.DisplayRole) -> Any: + if not index.isValid(): + return None + + if role == Qt.ItemDataRole.DisplayRole: + return f"{self._name} - {self._start_time} ({self._id})" + + return None + + +class Experiment: + def __init__(self, experiment: ExperimentReader, parent: Any): + self._parent = parent + self._id = experiment.id + self._name = experiment.name + self._experiment_type = ( + experiment.simulation_arguments["analysis_module"] + if "analysis_module" in experiment.simulation_arguments + else "" + ) + self._children: List[Ensemble] = [] + + def add_ensemble(self, ensemble: Ensemble) -> None: + self._children.append(ensemble) + + def row(self) -> int: + if self._parent: + return self._parent._children.index(self) + return 0 + + def data(self, index: QModelIndex, role=Qt.ItemDataRole.DisplayRole) -> Any: + if not index.isValid(): + return None + + if role == Qt.ItemDataRole.DisplayRole: + return f"{self._name} - {self._experiment_type} ({self._id})" + + return None + + +class StorageModel(QAbstractItemModel): + def __init__(self, storage: StorageReader): + super().__init__(None) + self._children: List[Experiment] = [] + self._load_storage(storage) + + @Slot(StorageReader) + def reloadStorage(self, storage: StorageReader) -> None: + self.beginResetModel() + self._load_storage(storage) + self.endResetModel() + + @Slot() + def add_experiment(self, experiment: Experiment) -> None: + idx = QModelIndex() + self.beginInsertRows(idx, 0, 0) + self._children.append(experiment) + self.endInsertRows() + + def _load_storage(self, storage: StorageReader) -> None: + self._children = [] + for experiment in storage.experiments: + ex = Experiment(experiment, self) + for ensemble in experiment.ensembles: + ens = Ensemble(ensemble, ex) + ex.add_ensemble(ens) + self._children.append(ex) + + def columnCount(self, parent: QModelIndex) -> int: + return 1 + + def rowCount(self, parent: QModelIndex) -> int: + if parent.isValid(): + if isinstance(parent.internalPointer(), Ensemble): + return 0 + return len(parent.internalPointer()._children) + else: + return len(self._children) + + def parent(self, index: QModelIndex) -> QModelIndex: + if not index.isValid(): + return QModelIndex() + + child_item = index.internalPointer() + parentItem = child_item._parent + + if parentItem == self: + return QModelIndex() + + return self.createIndex(parentItem.row(), 0, parentItem) + + def data(self, index: QModelIndex, role=Qt.ItemDataRole.DisplayRole) -> Any: + if not index.isValid(): + return None + + return index.internalPointer().data(index, role) + + def index(self, row: int, column: int, parent: QModelIndex) -> QModelIndex: + parentItem = parent.internalPointer() if parent.isValid() else self + try: + childItem = parentItem._children[row] + except KeyError: + childItem = None + if childItem: + return self.createIndex(row, column, childItem) + return QModelIndex() diff --git a/src/ert/gui/ertwidgets/storage_widget.py b/src/ert/gui/ertwidgets/storage_widget.py new file mode 100644 index 00000000000..caeef9639c4 --- /dev/null +++ b/src/ert/gui/ertwidgets/storage_widget.py @@ -0,0 +1,70 @@ +from qtpy.QtCore import QSortFilterProxyModel, Qt +from qtpy.QtWidgets import ( + QHeaderView, + QLineEdit, + QTreeView, + QVBoxLayout, + QWidget, +) + +from ert.config import ErtConfig +from ert.gui.ertnotifier import ErtNotifier +from ert.gui.ertwidgets.caselist import AddWidget +from ert.gui.ertwidgets.create_experiment_dialog import CreateExperimentDialog +from ert.gui.ertwidgets.models.storage_model import StorageModel + + +class StorageWidget(QWidget): + def __init__( + self, notifier: ErtNotifier, ert_config: ErtConfig, ensemble_size: int + ): + QWidget.__init__(self) + + self._notifier = notifier + self._ert_config = ert_config + self._ensemble_size = ensemble_size + + tree_view = QTreeView(self) + storage_model = StorageModel(self._notifier.storage) + notifier.storage_changed.connect(storage_model.reloadStorage) + notifier.ertChanged.connect( + lambda: storage_model.reloadStorage(self._notifier.storage) + ) + + if isinstance(tree_view.header(), QHeaderView): + tree_view.header().hide() + + search_bar = QLineEdit(self) + search_bar.setPlaceholderText("Filter") + proxy_model = QSortFilterProxyModel() + proxy_model.setFilterKeyColumn(-1) # Search all columns. + proxy_model.setSourceModel(storage_model) + proxy_model.sort(0, Qt.SortOrder.AscendingOrder) + + tree_view.setModel(proxy_model) + search_bar.textChanged.connect(proxy_model.setFilterFixedString) + + self._create_experiment_button = AddWidget(self.add_item) + + layout = QVBoxLayout() + layout.addWidget(search_bar) + layout.addWidget(tree_view) + layout.addWidget(self._create_experiment_button) + + self.setLayout(layout) + + def add_item(self) -> None: + create_experiment_dialog = CreateExperimentDialog(parent=self) + create_experiment_dialog.show() + if create_experiment_dialog.exec_(): + ensemble = self._notifier.storage.create_experiment( + parameters=self._ert_config.ensemble_config.parameter_configuration, + responses=self._ert_config.ensemble_config.response_configuration, + observations=self._ert_config.observations, + name=create_experiment_dialog.experiment_name, + ).create_ensemble( + name=create_experiment_dialog.ensemble_name, + ensemble_size=self._ensemble_size, + ) + self._notifier.set_current_case(ensemble) + self._notifier.ertChanged.emit() diff --git a/src/ert/gui/tools/manage_cases/case_init_configuration.py b/src/ert/gui/tools/manage_cases/case_init_configuration.py index eb40e2fbe7d..28fdc1e0b69 100644 --- a/src/ert/gui/tools/manage_cases/case_init_configuration.py +++ b/src/ert/gui/tools/manage_cases/case_init_configuration.py @@ -16,10 +16,10 @@ from ert.config import ErtConfig from ert.enkf_main import sample_prior from ert.gui.ertwidgets import showWaitCursorWhileWaiting -from ert.gui.ertwidgets.caselist import CaseList from ert.gui.ertwidgets.caseselector import CaseSelector from ert.gui.ertwidgets.checklist import CheckList from ert.gui.ertwidgets.models.selectable_list_model import SelectableListModel +from ert.gui.ertwidgets.storage_widget import StorageWidget if TYPE_CHECKING: from typing import List @@ -77,13 +77,13 @@ def addCreateNewCaseTab(self): panel = QWidget() panel.setObjectName("create_new_case_tab") layout = QVBoxLayout() - case_list = CaseList(self.ert_config, self.notifier, self.ensemble_size) - - layout.addWidget(case_list, stretch=1) - + storage_widget = StorageWidget( + self.notifier, self.ert_config, self.ensemble_size + ) + layout.addWidget(storage_widget, stretch=1) panel.setLayout(layout) - self.addTab(panel, "Create new case") + self.addTab(panel, "Create new experiment") def addInitializeFromScratchTab(self): panel = QWidget() @@ -139,7 +139,7 @@ def addShowCaseInfo(self): self.notifier, update_ert=False, ) - row1 = createRow(QLabel("Select case:"), case_selector) + row1 = createRow(QLabel("Select ensemble:"), case_selector) layout.addLayout(row1) @@ -147,7 +147,7 @@ def addShowCaseInfo(self): self._case_info_area.setReadOnly(True) self._case_info_area.setMinimumHeight(300) - row2 = createRow(QLabel("Case info:"), self._case_info_area) + row2 = createRow(QLabel("Ensemble info:"), self._case_info_area) layout.addLayout(row2) @@ -157,7 +157,7 @@ def addShowCaseInfo(self): case_selector.currentIndexChanged[int].connect(self._showInfoForCase) self.notifier.ertChanged.connect(self._showInfoForCase) - self.addTab(case_widget, "Case info") + self.addTab(case_widget, "Ensemble info") def _showInfoForCase(self, index=None): if index is None: @@ -179,5 +179,5 @@ def _showInfoForCase(self, index=None): @showWaitCursorWhileWaiting def on_tab_changed(self, p_int): - if self.tabText(p_int) == "Case info": + if self.tabText(p_int) == "Ensemble info": self._showInfoForCase() diff --git a/src/ert/run_models/ensemble_experiment.py b/src/ert/run_models/ensemble_experiment.py index 8a97c728072..ea5528a2d27 100644 --- a/src/ert/run_models/ensemble_experiment.py +++ b/src/ert/run_models/ensemble_experiment.py @@ -55,6 +55,7 @@ def run_experiment( name=self.simulation_arguments.experiment_name, parameters=self.ert_config.ensemble_config.parameter_configuration, observations=self.ert_config.observations, + simulation_arguments=self._simulation_arguments, responses=self.ert_config.ensemble_config.response_configuration, ) ensemble = self._storage.create_ensemble( diff --git a/src/ert/run_models/ensemble_smoother.py b/src/ert/run_models/ensemble_smoother.py index a4381a53475..4dc75f631e6 100644 --- a/src/ert/run_models/ensemble_smoother.py +++ b/src/ert/run_models/ensemble_smoother.py @@ -68,8 +68,10 @@ def run_experiment( parameters=self.ert_config.ensemble_config.parameter_configuration, observations=self.ert_config.observations, responses=self.ert_config.ensemble_config.response_configuration, + simulation_arguments=self._simulation_arguments, name=self._simulation_arguments.experiment_name, ) + self.set_env_key("_ERT_EXPERIMENT_ID", str(experiment.id)) prior_name = self._simulation_arguments.current_case prior = self._storage.create_ensemble( diff --git a/src/ert/run_models/iterated_ensemble_smoother.py b/src/ert/run_models/iterated_ensemble_smoother.py index 0e05aa7e5ae..860d687c493 100644 --- a/src/ert/run_models/iterated_ensemble_smoother.py +++ b/src/ert/run_models/iterated_ensemble_smoother.py @@ -133,6 +133,7 @@ def run_experiment( parameters=self.ert_config.ensemble_config.parameter_configuration, observations=self.ert_config.observations, responses=self.ert_config.ensemble_config.response_configuration, + simulation_arguments=self._simulation_arguments, name=self._simulation_arguments.experiment_name, ) prior = self._storage.create_ensemble( diff --git a/src/ert/run_models/multiple_data_assimilation.py b/src/ert/run_models/multiple_data_assimilation.py index 2c788209898..e72eb37d58e 100644 --- a/src/ert/run_models/multiple_data_assimilation.py +++ b/src/ert/run_models/multiple_data_assimilation.py @@ -108,6 +108,7 @@ def run_experiment( parameters=self.ert_config.ensemble_config.parameter_configuration, observations=self.ert_config.observations, responses=self.ert_config.ensemble_config.response_configuration, + simulation_arguments=self._simulation_arguments, name=self._simulation_arguments.experiment_name, ) diff --git a/src/ert/run_models/run_arguments.py b/src/ert/run_models/run_arguments.py index f0730013785..cbe3d69f9de 100644 --- a/src/ert/run_models/run_arguments.py +++ b/src/ert/run_models/run_arguments.py @@ -15,6 +15,7 @@ class SimulationArguments: class SingleTestRunArguments(SimulationArguments): current_case: str target_case: Optional[str] = None + analysis_module: str = "Single test" def __post_init__(self) -> None: self.num_iterations = 1 @@ -32,6 +33,7 @@ class EnsembleExperimentRunArguments(SimulationArguments): start_iteration: int = 0 current_case: str = "prior" target_case: Optional[str] = None + analysis_module: str = "Ensemble experiment" def __post_init__(self) -> None: self.num_iterations = 1 @@ -42,6 +44,7 @@ def __post_init__(self) -> None: class EvaluateEnsembleRunArguments(SimulationArguments): active_realizations: List[bool] current_case: str + analysis_module: str = "Evaluate ensemble" def __post_init__(self) -> None: self.target_case = None @@ -56,12 +59,11 @@ class ESRunArguments(SimulationArguments): active_realizations: List[bool] current_case: str target_case: str + analysis_module: str = "ES" - def __post_init__(self) -> None: - self.analysis_module = "STD_ENKF" - self.num_iterations = 1 - self.start_iteration = 0 - self.prev_successful_realizations = 0 + num_iterations: int = 1 + start_iteration: int = 0 + prev_successful_realizations: int = 0 # pylint: disable=R0902 @@ -73,11 +75,13 @@ class ESMDARunArguments(SimulationArguments): restart_run: bool prior_ensemble: str + analysis_module: str = "ES_MDA" + start_iteration: int = 0 + prev_successful_realizations: int = 0 + current_case = None + def __post_init__(self) -> None: - self.start_iteration = 0 - self.prev_successful_realizations = 0 - self.current_case = None - self.num_iterations = len(self.weights) + self.num_iterations: int = len(self.weights) @dataclass @@ -88,11 +92,10 @@ class SIESRunArguments(SimulationArguments): num_iterations: int num_retries_per_iter: int - def __post_init__(self) -> None: - self.analysis_module = "IES_ENKF" - self.start_iteration = 0 - self.iter_num = 0 - self.prev_successful_realizations = 0 + analysis_module: str = "IES" + start_iteration = 0 + iter_num = 0 + prev_successful_realizations = 0 RunArgumentsType = Union[ diff --git a/src/ert/storage/local_experiment.py b/src/ert/storage/local_experiment.py index e48fc43abbc..1591cb0fd60 100644 --- a/src/ert/storage/local_experiment.py +++ b/src/ert/storage/local_experiment.py @@ -86,11 +86,12 @@ def create( if name is None: name = datetime.today().strftime("%Y-%m-%d") + (path / "index.json").write_text(_Index(id=uuid, name=name).model_dump_json()) + parameter_data = {} for parameter in parameters or []: parameter.save_experiment_data(path) parameter_data.update({parameter.name: parameter.to_dict()}) - with open(path / cls._parameter_file, "w", encoding="utf-8") as f: json.dump(parameter_data, f) @@ -106,15 +107,11 @@ def create( for obs_name, dataset in observations.items(): dataset.to_netcdf(output_path / f"{obs_name}", engine="scipy") - if simulation_arguments: - with open( - path / cls._simulation_arguments_file, "w", encoding="utf-8" - ) as f: - json.dump( - dataclasses.asdict(simulation_arguments), f, cls=ContextBoolEncoder - ) - - (path / "index.json").write_text(_Index(id=uuid, name=name).model_dump_json()) + with open(path / cls._simulation_arguments_file, "w", encoding="utf-8") as f: + simulation_data = ( + dataclasses.asdict(simulation_arguments) if simulation_arguments else {} + ) + json.dump(simulation_data, f, cls=ContextBoolEncoder) return cls(storage, path, Mode.WRITE) diff --git a/src/ert/storage/local_storage.py b/src/ert/storage/local_storage.py index 99fc8d716db..f2e2e1a7411 100644 --- a/src/ert/storage/local_storage.py +++ b/src/ert/storage/local_storage.py @@ -217,6 +217,7 @@ def create_experiment( simulation_arguments=simulation_arguments, name=name, ) + self._experiments[exp.id] = exp return exp diff --git a/tests/unit_tests/gui/conftest.py b/tests/unit_tests/gui/conftest.py index dbd349a4e3c..de797dd29a7 100644 --- a/tests/unit_tests/gui/conftest.py +++ b/tests/unit_tests/gui/conftest.py @@ -33,7 +33,8 @@ from ert.gui.ertwidgets import ClosableDialog from ert.gui.ertwidgets.caselist import AddWidget from ert.gui.ertwidgets.caseselector import CaseSelector -from ert.gui.ertwidgets.validateddialog import ValidatedDialog +from ert.gui.ertwidgets.create_experiment_dialog import CreateExperimentDialog +from ert.gui.ertwidgets.storage_widget import StorageWidget from ert.gui.main import ErtMainWindow, GUILogHandler, _setup_main_window from ert.gui.simulation.ensemble_experiment_panel import EnsembleExperimentPanel from ert.gui.simulation.run_dialog import RunDialog @@ -408,22 +409,27 @@ def handle_popup_dialog(): load_results_tool.trigger() -def add_case_manually(qtbot, gui, case_name="default"): - def handle_dialog(dialog, cases_panel): - # Open the create new cases tab - cases_panel.setCurrentIndex(0) - current_tab = cases_panel.currentWidget() +def add_experiment_manually( + qtbot, gui, experiment_name="My experiment", ensemble_name="default" +): + def handle_dialog(dialog, experiments_panel): + # Open the create new experiment tab + experiments_panel.setCurrentIndex(0) + current_tab = experiments_panel.currentWidget() assert current_tab.objectName() == "create_new_case_tab" - create_widget = get_child(current_tab, AddWidget) + storage_widget = get_child(current_tab, StorageWidget) + add_widget = get_child(storage_widget, AddWidget) - # Click add case and name it "iter-0" def handle_add_dialog(): - dialog = wait_for_child(gui, qtbot, ValidatedDialog) - dialog.param_name.setText(case_name) - qtbot.mouseClick(dialog.ok_button, Qt.LeftButton) + # dialog = wait_for_child(gui, qtbot, CreateExperimentDialog, options=Qt.FindChildOption.FindChildrenRecursively) + dialog = wait_for_child(gui, qtbot, CreateExperimentDialog) + dialog._experiment_edit.setText(experiment_name) + dialog._ensemble_edit.setText(ensemble_name) + + qtbot.mouseClick(dialog._ok_button, Qt.MouseButton.LeftButton) QTimer.singleShot(1000, handle_add_dialog) - qtbot.mouseClick(create_widget.addButton, Qt.LeftButton) + qtbot.mouseClick(add_widget.addButton, Qt.MouseButton.LeftButton) dialog.close() diff --git a/tests/unit_tests/gui/test_full_manual_update_workflow.py b/tests/unit_tests/gui/test_full_manual_update_workflow.py index b36fb392e6e..e16b5bd2a3c 100644 --- a/tests/unit_tests/gui/test_full_manual_update_workflow.py +++ b/tests/unit_tests/gui/test_full_manual_update_workflow.py @@ -1,11 +1,19 @@ +import contextlib import shutil import numpy as np from qtpy.QtCore import Qt, QTimer -from qtpy.QtWidgets import QApplication, QComboBox, QMessageBox, QPushButton, QWidget +from qtpy.QtWidgets import ( + QApplication, + QComboBox, + QMessageBox, + QPushButton, + QTreeView, + QWidget, +) from ert.data import MeasuredData -from ert.gui.ertwidgets.caselist import CaseList +from ert.gui.ertwidgets.storage_widget import StorageWidget from ert.gui.simulation.evaluate_ensemble_panel import EvaluateEnsemblePanel from ert.gui.simulation.run_dialog import RunDialog from ert.gui.simulation.simulation_panel import SimulationPanel @@ -67,8 +75,15 @@ def handle_manage_dialog(dialog, cases_panel): cases_panel.setCurrentIndex(0) current_tab = cases_panel.currentWidget() assert current_tab.objectName() == "create_new_case_tab" - case_list = get_child(current_tab, CaseList) - assert len(case_list._list.findItems("iter-1", Qt.MatchFlag.MatchContains)) == 1 + storage_widget = get_child(current_tab, StorageWidget) + tree_view = get_child(storage_widget, QTreeView) + tree_view.expandAll() + + assert tree_view.model().rowCount() == 2 + assert "iter-1" in tree_view.model().index( + 1, 0, tree_view.model().index(1, 0) + ).data(0) + dialog.close() with_manage_tool(gui, qtbot, handle_manage_dialog) @@ -78,7 +93,9 @@ def handle_manage_dialog(dialog, cases_panel): simulation_mode_combo = get_child(simulation_panel, QComboBox) simulation_settings = get_child(simulation_panel, EvaluateEnsemblePanel) simulation_mode_combo.setCurrentText(EvaluateEnsemble.name()) - shutil.rmtree("poly_out") + + with contextlib.suppress(FileNotFoundError): + shutil.rmtree("poly_out") current_select = 0 simulation_settings._case_selector.setCurrentIndex(current_select) diff --git a/tests/unit_tests/gui/test_main_window.py b/tests/unit_tests/gui/test_main_window.py index fecb7440abc..fcc617aa6b8 100644 --- a/tests/unit_tests/gui/test_main_window.py +++ b/tests/unit_tests/gui/test_main_window.py @@ -15,6 +15,7 @@ QMessageBox, QPushButton, QToolButton, + QTreeView, QWidget, ) @@ -22,12 +23,13 @@ from ert.config import ErtConfig from ert.enkf_main import EnKFMain from ert.gui.ertwidgets.analysismodulevariablespanel import AnalysisModuleVariablesPanel -from ert.gui.ertwidgets.caselist import AddWidget, CaseList +from ert.gui.ertwidgets.caselist import AddWidget from ert.gui.ertwidgets.caseselector import CaseSelector +from ert.gui.ertwidgets.create_experiment_dialog import CreateExperimentDialog from ert.gui.ertwidgets.customdialog import CustomDialog from ert.gui.ertwidgets.listeditbox import ListEditBox from ert.gui.ertwidgets.pathchooser import PathChooser -from ert.gui.ertwidgets.validateddialog import ValidatedDialog +from ert.gui.ertwidgets.storage_widget import StorageWidget from ert.gui.main import ErtMainWindow, GUILogHandler, _setup_main_window from ert.gui.simulation.run_dialog import RunDialog from ert.gui.simulation.simulation_panel import SimulationPanel @@ -45,7 +47,7 @@ from ert.shared.plugins.plugin_manager import ErtPluginManager from .conftest import ( - add_case_manually, + add_experiment_manually, get_child, get_children, load_results_manually, @@ -460,53 +462,43 @@ def test_that_the_plot_window_contains_the_expected_elements( @pytest.mark.usefixtures("use_tmpdir") -def test_that_the_manage_cases_tool_can_be_used( +def test_that_the_manage_experiments_tool_can_be_used( esmda_has_run, opened_main_window, qtbot, ): gui = opened_main_window - # Click on "Manage Cases" - def handle_dialog(dialog, cases_panel): - # Open the create new cases tab - cases_panel.setCurrentIndex(0) - current_tab = cases_panel.currentWidget() + # Click on "Manage Experiments" in the main window + def handle_dialog(dialog, experiments_panel): + # Open the tab + experiments_panel.setCurrentIndex(0) + current_tab = experiments_panel.currentWidget() assert current_tab.objectName() == "create_new_case_tab" - create_widget = get_child(current_tab, AddWidget) - case_list = get_child(current_tab, CaseList) - # The case list should contain the expected cases - assert case_list._list.count() == 5 + storage_widget = get_child(current_tab, StorageWidget) + tree_view = get_child(storage_widget, QTreeView) + tree_view.expandAll() + + # The storage view should contain the expected experiments and ensembles + # Two experiments. The first experiment with one ensemble the second with four + assert tree_view.model().rowCount() == 2 + assert tree_view.model().rowCount(tree_view.model().index(0, 0)) == 1 + assert tree_view.model().rowCount(tree_view.model().index(1, 0)) == 4 - # Click add case and name it "new_case" def handle_add_dialog(): - dialog = wait_for_child(current_tab, qtbot, ValidatedDialog) - dialog.param_name.setText("new_case") - qtbot.mouseClick(dialog.ok_button, Qt.LeftButton) + dialog = wait_for_child(current_tab, qtbot, CreateExperimentDialog) + dialog._experiment_edit.setText("my-experiment") + dialog._ensemble_edit.setText("_new_ensemble_") + qtbot.mouseClick(dialog._ok_button, Qt.MouseButton.LeftButton) QTimer.singleShot(1000, handle_add_dialog) + create_widget = get_child(storage_widget, AddWidget) qtbot.mouseClick(create_widget.addButton, Qt.LeftButton) - # The list should now contain "new_case" - assert case_list._list.count() == 6 - - # Click add case and try to name it "new_case" again - def handle_add_dialog_again(): - dialog = wait_for_child(current_tab, qtbot, ValidatedDialog) - dialog.param_name.setText("new_case") - assert not dialog.ok_button.isEnabled() - qtbot.mouseClick(dialog.cancel_button, Qt.LeftButton) - - QTimer.singleShot(1000, handle_add_dialog_again) - qtbot.mouseClick(create_widget.addButton, Qt.LeftButton) - - # The list contains the same amount of cases as before - assert case_list._list.count() == 6 - # Go to the "initialize from scratch" panel - cases_panel.setCurrentIndex(1) - current_tab = cases_panel.currentWidget() + experiments_panel.setCurrentIndex(1) + current_tab = experiments_panel.currentWidget() assert current_tab.objectName() == "initialize_from_scratch_panel" combo_box = get_child(current_tab, CaseSelector) @@ -602,42 +594,47 @@ def handle_finished_box(): qtbot.waitUntil(lambda: os.path.exists(file_name)) -def test_that_the_manage_cases_tool_can_be_used_with_clean_storage( +def test_that_the_manage_experiments_tool_can_be_used_with_clean_storage( opened_main_window_clean, qtbot ): gui = opened_main_window_clean # Click on "Manage Cases" - def handle_dialog(dialog, cases_panel): + def handle_dialog(dialog, experiments_panel): # Open the create new cases tab - cases_panel.setCurrentIndex(0) - current_tab = cases_panel.currentWidget() + experiments_panel.setCurrentIndex(0) + current_tab = experiments_panel.currentWidget() assert current_tab.objectName() == "create_new_case_tab" - create_widget = get_child(current_tab, AddWidget) - case_list = get_child(current_tab, CaseList) - assert case_list._list.count() == 0 + storage_widget = get_child(current_tab, StorageWidget) + tree_view = get_child(storage_widget, QTreeView) + tree_view.expandAll() + + assert tree_view.model().rowCount() == 0 - # Click add case and name it "new_case" def handle_add_dialog(): - dialog = wait_for_child(current_tab, qtbot, ValidatedDialog) - dialog.param_name.setText("new_case") - qtbot.mouseClick(dialog.ok_button, Qt.LeftButton) + dialog = wait_for_child(current_tab, qtbot, CreateExperimentDialog) + dialog._experiment_edit.setText("my-experiment") + dialog._ensemble_edit.setText("_new_ensemble_") + qtbot.mouseClick(dialog._ok_button, Qt.MouseButton.LeftButton) QTimer.singleShot(1000, handle_add_dialog) - qtbot.mouseClick(create_widget.addButton, Qt.LeftButton) + create_widget = get_child(storage_widget, AddWidget) + qtbot.mouseClick(create_widget.addButton, Qt.MouseButton.LeftButton) - # The list should now contain "new_case" - assert case_list._list.count() == 1 - assert case_list._list.item(0).data(Qt.UserRole).name == "new_case" + assert tree_view.model().rowCount() == 1 + assert tree_view.model().rowCount(tree_view.model().index(0, 0)) == 1 + assert "_new_ensemble_" in tree_view.model().index( + 0, 0, tree_view.model().index(0, 0) + ).data(0) # Go to the "initialize from scratch" panel - cases_panel.setCurrentIndex(1) - current_tab = cases_panel.currentWidget() + experiments_panel.setCurrentIndex(1) + current_tab = experiments_panel.currentWidget() assert current_tab.objectName() == "initialize_from_scratch_panel" combo_box = get_child(current_tab, CaseSelector) - assert combo_box.currentText().startswith("new_case") + assert combo_box.currentText().startswith("_new_ensemble_") # click on "initialize" initialize_button = get_child( @@ -775,7 +772,7 @@ def test_that_es_mda_restart_run_box_is_disabled_when_there_are_no_cases(qtbot): assert len(case_selector._case_list()) == 0 assert not restart_button.isEnabled() - add_case_manually(qtbot, gui, case_name="test_case") + add_experiment_manually(qtbot, gui, ensemble_name="test_ensemble") assert len(case_selector._case_list()) == 1 assert restart_button.isEnabled() diff --git a/tests/unit_tests/gui/test_rft_export_plugin.py b/tests/unit_tests/gui/test_rft_export_plugin.py index 590403d9a5f..322fc896410 100644 --- a/tests/unit_tests/gui/test_rft_export_plugin.py +++ b/tests/unit_tests/gui/test_rft_export_plugin.py @@ -17,7 +17,7 @@ from ert.storage import open_storage from .conftest import ( - add_case_manually, + add_experiment_manually, get_child, load_results_manually, wait_for_child, @@ -89,7 +89,7 @@ def test_rft_csv_export_plugin_exports_rft_data( gui = _setup_main_window(enkf_main, args, GUILogHandler(), storage) qtbot.addWidget(gui) - add_case_manually(qtbot, gui) + add_experiment_manually(qtbot, gui) load_results_manually(qtbot, gui) def handle_finished_box(): diff --git a/tests/unit_tests/gui/tools/test_case_tool.py b/tests/unit_tests/gui/tools/test_case_tool.py index e4b4aeaff21..e36cc7fac92 100644 --- a/tests/unit_tests/gui/tools/test_case_tool.py +++ b/tests/unit_tests/gui/tools/test_case_tool.py @@ -16,7 +16,7 @@ def test_case_tool_init_prior(qtbot, storage): notifier = ErtNotifier(config.config_path) notifier.set_storage(storage) ensemble = storage.create_experiment( - parameters=config.ensemble_config.parameter_configuration + parameters=config.ensemble_config.parameter_configuration, name="my-experiment" ).create_ensemble( ensemble_size=config.model_config.num_realizations, name="prior", @@ -46,11 +46,12 @@ def test_case_tool_init_updates_the_case_info_tab(qtbot, storage): notifier = ErtNotifier(config.config_path) notifier.set_storage(storage) ensemble = storage.create_experiment( - parameters=config.ensemble_config.parameter_configuration + parameters=config.ensemble_config.parameter_configuration, name="my-experiment" ).create_ensemble( ensemble_size=config.model_config.num_realizations, name="default" ) notifier.set_current_case(ensemble) + tool = CaseInitializationConfigurationPanel( config, notifier, config.model_config.num_realizations )