Skip to content

Commit

Permalink
Improve experiment name validation
Browse files Browse the repository at this point in the history
  • Loading branch information
larsevj committed Sep 5, 2024
1 parent e514038 commit 2da1b88
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 17 deletions.
28 changes: 18 additions & 10 deletions src/ert/gui/ertwidgets/create_experiment_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
QDialogButtonBox,
QGridLayout,
QLabel,
QLineEdit,
QWidget,
)

from ert.gui.ertnotifier import ErtNotifier
from ert.gui.ertwidgets import StringBox, TextModel
from ert.validation.range_string_argument import NotInStorage
from ert.validation.range_string_argument import EnsembleCreate, NotInStorage


class CreateExperimentDialog(QDialog):
Expand All @@ -36,16 +35,17 @@ def __init__(

experiment_label = QLabel("Experiment name:")
self._experiment_edit = StringBox(
TextModel(""), placeholder_text="My experiment", minimum_width=200
TextModel(""), placeholder_text="My_experiment", minimum_width=200
)
self._experiment_edit.setValidator(
NotInStorage(notifier.storage, "experiments")
)

ensemble_label = QLabel("Ensemble name:")
self._ensemble_edit = QLineEdit()
self._ensemble_edit.setMinimumWidth(200)
self._ensemble_edit.setPlaceholderText("My ensemble")
self._ensemble_edit = StringBox(
TextModel(""), placeholder_text="My_ensemble", minimum_width=200
)
self._ensemble_edit.setValidator(EnsembleCreate())

buttons = QDialogButtonBox(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
Expand All @@ -66,10 +66,7 @@ def __init__(
self._ok_button.setEnabled(False)

def enableOkButton() -> None:
self._ok_button.setEnabled(
len(self._experiment_edit.text()) != 0
and len(self._ensemble_edit.text()) != 0
)
self._ok_button.setEnabled(self.isConfigurationValid())

self._experiment_edit.textChanged.connect(enableOkButton)
self._ensemble_edit.textChanged.connect(enableOkButton)
Expand All @@ -82,6 +79,14 @@ def enableOkButton() -> None:

self.setLayout(layout)

self._experiment_edit.getValidationSupport().validationChanged.connect( # noqa
enableOkButton
)

self._ensemble_edit.getValidationSupport().validationChanged.connect( # noqa
enableOkButton
)

self._experiment_edit.setFocus()

@property
Expand All @@ -91,3 +96,6 @@ def experiment_name(self) -> str:
@property
def ensemble_name(self) -> str:
return self._ensemble_edit.text()

def isConfigurationValid(self) -> bool:
return self._experiment_edit.isValid() and self._ensemble_edit.isValid()
3 changes: 2 additions & 1 deletion src/ert/gui/simulation/ensemble_experiment_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from ert.mode_definitions import ENSEMBLE_EXPERIMENT_MODE
from ert.run_models import EnsembleExperiment
from ert.validation import RangeStringArgument
from ert.validation.range_string_argument import NotInStorage
from ert.validation.range_string_argument import EnsembleCreate, NotInStorage

from .experiment_config_panel import ExperimentConfigPanel

Expand Down Expand Up @@ -54,6 +54,7 @@ def __init__(self, ensemble_size: int, run_path: str, notifier: ErtNotifier):
self._ensemble_name_field = StringBox(
TextModel(""), placeholder_text="ensemble"
)
self._ensemble_name_field.setValidator(EnsembleCreate())
self._ensemble_name_field.setMinimumWidth(250)

layout.addRow("Ensemble name:", self._ensemble_name_field)
Expand Down
3 changes: 3 additions & 0 deletions src/ert/gui/simulation/ensemble_smoother_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ def __init__(

self.setLayout(layout)

self._experiment_name_field.getValidationSupport().validationChanged.connect( # noqa
self.simulationConfigurationChanged
)
self._ensemble_format_field.getValidationSupport().validationChanged.connect( # noqa
self.simulationConfigurationChanged
)
Expand Down
5 changes: 4 additions & 1 deletion src/ert/gui/simulation/iterated_ensemble_smoother_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ def __init__(
),
)
self._experiment_name_field.setMinimumWidth(250)
layout.addRow("Experiment name:", self._experiment_name_field)
self._experiment_name_field.setValidator(
NotInStorage(self.notifier.storage, "experiments")
)
layout.addRow("Experiment name:", self._experiment_name_field)

runpath_label = CopyableLabel(text=run_path)
layout.addRow("Runpath:", runpath_label)
Expand Down Expand Up @@ -102,6 +102,9 @@ def __init__(
self._active_realizations_field.setValidator(RangeStringArgument(ensemble_size))
layout.addRow("Active realizations", self._active_realizations_field)

self._experiment_name_field.getValidationSupport().validationChanged.connect( # noqa
self.simulationConfigurationChanged
)
self._iterated_target_ensemble_format_field.getValidationSupport().validationChanged.connect( # noqa
self.simulationConfigurationChanged
)
Expand Down
9 changes: 7 additions & 2 deletions src/ert/gui/simulation/multiple_data_assimilation_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ def __init__(
),
)
self._experiment_name_field.setMinimumWidth(250)
layout.addRow("Experiment name:", self._experiment_name_field)
self._experiment_name_field.setValidator(
NotInStorage(self.notifier.storage, "experiments")
)
layout.addRow("Experiment name:", self._experiment_name_field)

runpath_label = CopyableLabel(text=run_path)
layout.addRow("Runpath:", runpath_label)

Expand Down Expand Up @@ -118,6 +119,9 @@ def __init__(
self._ensemble_selector.currentIndexChanged.connect(self.update_experiment_name)
layout.addRow("Restart from:", self._ensemble_selector)

self._experiment_name_field.getValidationSupport().validationChanged.connect( # noqa
self.simulationConfigurationChanged
)
self._target_ensemble_format_field.getValidationSupport().validationChanged.connect( # noqa
self.simulationConfigurationChanged
)
Expand Down Expand Up @@ -219,7 +223,8 @@ def updateVisualizationOfNormalizedWeights() -> None:

def isConfigurationValid(self) -> bool:
return (
self._target_ensemble_format_field.isValid()
self._experiment_name_field.isValid()
and self._target_ensemble_format_field.isValid()
and self._active_realizations_field.isValid()
and self._relative_iteration_weights_box.isValid()
and self.weights_valid
Expand Down
49 changes: 47 additions & 2 deletions src/ert/validation/range_string_argument.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import re
from typing import TYPE_CHECKING, Optional

from .active_range import ActiveRange
Expand Down Expand Up @@ -49,15 +50,31 @@ def validate(self, token: str) -> ValidationStatus:


class NotInStorage(ArgumentDefinition):
NOT_A_VALID_NAME_FORMAT = (
"The argument must be a valid string containing "
"only characters of these types: "
"Letters: A-Z and a-z, "
"numbers: 0-9, "
"underscore: _, "
"dash: -, "
"period: . and "
"brackets: > < "
)
PATTERN = re.compile(r"^[A-Za-z0-9_\-.<>]*$")

def __init__(self, storage: Storage, prop: str) -> None:
self.storage = storage
self.prop = prop
super().__init__()

def validate(self, token: str) -> ValidationStatus:
validation_status = ValidationStatus()

existing = [exp.name for exp in getattr(self.storage, self.prop)]
if token in existing:
match = self.PATTERN.match(token)
if match is None:
validation_status.setFailed()
validation_status.addToMessage(self.NOT_A_VALID_NAME_FORMAT)
elif token in existing:
validation_status.setFailed()
validation_status.addToMessage(
f"{self.prop.capitalize()} name must be unique, not one of: {existing}"
Expand All @@ -66,3 +83,31 @@ def validate(self, token: str) -> ValidationStatus:
validation_status.setValue(token)

return validation_status


class EnsembleCreate(ArgumentDefinition):
NOT_A_VALID_NAME_FORMAT = (
"The argument must be a valid string containing "
"only characters of these types: "
"Letters: A-Z and a-z, "
"numbers: 0-9, "
"underscore: _, "
"dash: -, "
"period: . and "
"brackets: > < "
)
PATTERN = re.compile(r"^[A-Za-z0-9_\-.<>]*$")

def __init__(self) -> None:
super().__init__()

def validate(self, token: str) -> ValidationStatus:
validation_status = ValidationStatus()
match = self.PATTERN.match(token)
if match is None:
validation_status.setFailed()
validation_status.addToMessage(self.NOT_A_VALID_NAME_FORMAT)

validation_status.setValue(token)

return validation_status
2 changes: 1 addition & 1 deletion tests/unit_tests/gui/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ def handle_popup_dialog():


def add_experiment_manually(
qtbot, gui, experiment_name="My experiment", ensemble_name="default"
qtbot, gui, experiment_name="My_experiment", ensemble_name="default"
):
manage_tool = gui.tools["Manage experiments"]
manage_tool.trigger()
Expand Down

0 comments on commit 2da1b88

Please sign in to comment.