Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 32 additions & 32 deletions src/badger/gui/components/data_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def get_data_from_dialog(self) -> None:
"""
dlg = BadgerLoadDataFromRunDialog(
parent=self,
data_table=self.data_table,
env_vocs=self.env_vocs.variable_names + self.env_vocs.output_names,
on_set=self.load_data_from_dialog,
)
self.tc_dialog = dlg
Expand Down Expand Up @@ -267,26 +267,14 @@ def get_data(self) -> pd.DataFrame:
def get_data_as_dict(self) -> dict:
data = get_table_content_as_dict(self.data_table)
if not self.info:
data = self.filter_metadata(data)
data = filter_metadata(data)

return data

@property
def routine(self) -> Routine:
return self.selected_routine

def filter_metadata(self, data: pd.DataFrame) -> dict:
"""
Remove metadata columns from data dictionary
"""
data_copy = data.copy()

metadata_cols = ["xopt_runtime", "xopt_error", "timestamp", "live"]
cols_to_drop = [col for col in metadata_cols if col in data_copy]
for key in cols_to_drop:
del data_copy[key]
return data_copy

def load_data_from_dialog(self, routine: Routine) -> None:
"""
Load routine data from dialog window. Checks to make sure the data in the selected routine to load
Expand All @@ -299,35 +287,34 @@ def load_data_from_dialog(self, routine: Routine) -> None:
# Data from routine to load
data = routine.data
# Create copy of data without metadata columns
filtered_data = self.filter_metadata(data)
filtered_data = filter_metadata(data)
data_keys = list(filtered_data.keys())

# VOCS selected in environment tab
selected_vocs = self.env_vocs.variable_names + self.env_vocs.output_names
# Data currently in table:
filtered_table_keys = list(filter_metadata(self.table_data).keys())

# Raise error if loaded data keys do not match selected vocs
if set(data_keys) != set(selected_vocs):
# This happens here if selected VOCS have been changed but old data is still in the table.
if self.has_data and set(data_keys) != set(filtered_table_keys):
dialog = QMessageBox(
text=str(
"Variables and objectives in data must match currently selected VOCS\n\n"
+ f"Keys in data to load:\n {data_keys}\n\n"
+ f"Selected VOCS:\n {selected_vocs}"
"Keys in loaded data do not match current table!"
"\nTry clearing the table before adding new data."
),
parent=self,
)
dialog.setIcon(QMessageBox.Warning)
dialog.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
result = dialog.exec_()
if result == QMessageBox.Cancel:
return
else:
# All keys match, add selected routine data to table
data["live"] = 0
combined_data = pd.concat([self.table_data, data], ignore_index=True)
self.run_data_checkbox.setEnabled(True)
dialog.setStandardButtons(QMessageBox.Ok)
_ = dialog.exec_()
return

# All keys match, add selected routine data to table
data["live"] = 0
combined_data = pd.concat([self.table_data, data], ignore_index=True)
self.run_data_checkbox.setEnabled(True)

self.selected_routine = routine
self.update_table(self.data_table, combined_data, routine.vocs)
self.selected_routine = routine
self.update_table(self.data_table, combined_data, routine.vocs)

