diff --git a/src/ert/config/model_config.py b/src/ert/config/model_config.py index d75b0826f11..ccbc3e1e483 100644 --- a/src/ert/config/model_config.py +++ b/src/ert/config/model_config.py @@ -5,13 +5,12 @@ import os.path import shutil from datetime import datetime -from pathlib import Path from typing import no_type_check from pydantic import field_validator from pydantic.dataclasses import dataclass -from ert.shared.status.utils import byte_with_unit +from ert.shared.status.utils import byte_with_unit, get_mount_directory from .parsing import ( ConfigDict, @@ -94,7 +93,7 @@ def validate_runpath(cls, runpath_format_string: str) -> str: ConfigWarning.warn(msg) logger.warning(msg) with contextlib.suppress(Exception): - mount_dir = _get_mount_directory(runpath_format_string) + mount_dir = get_mount_directory(runpath_format_string) total_space, used_space, free_space = shutil.disk_usage(mount_dir) percentage_used = used_space / total_space if ( @@ -165,12 +164,3 @@ def _replace_runpath_format(format_string: str) -> str: format_string = format_string.replace("%d", "", 1) format_string = format_string.replace("%d", "", 1) return format_string - - -def _get_mount_directory(runpath: str) -> Path: - path = Path(runpath).absolute() - - while not path.is_mount(): - path = path.parent - - return path diff --git a/src/ert/gui/simulation/run_dialog.py b/src/ert/gui/simulation/run_dialog.py index 6c0269b5ba2..2330893bdfd 100644 --- a/src/ert/gui/simulation/run_dialog.py +++ b/src/ert/gui/simulation/run_dialog.py @@ -62,9 +62,9 @@ from ert.run_models.event import RunModelDataEvent, RunModelErrorEvent from ert.shared.status.utils import ( byte_with_unit, - disk_space_status, file_has_content, format_running_time, + get_mount_directory, ) from ..find_ert_info import find_ert_info @@ -196,7 +196,7 @@ def __init__( self._ticker = QTimer(self) self._ticker.timeout.connect(self._on_ticker) - self.run_path_mp = run_model.run_paths._runpath_format + self.run_path_mp = get_mount_directory(run_model.run_paths._runpath_format) self._total_progress_label = QLabel( _TOTAL_PROGRESS_TEMPLATE.format( @@ -430,10 +430,8 @@ def _on_ticker(self) -> None: self.running_time.setText(format_running_time(runtime)) maximum_memory_usage = self._snapshot_model.root.max_memory_usage - disk_usage = disk_space_status(self.run_path_mp) - if disk_usage: - self.disk_space.update_status(self.run_path_mp) + self.disk_space.update_status(self.run_path_mp) if maximum_memory_usage: self.memory_usage.setText( diff --git a/src/ert/gui/simulation/view/disk_space_widget.py b/src/ert/gui/simulation/view/disk_space_widget.py index 54d04901953..b997b12d247 100644 --- a/src/ert/gui/simulation/view/disk_space_widget.py +++ b/src/ert/gui/simulation/view/disk_space_widget.py @@ -5,7 +5,7 @@ class DiskSpaceWidget(QWidget): - def __init__(self, parent=None): + def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) layout = QHBoxLayout(self) @@ -13,43 +13,51 @@ def __init__(self, parent=None): layout.setSpacing(10) # Text label - self.label = QLabel(self) + self.usage_label = QLabel(self) + self.space_left_label = QLabel(self) # Progress bar self.progress_bar = QProgressBar(self) self.progress_bar.setRange(0, 100) self.progress_bar.setTextVisible(True) self.progress_bar.setFixedWidth(100) - self.progress_bar.setAlignment(Qt.AlignCenter) - - # Add color styling based on usage - self.progress_bar.setStyleSheet(""" - QProgressBar { - border: 1px solid #ccc; - border-radius: 2px; - text-align: center; - } - - QProgressBar::chunk { - background-color: qlineargradient( - x1: 0, y1: 0.5, x2: 1, y2: 0.5, - stop: 0 #2ecc71, - stop: 0.7 #f1c40f, - stop: 0.9 #e74c3c - ); - } - """) - - layout.addWidget(self.label) + self.progress_bar.setAlignment(Qt.AlignCenter) # type: ignore + + layout.addWidget(self.usage_label) layout.addWidget(self.progress_bar) + layout.addWidget(self.space_left_label) - def update_status(self, run_path_mp): + def update_status(self, mount_dir: str) -> None: """Update both the label and progress bar with current disk usage""" - disk_usage = disk_space_status(run_path_mp) - if disk_usage is not None: - self.label.setText("Disk space used runpath:") - self.progress_bar.setValue(int(disk_usage)) - self.progress_bar.setFormat(f"{disk_usage:.1f}%") + disk_info = disk_space_status(mount_dir) + if disk_info is not None: + usage = int(disk_info[0]) + self.usage_label.setText("Disk space runpath:") + self.progress_bar.setValue(usage) + self.progress_bar.setFormat(f"{disk_info[0]:.1f}%") + + # Set color based on usage threshold + if usage >= 90: + color = "#e74c3c" # Red for critical usage + elif usage >= 70: + color = "#f1c40f" # Yellow for warning + else: + color = "#2ecc71" # Green for normal usage + + self.progress_bar.setStyleSheet(f""" + QProgressBar {{ + border: 1px solid #ccc; + border-radius: 2px; + text-align: center; + }} + + QProgressBar::chunk {{ + background-color: {color}; + }} + """) + + self.space_left_label.setText(f"{disk_info[1]} free") + self.setVisible(True) else: self.setVisible(False) diff --git a/src/ert/shared/status/utils.py b/src/ert/shared/status/utils.py index f472e3e05a5..88cf3aa94a0 100644 --- a/src/ert/shared/status/utils.py +++ b/src/ert/shared/status/utils.py @@ -87,15 +87,15 @@ def get_ert_memory_usage() -> int: return usage.ru_maxrss // rss_scale -def disk_space_status(runpath: str) -> float | None: +def disk_space_status(mount_dir: str) -> tuple[float, str] | None: with contextlib.suppress(Exception): - mount_dir = _get_mount_directory(runpath) - total_space, used_space, _free_space = shutil.disk_usage(mount_dir) - percentage_used = used_space / total_space - return percentage_used + disk_info = shutil.disk_usage(mount_dir) + percentage_used = (disk_info.used / disk_info.total) * 100 + return percentage_used, byte_with_unit(disk_info.free) + return None -def _get_mount_directory(runpath: str) -> Path: +def get_mount_directory(runpath: str) -> Path: path = Path(runpath).absolute() while not path.is_mount():