Skip to content

Commit d2273ee

Browse files
authored
Merge branch 'develop' into about
2 parents 5121571 + 41d48f4 commit d2273ee

4 files changed

Lines changed: 127 additions & 26 deletions

File tree

src/petab_gui/controllers/mother_controller.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -840,13 +840,15 @@ def new_file(self):
840840
continue
841841
controller.clear_table()
842842
self.view.plot_dock.plot_it()
843+
self.unsaved_changes_change(False)
843844

844845
def check_model(self):
845846
"""Check the consistency of the model. And log the results."""
846847
capture_handler = CaptureLogHandler()
847-
logger = logging.getLogger("petab.v1.lint") # Target the specific
848-
# logger
849-
logger.addHandler(capture_handler)
848+
logger_lint = logging.getLogger("petab.v1.lint")
849+
logger_vis = logging.getLogger("petab.v1.visualize.lint")
850+
logger_lint.addHandler(capture_handler)
851+
logger_vis.addHandler(capture_handler)
850852

851853
try:
852854
# Run the consistency check
@@ -865,17 +867,22 @@ def check_model(self):
865867

866868
# Log the consistency check result
867869
if not failed:
868-
self.logger.log_message("Model is consistent.", color="green")
870+
self.logger.log_message(
871+
"PEtab problem has no errors.", color="green"
872+
)
869873
for model in self.model.pandas_models.values():
870874
model.reset_invalid_cells()
871875
else:
872-
self.logger.log_message("Model is inconsistent.", color="red")
876+
self.logger.log_message(
877+
"PEtab problem has errors.", color="red"
878+
)
873879
except Exception as e:
874880
msg = f"PEtab linter failed at some point: {filtered_error(e)}"
875881
self.logger.log_message(msg, color="red")
876882
finally:
877883
# Always remove the capture handler
878-
logger.removeHandler(capture_handler)
884+
logger_lint.removeHandler(capture_handler)
885+
logger_vis.removeHandler(capture_handler)
879886

880887
def unsaved_changes_change(self, unsaved_changes: bool):
881888
self.unsaved_changes = unsaved_changes
@@ -1176,3 +1183,7 @@ def about(self):
11761183
f"<small>Settings are stored in "
11771184
f"<a href='file://{config_file}'>{config_file}</a></small>",
11781185
)
1186+
1187+
def get_current_problem(self):
1188+
"""Get the current PEtab problem from the model."""
1189+
return self.model.current_petab_problem