def load_data(self, routine: Routine) -> None:
"""
Expand Down Expand Up @@ -390,3 +377,16 @@ def reset_data_table(self) -> None:
self.table_data = pd.DataFrame()
self.run_data_checkbox.setChecked(False)
self.run_data_checkbox.setEnabled(False)


def filter_metadata(data: dict) -> dict:
"""
Remove metadata columns from data dictionary
"""
data_copy = data.copy()

metadata_cols = ["xopt_runtime", "xopt_error", "timestamp", "live"]
cols_to_drop = [col for col in metadata_cols if col in data_copy]
for key in cols_to_drop:
del data_copy[key]
return data_copy
5 changes: 3 additions & 2 deletions src/badger/gui/pages/home_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from badger.gui.components.run_monitor import BadgerOptMonitor
from badger.gui.components.status_bar import BadgerStatusBar
from badger.gui.components.action_bar import BadgerActionBar
from badger.gui.components.data_panel import filter_metadata
from badger.utils import get_header
from badger.settings import init_settings

Expand Down Expand Up @@ -399,7 +400,7 @@ def toggle_lock(self, lock, lock_tab=1):
def validate_loaded_data_keys(self, vocs):
"""
This function is called when adding historical data to a new routine.
Makes sure that the keys of data to be loaded from data_panel match the
It makes sure that the keys of data to be loaded from data_panel match the
selected variables and objectives in VOCS. If they do not, raises an error.
If the set of data keys from self.data_panel matches provided VOCS variables
and objectives, opens a dialog to inform user that data has been added.
Expand Down Expand Up @@ -427,7 +428,7 @@ def validate_loaded_data_keys(self, vocs):
)

data = self.data_panel.get_data_as_dict()
data = self.data_panel.filter_metadata(data)
data = filter_metadata(data)
data_keys = data.keys()

# Notify user that data has been added to the routine
Expand Down
42 changes: 36 additions & 6 deletions src/badger/gui/windows/load_data_from_run_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
QVBoxLayout,
QLabel,
QFileDialog,
QMessageBox,
)
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
Expand All @@ -21,7 +22,7 @@
from badger.settings import init_settings
from badger.errors import BadgerRoutineError
from badger.routine import Routine
from badger.gui.components.data_table import TableWithCopy
from xopt.vocs import VOCS

stylesheet_run = """
QPushButton:hover:pressed
Expand Down Expand Up @@ -49,7 +50,7 @@ class BadgerLoadDataFromRunDialog(QDialog):
def __init__(
self,
parent: QWidget,
data_table: TableWithCopy = None,
env_vocs: VOCS = None,
on_set: Callable[[Routine], None] = None,
):
"""
Expand All @@ -65,8 +66,8 @@ def __init__(
"""
super().__init__(parent)

self.data_table = data_table
self.selected_routine = None
self.env_vocs = env_vocs
self.on_set = on_set # function from parent to call when loading data

self.init_ui()
Expand Down Expand Up @@ -191,8 +192,18 @@ def select_run(self) -> None:
"""
Update the data table with variable and objective data from the selected routine
"""
self.on_set(self.selected_routine)
self.close()
# Data from routine to load
data_keys = list(
self.selected_routine.vocs.variable_names
+ self.selected_routine.vocs.objective_names
)

# Check that variables and objectives match selected VOCS
if set(data_keys) == set(self.env_vocs):
self.on_set(self.selected_routine)
self.close()
else:
self.show_vocs_mismatch_dialog(data_keys, self.env_vocs)

def load_from_file(self) -> None:
options = QFileDialog.Options()
Expand Down Expand Up @@ -359,8 +370,27 @@ def _set_plot_data(
hist_x, not_live_data[name].to_numpy(dtype=np.double)
)

def show_vocs_mismatch_dialog(self, list1: List[str], list2: List[str]):
"""
Display a helpful dialog notifying the user that the data they are trying to load
does not have the same variables and objectives as they have selected in the GUI.
list1: list of keys (variables + objectives) in data to be loaded
list2: list of selected VOCS in main GUI for data to be added to
"""
dialog = QMessageBox(
text=str(
"Variables and objectives in data must match currently selected VOCS\n\n"
+ f"Keys in data to load:\n {list1}\n\n"
+ f"Selected VOCS:\n {list2}"
),
parent=None,
)
dialog.setIcon(QMessageBox.Warning)
dialog.setStandardButtons(QMessageBox.Ok)
dialog.exec_()

def cancel_changes(self):
self.close()

def closeEvent(self, event: QtGui.QCloseEvent) -> None:
def closeEvent(self, event):
event.accept()
Loading