Skip to content

Commit 3cdb29b

Browse files
default_values (#55)
* Default values for parameter table * Added default configuration as option (mainly for condition table * Added default configuration for condition table * Fixed a bug when adding new columns * Added DEfault values to observables * Added Interface to ask for condition ID in Uploading datamatrix * Adding a new row due to automatic addition of condition or observable also fills the defaults now * No majority vote on condition files * Created a settings manager, loading and saving settings to manager. * Unified settings to also include recent files * static method * moved default configurations to settings manager * Default values can be now changed and applied. * fixed typo that lead to warnings * Settings slightly adapted for now! * Adapted changes to handler * intermediate * Limit choice for some columns. Apply index defaults also to par and cond tables. Allow defaults for meas table
1 parent c335f18 commit 3cdb29b

File tree

11 files changed

+739
-26
lines changed

11 files changed

+739
-26
lines changed

src/petab_gui/C.py

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,117 @@
5959
# String constants
6060
ROW = 'row'
6161
COLUMN = 'column'
62-
INDEX = 'index'
62+
INDEX = 'index'
63+
64+
COPY_FROM = "copy from"
65+
USE_DEFAULT = "use default"
66+
NO_DEFAULT = "no default"
67+
MIN_COLUMN = "use column min"
68+
MAX_COLUMN = "use column max"
69+
MODE = "use most frequent"
70+
STRATEGIES_DEFAULT = [COPY_FROM, USE_DEFAULT, NO_DEFAULT]
71+
STRATEGIES_DEFAULT_EXT = STRATEGIES_DEFAULT + [MODE]
72+
STRATEGIES_DEFAULT_ALL = STRATEGIES_DEFAULT_EXT + [MIN_COLUMN, MAX_COLUMN]
73+
STRATEGY_TOOLTIP = {
74+
COPY_FROM: "Copy from another column in the same row",
75+
USE_DEFAULT: "Use default value",
76+
NO_DEFAULT: "Do not set a value",
77+
MIN_COLUMN: "Use the minimum value of the column",
78+
MAX_COLUMN: "Use the maximum value of the column",
79+
MODE: "Use the most frequent value of the column",
80+
}
81+
SOURCE_COLUMN = "source_column"
82+
DEFAULT_VALUE = "default_value"
83+
84+
# Default Configurations of Default Values
85+
ALLOWED_STRATEGIES_OBS = {
86+
"observableId": STRATEGIES_DEFAULT,
87+
"observableName": STRATEGIES_DEFAULT,
88+
"observableFormula": STRATEGIES_DEFAULT,
89+
"observableTransformation": [USE_DEFAULT, NO_DEFAULT, MODE],
90+
"noiseFormula": [COPY_FROM, USE_DEFAULT, NO_DEFAULT, MODE],
91+
"noiseDistribution": [USE_DEFAULT, NO_DEFAULT, MODE],
92+
}
93+
ALLOWED_STRATEGIES_PAR = {
94+
"parameterId": STRATEGIES_DEFAULT,
95+
"parameterName": STRATEGIES_DEFAULT,
96+
"parameterScale": [USE_DEFAULT, NO_DEFAULT, MODE],
97+
"lowerBound": [MIN_COLUMN, MAX_COLUMN, USE_DEFAULT, NO_DEFAULT, MODE],
98+
"upperBound": [MAX_COLUMN, MAX_COLUMN, USE_DEFAULT, NO_DEFAULT, MODE],
99+
"nominalValue": [USE_DEFAULT, NO_DEFAULT],
100+
"estimate": [USE_DEFAULT, NO_DEFAULT, MODE],
101+
}
102+
ALLOWED_STRATEGIES_COND = {
103+
"conditionId": STRATEGIES_DEFAULT,
104+
"conditionName": STRATEGIES_DEFAULT,
105+
}
106+
ALLOWED_STRATEGIES_MEAS = {
107+
"observableId": STRATEGIES_DEFAULT,
108+
"preequilibrationConditionId": STRATEGIES_DEFAULT_EXT,
109+
"simulationConditionId": STRATEGIES_DEFAULT_EXT,
110+
"time": [NO_DEFAULT, USE_DEFAULT, MODE],
111+
"measurement": [NO_DEFAULT, USE_DEFAULT, MODE],
112+
"observableParameters": STRATEGIES_DEFAULT_EXT,
113+
"noiseParameters": STRATEGIES_DEFAULT_EXT,
114+
"datasetId": [COPY_FROM, USE_DEFAULT, NO_DEFAULT, MODE],
115+
"replicateId": [COPY_FROM, USE_DEFAULT, NO_DEFAULT, MODE],
116+
}
117+
ALLOWED_STRATEGIES = {
118+
"observable": ALLOWED_STRATEGIES_OBS,
119+
"parameter": ALLOWED_STRATEGIES_PAR,
120+
"condition": ALLOWED_STRATEGIES_COND,
121+
"measurement": ALLOWED_STRATEGIES_MEAS
122+
}
123+
DEFAULT_OBS_CONFIG = {
124+
"observableId": {
125+
"strategy": COPY_FROM, SOURCE_COLUMN: "observableFormula",
126+
DEFAULT_VALUE: "new_observable"
127+
},
128+
"observableName": {
129+
"strategy": COPY_FROM, SOURCE_COLUMN: "observableId"
130+
},
131+
"noiseFormula": {
132+
"strategy": USE_DEFAULT, DEFAULT_VALUE: 1
133+
},
134+
"observableTransformation": {
135+
"strategy": USE_DEFAULT,
136+
DEFAULT_VALUE: "lin"
137+
},
138+
"noiseDistribution": {
139+
"strategy": USE_DEFAULT,
140+
DEFAULT_VALUE: "normal"
141+
}
142+
}
143+
DEFAULT_PAR_CONFIG = {
144+
"parameterName": {
145+
"strategy": COPY_FROM, SOURCE_COLUMN: "parameterId",
146+
DEFAULT_VALUE: "new_parameter"
147+
},
148+
"parameterScale": {
149+
"strategy": USE_DEFAULT, DEFAULT_VALUE: "log10"
150+
},
151+
"lowerBound": {
152+
"strategy": MIN_COLUMN
153+
},
154+
"upperBound": {
155+
"strategy": MAX_COLUMN
156+
},
157+
"estimate": {
158+
"strategy": USE_DEFAULT, DEFAULT_VALUE: 1
159+
},
160+
}
161+
DEFAULT_COND_CONFIG = {
162+
"conditionId": {
163+
"strategy": USE_DEFAULT, DEFAULT_VALUE: "new_condition"
164+
},
165+
"conditionName": {
166+
"strategy": COPY_FROM, SOURCE_COLUMN: "conditionId"
167+
}
168+
}
169+
DEFAULT_MEAS_CONFIG = {}
170+
DEFAULT_CONFIGS = {
171+
"observable": DEFAULT_OBS_CONFIG,
172+
"parameter": DEFAULT_PAR_CONFIG,
173+
"condition": DEFAULT_COND_CONFIG,
174+
"measurement": DEFAULT_MEAS_CONFIG
175+
}

src/petab_gui/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class PEtabGuiApp(QApplication):
2525
def __init__(self):
2626
super().__init__(sys.argv)
2727

28-
# Load the styleshee
28+
# Load the stylesheet
2929
# self.apply_stylesheet()
3030
self.model = PEtabModel()
3131
self.view = MainWindow()
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""The Default Handlers for the GUI."""
2+
import pandas as pd
3+
import numpy as np
4+
import copy
5+
6+
from collections import Counter
7+
from ..C import (COPY_FROM, USE_DEFAULT, NO_DEFAULT, MIN_COLUMN, MAX_COLUMN,
8+
MODE, DEFAULT_VALUE, SOURCE_COLUMN, STRATEGIES_DEFAULT)
9+
10+
11+
class DefaultHandlerModel:
12+
def __init__(self, model, config):
13+
"""
14+
Initialize the handler for the model.
15+
:param model: The PandasTable Model containing the Data.
16+
:param config: Dictionary containing strategies and settings for each column.
17+
"""
18+
self._model = model
19+
# TODO: Check what happens with non inplace operations
20+
self.model = model._data_frame
21+
self.config = config
22+
self.model_index = self.model.index.name
23+
24+
def get_default(self, column_name, row_index=None):
25+
"""
26+
Get the default value for a column based on its strategy.
27+
:param column_name: The name of the column to compute the default for.
28+
:param row_index: Optional index of the row (needed for some strategies).
29+
:return: The computed default value.
30+
"""
31+
source_column = column_name
32+
if column_name not in self.config:
33+
if "default_config" in self.config:
34+
column_name = "default_config"
35+
else:
36+
return ""
37+
38+
column_config = self.config[column_name]
39+
strategy = column_config.get("strategy", NO_DEFAULT)
40+
default_value = column_config.get(DEFAULT_VALUE, "")
41+
42+
if strategy == USE_DEFAULT:
43+
return default_value
44+
elif strategy == NO_DEFAULT:
45+
return ""
46+
elif strategy == MIN_COLUMN:
47+
return self._min_column(column_name)
48+
elif strategy == MAX_COLUMN:
49+
return self._max_column(column_name)
50+
elif strategy == COPY_FROM:
51+
return self._copy_column(column_name, column_config, row_index)
52+
elif strategy == MODE:
53+
column_config[SOURCE_COLUMN] = source_column
54+
return self._majority_vote(column_name, column_config)
55+
else:
56+
raise ValueError(f"Unknown strategy '{strategy}' for column '{column_name}'.")
57+
58+
def _min_column(self, column_name):
59+
if column_name in self.model:
60+
column_data = self.model[column_name].replace("", np.nan).dropna()
61+
if not column_data.empty:
62+
return column_data.min()
63+
return ""
64+
65+
def _max_column(self, column_name):
66+
if column_name in self.model:
67+
column_data = self.model[column_name].replace("", np.nan).dropna()
68+
if not column_data.empty:
69+
return column_data.max()
70+
return ""
71+
72+
def _copy_column(self, column_name, config, row_index):
73+
source_column = config.get(SOURCE_COLUMN, column_name)
74+
source_column_valid = (
75+
source_column in self.model or source_column == self.model_index
76+
)
77+
if source_column and source_column_valid and row_index is not None:
78+
prefix = config.get("prefix", "")
79+
if row_index in self.model.index:
80+
if source_column == self.model_index:
81+
return f"{prefix}{row_index}"
82+
value = f"{prefix}{self.model.at[row_index, source_column]}"
83+
return value if pd.notna(value) else ""
84+
return ""
85+
86+
def _majority_vote(self, column_name, config):
87+
"""Use the most frequent value in the column as the default.
88+
89+
Defaults to last used value in case of a tie.
90+
"""
91+
source_column = config.get(SOURCE_COLUMN, column_name)
92+
source_column_valid = (
93+
source_column in self.model or source_column == self.model_index
94+
)
95+
if source_column and source_column_valid:
96+
valid_values = copy.deepcopy(self.model[source_column][:-1])
97+
valid_values = valid_values.iloc[::-1]
98+
if valid_values.empty:
99+
return ""
100+
value_counts = Counter(valid_values)
101+
return value_counts.most_common(1)[0][0]
102+
return ""

src/petab_gui/controllers/mother_controller.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from ..views import TaskBar
2222
from .utils import prompt_overwrite_or_append, RecentFilesManager
2323
from functools import partial
24+
from ..settings_manager import SettingsDialog, settings_manager
2425

2526

2627
class MainController:
@@ -168,6 +169,10 @@ def setup_connections(self):
168169
self.recent_files_manager.open_file.connect(
169170
partial(self.open_file, mode="overwrite")
170171
)
172+
# Settings logging
173+
settings_manager.new_log_message.connect(
174+
self.logger.log_message
175+
)
171176

172177
def setup_actions(self):
173178
"""Setup actions for the main controller."""
@@ -337,6 +342,12 @@ def setup_actions(self):
337342
"Clear Log", self.view
338343
)
339344
actions["clear_log"].triggered.connect(self.logger.clear_log)
345+
# Settings
346+
actions["settings"] = QAction(
347+
qta.icon("mdi6.cog"),
348+
"Settings", self.view
349+
)
350+
actions["settings"].triggered.connect(self.open_settings)
340351

341352
# Opening the PEtab documentation
342353
actions["open_documentation"] = QAction(
@@ -543,7 +554,7 @@ def _open_file(self, actionable, file_path, sep, mode):
543554
)
544555
elif actionable == "data_matrix":
545556
self.measurement_controller.process_data_matrix_file(
546-
file_path, sep, mode
557+
file_path, mode, sep
547558
)
548559

549560
def open_yaml_and_load_files(self, yaml_path=None, mode="overwrite"):
@@ -745,6 +756,18 @@ def paste_from_clipboard(self):
745756
if controller:
746757
controller.paste_from_clipboard()
747758

759+
def open_settings(self):
760+
"""Opens the settings Dialogue."""
761+
# retrieve all current columns from the tables
762+
table_columns = {
763+
"observable": self.observable_controller.get_columns(),
764+
"parameter": self.parameter_controller.get_columns(),
765+
"measurement": self.measurement_controller.get_columns(),
766+
"condition": self.condition_controller.get_columns(),
767+
}
768+
settings_dialog = SettingsDialog(table_columns, self.view)
769+
settings_dialog.exec()
770+
748771
def set_docks_visible(self):
749772
"""Handles Visibility of docks."""
750773
pass

0 commit comments

Comments
 (0)