src/petab_gui/controllers/table_controllers.py

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Classes for the controllers of the tables in the GUI."""
22

3+
import logging
34
import re
45
from collections.abc import Sequence
56
from pathlib import Path
@@ -22,7 +23,12 @@
2223
PandasTableModel,
2324
)
2425
from ..settings_manager import settings_manager
25-
from ..utils import ConditionInputDialog, get_selected, process_file
26+
from ..utils import (
27+
CaptureLogHandler,
28+
ConditionInputDialog,
29+
get_selected,
30+
process_file,
31+
)
2632
from ..views.other_views import DoseTimeDialog
2733
from ..views.table_view import (
2834
ColumnSuggestionDelegate,
@@ -1278,19 +1284,27 @@ def check_petab_lint(
12781284
col_name: str = None,
12791285
):
12801286
"""Check a number of rows of the model with petablint."""
1287+
# Validate full parameter table
12811288
if row_data is None:
12821289
row_data = self.model.get_df()
1283-
observable_df = self.mother_controller.model.observable.get_df()
1284-
measurement_df = self.mother_controller.model.measurement.get_df()
1285-
condition_df = self.mother_controller.model.condition.get_df()
1286-
sbml_model = self.mother_controller.model.sbml.get_current_sbml_model()
1287-
return petab.check_parameter_df(
1288-
row_data,
1289-
observable_df=observable_df,
1290-
measurement_df=measurement_df,
1291-
condition_df=condition_df,
1292-
model=sbml_model,
1293-
)
1290+
observable_df = self.mother_controller.model.observable.get_df()
1291+
measurement_df = self.mother_controller.model.measurement.get_df()
1292+
condition_df = self.mother_controller.model.condition.get_df()
1293+
sbml_model = (
1294+
self.mother_controller.model.sbml.get_current_sbml_model()
1295+
)
1296+
return petab.check_parameter_df(
1297+
row_data,
1298+
observable_df=observable_df,
1299+
measurement_df=measurement_df,
1300+
condition_df=condition_df,
1301+
model=sbml_model,
1302+
)
1303+
1304+
# Validate a single parameter row
1305+
# In this case, we don't pass any other dataframes/models to avoid
1306+
# false positives due to the incomplete parameter table.
1307+
return petab.check_parameter_df(row_data)
12941308

12951309

12961310
class VisualizationController(TableController):
@@ -1315,3 +1329,81 @@ def __init__(
13151329
undo_stack=undo_stack,
13161330
mother_controller=mother_controller,
13171331
)
1332+
1333+
@linter_wrapper(additional_error_check=True)
1334+
def check_petab_lint(
1335+
self,
1336+
row_data: pd.DataFrame = None,
1337+
row_name: str = None,
1338+
col_name: str = None,
1339+
):
1340+
"""Check a number of rows of the model with petablint."""
1341+
problem = self.mother_controller.get_current_problem()
1342+
capture_handler = CaptureLogHandler()
1343+
logger_vis = logging.getLogger("petab.v1.visualize.lint")
1344+
logger_vis.addHandler(capture_handler)
1345+
errors = petab.visualize.lint.validate_visualization_df(problem)
1346+
if not errors:
1347+
return not errors
1348+
captured_output = "<br>&nbsp;&nbsp;&nbsp;&nbsp;".join(
1349+
capture_handler.get_formatted_messages()
1350+
)
1351+
raise ValueError(captured_output)
1352+
1353+
def setup_completers(self):
1354+
"""Set completers for the visualization table."""
1355+
table_view = self.view.table_view
1356+
# plotTypeSimulation
1357+
index = self.model.return_column_index("plotTypeSimulation")
1358+
if index and index > -1:
1359+
self.completers["plotTypeSimulation"] = ComboBoxDelegate(
1360+
["LinePlot", "BarPlot", "ScatterPlot"]
1361+
)
1362+
table_view.setItemDelegateForColumn(
1363+
index, self.completers["plotTypeSimulation"]
1364+
)
1365+
# plotTypeData
1366+
index = self.model.return_column_index("plotTypeData")
1367+
if index and index > -1:
1368+
self.completers["plotTypeData"] = ComboBoxDelegate(
1369+
["MeanAndSD", "MeanAndSEM", "replicate", "provided"]
1370+
)
1371+
table_view.setItemDelegateForColumn(
1372+
index, self.completers["plotTypeData"]
1373+
)
1374+
# datasetId
1375+
index = self.model.return_column_index("datasetId")
1376+
if index and index > -1:
1377+
self.completers["datasetId"] = ColumnSuggestionDelegate(
1378+
self.mother_controller.model.measurement, "datasetId"
1379+
)
1380+
table_view.setItemDelegateForColumn(
1381+
index, self.completers["datasetId"]
1382+
)
1383+
# yValues
1384+
index = self.model.return_column_index("yValues")
1385+
if index and index > -1:
1386+
self.completers["yValues"] = ColumnSuggestionDelegate(
1387+
self.mother_controller.model.observable, "observableId"
1388+
)
1389+
table_view.setItemDelegateForColumn(
1390+
index, self.completers["yValues"]
1391+
)
1392+
# xScale
1393+
index = self.model.return_column_index("xScale")
1394+
if index and index > -1:
1395+
self.completers["xScale"] = ComboBoxDelegate(
1396+
["lin", "log", "log10", "order"]
1397+
)
1398+
table_view.setItemDelegateForColumn(
1399+
index, self.completers["xScale"]
1400+
)
1401+
# yScale
1402+
index = self.model.return_column_index("yScale")
1403+
if index and index > -1:
1404+
self.completers["yScale"] = ComboBoxDelegate(
1405+
["lin", "log", "log10", "order"]
1406+
)
1407+
table_view.setItemDelegateForColumn(
1408+
index, self.completers["yScale"]
1409+
)

src/petab_gui/models/pandas_table_model.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ def _set_data_single(self, index, value):
377377
if error:
378378
self.new_log_message.emit(
379379
f"Column '{column_name}' expects a value of type "
380-
f"{expected_type}, but got '{value}'",
380+
f"{expected_type.__name__}, but got '{value}'",
381381
"red",
382382
)
383383
return False
@@ -670,7 +670,9 @@ def return_column_index(self, column_name):
670670
Returns:
671671
int: The view column index for the given column name, or -1
672672
"""
673-
pass
673+
if column_name in self._data_frame.columns:
674+
return self._data_frame.columns.get_loc(column_name)
675+
return -1
674676

675677
def unique_values(self, column_name):
676678
"""Return a list of unique values in a specified column.
@@ -1115,12 +1117,6 @@ def get_default_values(self, index, changed: dict | None = None):
11151117
else:
11161118
command.redo()
11171119

1118-
def return_column_index(self, column_name):
1119-
"""Return the index of a column."""
1120-
if column_name in self._data_frame.columns:
1121-
return self._data_frame.columns.get_loc(column_name)
1122-
return -1
1123-
11241120

11251121
class ObservableModel(IndexedPandasTableModel):
11261122
"""Table model for the observable data."""

src/petab_gui/views/find_replace_bar.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def __init__(self, controller, parent=None):
2525
"Condition Table": self.controller.condition_controller,
2626
"Parameter Table": self.controller.parameter_controller,
2727
"Measurement Table": self.controller.measurement_controller,
28+
"Visualization Table": self.controller.visualization_controller,
29+
"Simulation Table": self.controller.simulation_controller,
2830
}
2931
self.selected_controllers = self.controller_map.values()
3032
self.only_search = False

0 commit comments

Comments
 (0)