steer-core 0.1.26__tar.gz → 0.1.28__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {steer_core-0.1.26 → steer_core-0.1.28}/PKG-INFO +1 -1
- {steer_core-0.1.26/steer_core/Apps → steer_core-0.1.28/steer_core/ContextManagers}/ContextManagers.py +1 -1
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Data/database.db +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/DataManager.py +21 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Mixins/Coordinates.py +49 -3
- steer_core-0.1.28/steer_core/__init__.py +13 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core.egg-info/PKG-INFO +1 -1
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core.egg-info/SOURCES.txt +1 -13
- steer_core-0.1.26/steer_core/Apps/Callbacks/ConfigInteractions.py +0 -377
- steer_core-0.1.26/steer_core/Apps/Callbacks/StyleManagement.py +0 -60
- steer_core-0.1.26/steer_core/Apps/Components/MaterialSelectors.py +0 -836
- steer_core-0.1.26/steer_core/Apps/Components/RangeSliderComponents.py +0 -552
- steer_core-0.1.26/steer_core/Apps/Components/SliderComponents.py +0 -693
- steer_core-0.1.26/steer_core/Apps/Performance/CallbackTimer.py +0 -18
- steer_core-0.1.26/steer_core/Apps/Utils/SliderControls.py +0 -728
- steer_core-0.1.26/steer_core/ContextManagers/__init__.py +0 -0
- steer_core-0.1.26/steer_core/Data/__init__.py +0 -0
- steer_core-0.1.26/steer_core/Decorators/Electrochemical.py +0 -29
- steer_core-0.1.26/steer_core/Decorators/__init__.py +0 -0
- steer_core-0.1.26/steer_core/Mixins/__init__.py +0 -0
- steer_core-0.1.26/steer_core/__init__.py +0 -4
- {steer_core-0.1.26 → steer_core-0.1.28}/README.md +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/pyproject.toml +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/setup.cfg +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Constants/Units.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Constants/Universal.py +0 -0
- {steer_core-0.1.26/steer_core/Apps/Components → steer_core-0.1.28/steer_core/Constants}/__init__.py +0 -0
- {steer_core-0.1.26/steer_core/Apps/Performance → steer_core-0.1.28/steer_core/ContextManagers}/__init__.py +0 -0
- {steer_core-0.1.26/steer_core/Apps/Utils → steer_core-0.1.28/steer_core/Data}/__init__.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Decorators/Coordinates.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Decorators/General.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Decorators/Objects.py +0 -0
- {steer_core-0.1.26/steer_core/Apps → steer_core-0.1.28/steer_core/Decorators}/__init__.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Mixins/Colors.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Mixins/Data.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Mixins/Dunder.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Mixins/Plotter.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Mixins/Serializer.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core/Mixins/TypeChecker.py +0 -0
- {steer_core-0.1.26/steer_core/Constants → steer_core-0.1.28/steer_core/Mixins}/__init__.py +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core.egg-info/dependency_links.txt +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core.egg-info/requires.txt +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/steer_core.egg-info/top_level.txt +0 -0
- {steer_core-0.1.26 → steer_core-0.1.28}/test/test_validation_mixin.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: steer-core
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.28
|
|
4
4
|
Summary: Modelling energy storage from cell to site - STEER OpenCell Design
|
|
5
5
|
Author-email: Nicholas Siemons <nsiemons@stanford.edu>
|
|
6
6
|
Maintainer-email: Nicholas Siemons <nsiemons@stanford.edu>
|
|
Binary file
|
|
@@ -7,6 +7,7 @@ from steer_core.Constants.Units import *
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class DataManager:
|
|
10
|
+
|
|
10
11
|
def __init__(self):
|
|
11
12
|
with importlib.resources.path("steer_core.Data", "database.db") as db_path:
|
|
12
13
|
self._db_path = db_path
|
|
@@ -280,6 +281,26 @@ class DataManager:
|
|
|
280
281
|
|
|
281
282
|
return data
|
|
282
283
|
|
|
284
|
+
def get_prismatic_container_materials(self, most_recent: bool = True) -> pd.DataFrame:
|
|
285
|
+
"""
|
|
286
|
+
Retrieves prismatic container materials from the database.
|
|
287
|
+
|
|
288
|
+
:param most_recent: If True, returns only the most recent entry.
|
|
289
|
+
:return: DataFrame with prismatic container materials.
|
|
290
|
+
"""
|
|
291
|
+
data = (
|
|
292
|
+
self.get_data(table_name="prismatic_container_materials")
|
|
293
|
+
.groupby("name", group_keys=False)
|
|
294
|
+
.apply(
|
|
295
|
+
lambda x: x.sort_values("date", ascending=False).head(1)
|
|
296
|
+
if most_recent
|
|
297
|
+
else x
|
|
298
|
+
)
|
|
299
|
+
.reset_index(drop=True)
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
return data
|
|
303
|
+
|
|
283
304
|
@staticmethod
|
|
284
305
|
def read_half_cell_curve(half_cell_path) -> pd.DataFrame:
|
|
285
306
|
"""
|
|
@@ -130,7 +130,10 @@ class CoordinateMixin:
|
|
|
130
130
|
|
|
131
131
|
@staticmethod
|
|
132
132
|
def rotate_coordinates(
|
|
133
|
-
coords: np.ndarray,
|
|
133
|
+
coords: np.ndarray,
|
|
134
|
+
axis: str,
|
|
135
|
+
angle: float,
|
|
136
|
+
center: tuple = None
|
|
134
137
|
) -> np.ndarray:
|
|
135
138
|
"""
|
|
136
139
|
Rotate a (N, 3) NumPy array of 3D coordinates around the specified axis.
|
|
@@ -240,9 +243,52 @@ class CoordinateMixin:
|
|
|
240
243
|
The width of the square.
|
|
241
244
|
y_width : float
|
|
242
245
|
The height of the square.
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
Tuple[np.ndarray, np.ndarray]
|
|
250
|
+
Tuple containing x and y coordinate arrays defining the square/rectangle
|
|
243
251
|
"""
|
|
244
|
-
x_coords = [x, x, x + x_width, x + x_width, x]
|
|
245
|
-
y_coords = [y, y + y_width, y + y_width, y, y]
|
|
252
|
+
x_coords = np.array([x, x, x + x_width, x + x_width, x])
|
|
253
|
+
y_coords = np.array([y, y + y_width, y + y_width, y, y])
|
|
254
|
+
return x_coords, y_coords
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def build_circle_array(
|
|
258
|
+
center_x: float, center_y: float, radius: float, num_points: int = 64, anticlockwise: bool = True
|
|
259
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
260
|
+
"""
|
|
261
|
+
Build a NumPy array representing a circle defined by its center and radius.
|
|
262
|
+
|
|
263
|
+
Parameters
|
|
264
|
+
----------
|
|
265
|
+
center_x : float
|
|
266
|
+
The x-coordinate of the circle center.
|
|
267
|
+
center_y : float
|
|
268
|
+
The y-coordinate of the circle center.
|
|
269
|
+
radius : float
|
|
270
|
+
The radius of the circle.
|
|
271
|
+
num_points : int, optional
|
|
272
|
+
Number of points to use for the circle approximation, by default 64
|
|
273
|
+
anticlockwise : bool, optional
|
|
274
|
+
If True, points are ordered anticlockwise; if False, clockwise, by default True
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
Tuple[np.ndarray, np.ndarray]
|
|
279
|
+
Tuple containing x and y coordinate arrays defining the circle
|
|
280
|
+
"""
|
|
281
|
+
# Generate angles from 0 to 2π
|
|
282
|
+
angles = np.linspace(0, 2 * np.pi, num_points + 1)
|
|
283
|
+
|
|
284
|
+
# Reverse angles for clockwise direction
|
|
285
|
+
if not anticlockwise:
|
|
286
|
+
angles = angles[::-1]
|
|
287
|
+
|
|
288
|
+
# Calculate x and y coordinates
|
|
289
|
+
x_coords = center_x + radius * np.cos(angles)
|
|
290
|
+
y_coords = center_y + radius * np.sin(angles)
|
|
291
|
+
|
|
246
292
|
return x_coords, y_coords
|
|
247
293
|
|
|
248
294
|
@staticmethod
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
__version__ = "0.1.28"
|
|
2
|
+
|
|
3
|
+
# datamanager import
|
|
4
|
+
from .DataManager import DataManager
|
|
5
|
+
|
|
6
|
+
from .Mixins.Colors import ColorMixin
|
|
7
|
+
from .Mixins.Coordinates import CoordinateMixin
|
|
8
|
+
from .Mixins.Plotter import PlotterMixin
|
|
9
|
+
from .Mixins.TypeChecker import ValidationMixin
|
|
10
|
+
from .Mixins.Dunder import DunderMixin
|
|
11
|
+
from .Mixins.Serializer import SerializerMixin
|
|
12
|
+
from .Mixins.Data import DataMixin
|
|
13
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: steer-core
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.28
|
|
4
4
|
Summary: Modelling energy storage from cell to site - STEER OpenCell Design
|
|
5
5
|
Author-email: Nicholas Siemons <nsiemons@stanford.edu>
|
|
6
6
|
Maintainer-email: Nicholas Siemons <nsiemons@stanford.edu>
|
|
@@ -7,26 +7,14 @@ steer_core.egg-info/SOURCES.txt
|
|
|
7
7
|
steer_core.egg-info/dependency_links.txt
|
|
8
8
|
steer_core.egg-info/requires.txt
|
|
9
9
|
steer_core.egg-info/top_level.txt
|
|
10
|
-
steer_core/Apps/ContextManagers.py
|
|
11
|
-
steer_core/Apps/__init__.py
|
|
12
|
-
steer_core/Apps/Callbacks/ConfigInteractions.py
|
|
13
|
-
steer_core/Apps/Callbacks/StyleManagement.py
|
|
14
|
-
steer_core/Apps/Components/MaterialSelectors.py
|
|
15
|
-
steer_core/Apps/Components/RangeSliderComponents.py
|
|
16
|
-
steer_core/Apps/Components/SliderComponents.py
|
|
17
|
-
steer_core/Apps/Components/__init__.py
|
|
18
|
-
steer_core/Apps/Performance/CallbackTimer.py
|
|
19
|
-
steer_core/Apps/Performance/__init__.py
|
|
20
|
-
steer_core/Apps/Utils/SliderControls.py
|
|
21
|
-
steer_core/Apps/Utils/__init__.py
|
|
22
10
|
steer_core/Constants/Units.py
|
|
23
11
|
steer_core/Constants/Universal.py
|
|
24
12
|
steer_core/Constants/__init__.py
|
|
13
|
+
steer_core/ContextManagers/ContextManagers.py
|
|
25
14
|
steer_core/ContextManagers/__init__.py
|
|
26
15
|
steer_core/Data/__init__.py
|
|
27
16
|
steer_core/Data/database.db
|
|
28
17
|
steer_core/Decorators/Coordinates.py
|
|
29
|
-
steer_core/Decorators/Electrochemical.py
|
|
30
18
|
steer_core/Decorators/General.py
|
|
31
19
|
steer_core/Decorators/Objects.py
|
|
32
20
|
steer_core/Decorators/__init__.py
|
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
from typing import List, Tuple
|
|
2
|
-
from dash import no_update
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def generate_parameters_and_ranges(object, config) -> Tuple[List[float], List[float], List[float]]:
|
|
6
|
-
"""
|
|
7
|
-
Generate parameter value lists and their corresponding min/max ranges for any object.
|
|
8
|
-
|
|
9
|
-
This function extracts parameter values from an object based on a configuration's
|
|
10
|
-
parameter list, and retrieves the minimum and maximum values for each parameter
|
|
11
|
-
if range attributes exist (e.g., '{param}_range'), otherwise uses the current
|
|
12
|
-
value as both min and max.
|
|
13
|
-
|
|
14
|
-
Parameters
|
|
15
|
-
----------
|
|
16
|
-
object : Any
|
|
17
|
-
The object from which to extract parameter values. Must have attributes
|
|
18
|
-
corresponding to the parameters listed in config.parameter_list.
|
|
19
|
-
config : Type
|
|
20
|
-
Configuration class or object that contains a 'parameter_list' attribute
|
|
21
|
-
specifying which parameters to extract from the object.
|
|
22
|
-
|
|
23
|
-
Returns
|
|
24
|
-
-------
|
|
25
|
-
Tuple[List[float], List[float], List[float]]
|
|
26
|
-
A tuple containing three lists:
|
|
27
|
-
- parameter_values: Current values of each parameter
|
|
28
|
-
- min_values: Minimum values for each parameter (from {param}_range[0]
|
|
29
|
-
or current value if no range exists)
|
|
30
|
-
- max_values: Maximum values for each parameter (from {param}_range[1]
|
|
31
|
-
or current value if no range exists)
|
|
32
|
-
|
|
33
|
-
Examples
|
|
34
|
-
--------
|
|
35
|
-
>>> class MyObject:
|
|
36
|
-
... def __init__(self):
|
|
37
|
-
... self.temperature = 25.0
|
|
38
|
-
... self.temperature_range = (0.0, 100.0)
|
|
39
|
-
... self.pressure = 1.0
|
|
40
|
-
>>>
|
|
41
|
-
>>> class Config:
|
|
42
|
-
... parameter_list = ['temperature', 'pressure']
|
|
43
|
-
>>>
|
|
44
|
-
>>> obj = MyObject()
|
|
45
|
-
>>> values, mins, maxs = generate_parameter_and_list_ranges(obj, Config)
|
|
46
|
-
>>> print(values) # [25.0, 1.0]
|
|
47
|
-
>>> print(mins) # [0.0, 1.0]
|
|
48
|
-
>>> print(maxs) # [100.0, 1.0]
|
|
49
|
-
|
|
50
|
-
Notes
|
|
51
|
-
-----
|
|
52
|
-
- If an object has a '{param}_range' attribute, it should be a tuple/list
|
|
53
|
-
with exactly two elements: (min_value, max_value)
|
|
54
|
-
- If no range attribute exists for a parameter, the current value is used
|
|
55
|
-
for both minimum and maximum values
|
|
56
|
-
- All extracted values are expected to be numeric (convertible to float)
|
|
57
|
-
"""
|
|
58
|
-
parameter_values = []
|
|
59
|
-
min_values = []
|
|
60
|
-
max_values = []
|
|
61
|
-
|
|
62
|
-
parameter_list = config.parameter_list
|
|
63
|
-
|
|
64
|
-
for param in parameter_list:
|
|
65
|
-
|
|
66
|
-
value = getattr(object, param)
|
|
67
|
-
parameter_values.append(value)
|
|
68
|
-
|
|
69
|
-
if hasattr(object, f"{param}_range"):
|
|
70
|
-
min_values.append(getattr(object, f"{param}_range")[0])
|
|
71
|
-
max_values.append(getattr(object, f"{param}_range")[1])
|
|
72
|
-
else:
|
|
73
|
-
min_values.append(value)
|
|
74
|
-
max_values.append(value)
|
|
75
|
-
|
|
76
|
-
return parameter_values, min_values, max_values
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def generate_rangeslider_parameters_and_ranges(object, config) -> Tuple[
|
|
81
|
-
List[float],
|
|
82
|
-
List[float],
|
|
83
|
-
List[float],
|
|
84
|
-
List[float],
|
|
85
|
-
]:
|
|
86
|
-
"""
|
|
87
|
-
Generate range slider parameter values and their corresponding min/max bounds for any object.
|
|
88
|
-
|
|
89
|
-
This function extracts range parameter values from an object based on a configuration's
|
|
90
|
-
range_slider_parameters list. Each parameter is expected to be a tuple/list representing
|
|
91
|
-
a range (start, end). It also retrieves the absolute minimum and maximum bounds for each
|
|
92
|
-
parameter from corresponding '{param}_range' attributes, or uses the current range values
|
|
93
|
-
as bounds if no range attribute exists.
|
|
94
|
-
|
|
95
|
-
Parameters
|
|
96
|
-
----------
|
|
97
|
-
object : Any
|
|
98
|
-
The object from which to extract range parameter values. Must have attributes
|
|
99
|
-
corresponding to the parameters listed in config.range_slider_parameters, where
|
|
100
|
-
each attribute contains a tuple/list of (start, end) values. Optionally has
|
|
101
|
-
'{param}_range' attributes defining the absolute bounds for each parameter.
|
|
102
|
-
config : Any
|
|
103
|
-
Configuration class or object that contains a 'range_slider_parameters' attribute
|
|
104
|
-
specifying which range parameters to extract from the object.
|
|
105
|
-
|
|
106
|
-
Returns
|
|
107
|
-
-------
|
|
108
|
-
Tuple[List[float], List[float], List[float], List[float]]
|
|
109
|
-
A tuple containing four lists:
|
|
110
|
-
- start_values: Start values for each range parameter (from param[0])
|
|
111
|
-
- end_values: End values for each range parameter (from param[1])
|
|
112
|
-
- min_values: Absolute minimum bounds for each parameter (from {param}_range[0]
|
|
113
|
-
or param[0] if no range exists)
|
|
114
|
-
- max_values: Absolute maximum bounds for each parameter (from {param}_range[1]
|
|
115
|
-
or param[1] if no range exists)
|
|
116
|
-
|
|
117
|
-
Examples
|
|
118
|
-
--------
|
|
119
|
-
>>> class MyCollector:
|
|
120
|
-
... def __init__(self):
|
|
121
|
-
... self.voltage_window = (2.5, 4.2) # Current range setting
|
|
122
|
-
... self.voltage_window_range = (0.0, 5.0) # Absolute bounds
|
|
123
|
-
... self.current_limit = (0.1, 2.0) # Current range setting
|
|
124
|
-
... # No current_limit_range, so uses current values as bounds
|
|
125
|
-
>>>
|
|
126
|
-
>>> class Config:
|
|
127
|
-
... range_slider_parameters = ['voltage_window', 'current_limit']
|
|
128
|
-
>>>
|
|
129
|
-
>>> collector = MyCollector()
|
|
130
|
-
>>> starts, ends, mins, maxs = generate_rangeslider_parameters_and_ranges_from_config(
|
|
131
|
-
... collector, Config)
|
|
132
|
-
>>> print(starts) # [2.5, 0.1]
|
|
133
|
-
>>> print(ends) # [4.2, 2.0]
|
|
134
|
-
>>> print(mins) # [0.0, 0.1] # Uses range for voltage, current value for current
|
|
135
|
-
>>> print(maxs) # [5.0, 2.0] # Uses range for voltage, current value for current
|
|
136
|
-
|
|
137
|
-
Notes
|
|
138
|
-
-----
|
|
139
|
-
- If config.range_slider_parameters is empty, returns four empty lists
|
|
140
|
-
- Each parameter in range_slider_parameters must exist as an attribute on the object
|
|
141
|
-
and contain exactly two elements: (start_value, end_value)
|
|
142
|
-
- If a parameter has a corresponding '{param}_range' attribute, those bounds are used
|
|
143
|
-
- If no range attribute exists for a parameter, the current start/end values are used
|
|
144
|
-
as the minimum/maximum bounds respectively
|
|
145
|
-
- All values are expected to be numeric (convertible to float)
|
|
146
|
-
- This function is typically used for range slider UI components where
|
|
147
|
-
users can select a range within predefined bounds
|
|
148
|
-
|
|
149
|
-
Raises
|
|
150
|
-
------
|
|
151
|
-
AttributeError
|
|
152
|
-
If the object doesn't have the required parameter attributes or if config
|
|
153
|
-
doesn't have range_slider_parameters attribute
|
|
154
|
-
IndexError
|
|
155
|
-
If parameter values don't contain exactly two elements
|
|
156
|
-
"""
|
|
157
|
-
start_values = []
|
|
158
|
-
end_values = []
|
|
159
|
-
min_values = []
|
|
160
|
-
max_values = []
|
|
161
|
-
|
|
162
|
-
parameter_list = config.range_slider_parameters
|
|
163
|
-
|
|
164
|
-
for param in parameter_list:
|
|
165
|
-
|
|
166
|
-
value = getattr(object, param)
|
|
167
|
-
start_values.append(value[0])
|
|
168
|
-
end_values.append(value[1])
|
|
169
|
-
|
|
170
|
-
if hasattr(object, f"{param}_range"):
|
|
171
|
-
min_values.append(getattr(object, f"{param}_range")[0])
|
|
172
|
-
max_values.append(getattr(object, f"{param}_range")[1])
|
|
173
|
-
else:
|
|
174
|
-
min_values.append(value[0])
|
|
175
|
-
max_values.append(value[1])
|
|
176
|
-
|
|
177
|
-
return start_values, end_values, min_values, max_values
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def validate_dependent_properties(object, config) -> None:
|
|
181
|
-
"""
|
|
182
|
-
Validate and clamp dependent properties to their valid hard ranges for any object.
|
|
183
|
-
|
|
184
|
-
This function validates parameter values from an object based on a configuration's
|
|
185
|
-
parameter list, checking each parameter against its corresponding '{param}_hard_range'
|
|
186
|
-
attribute. If a parameter value falls outside its hard range, it is automatically
|
|
187
|
-
clamped to the nearest valid boundary (minimum or maximum).
|
|
188
|
-
|
|
189
|
-
Parameters
|
|
190
|
-
----------
|
|
191
|
-
object : Any
|
|
192
|
-
The object whose parameter values will be validated and potentially modified.
|
|
193
|
-
Must have attributes corresponding to the parameters listed in config.parameter_list.
|
|
194
|
-
Should have '{param}_hard_range' attributes defining the valid bounds for validation.
|
|
195
|
-
config : Any
|
|
196
|
-
Configuration class or object that contains a 'parameter_list' attribute
|
|
197
|
-
specifying which parameters to validate on the object.
|
|
198
|
-
|
|
199
|
-
Returns
|
|
200
|
-
-------
|
|
201
|
-
None
|
|
202
|
-
This function modifies the object in-place and does not return any values.
|
|
203
|
-
|
|
204
|
-
Examples
|
|
205
|
-
--------
|
|
206
|
-
>>> class MyObject:
|
|
207
|
-
... def __init__(self):
|
|
208
|
-
... self.temperature = 150.0 # Outside valid range
|
|
209
|
-
... self.temperature_hard_range = (0.0, 100.0)
|
|
210
|
-
... self.pressure = 0.5 # Within valid range
|
|
211
|
-
... self.pressure_hard_range = (0.0, 10.0)
|
|
212
|
-
>>>
|
|
213
|
-
>>> class Config:
|
|
214
|
-
... parameter_list = ['temperature', 'pressure']
|
|
215
|
-
>>>
|
|
216
|
-
>>> obj = MyObject()
|
|
217
|
-
>>> print(obj.temperature) # 150.0 (before validation)
|
|
218
|
-
>>> validate_dependent_properties(obj, Config)
|
|
219
|
-
>>> print(obj.temperature) # 100.0 (clamped to maximum)
|
|
220
|
-
>>> print(obj.pressure) # 0.5 (unchanged, within range)
|
|
221
|
-
|
|
222
|
-
Notes
|
|
223
|
-
-----
|
|
224
|
-
- Only parameters with corresponding '{param}_hard_range' attributes are validated
|
|
225
|
-
- Parameters without hard range attributes are silently skipped (no validation performed)
|
|
226
|
-
- Hard range attributes should be tuples/lists with exactly two elements: (min_value, max_value)
|
|
227
|
-
- Values are clamped to the nearest boundary if they fall outside the valid range
|
|
228
|
-
- This function modifies the object in-place, changing parameter values as needed
|
|
229
|
-
- Typically used after parameter updates to ensure all values remain within valid bounds
|
|
230
|
-
|
|
231
|
-
Raises
|
|
232
|
-
------
|
|
233
|
-
AttributeError
|
|
234
|
-
If the object doesn't have a parameter attribute listed in config.parameter_list,
|
|
235
|
-
or if config doesn't have a parameter_list attribute
|
|
236
|
-
IndexError
|
|
237
|
-
If a hard_range attribute doesn't contain exactly two elements
|
|
238
|
-
"""
|
|
239
|
-
parameter_list = config.parameter_list
|
|
240
|
-
|
|
241
|
-
for param in parameter_list:
|
|
242
|
-
try:
|
|
243
|
-
# Get the hard range and current value for this parameter
|
|
244
|
-
param_range = getattr(object, f"{param}_hard_range")
|
|
245
|
-
param_value = getattr(object, param)
|
|
246
|
-
|
|
247
|
-
# Clamp value to valid range if necessary
|
|
248
|
-
if param_value < param_range[0]:
|
|
249
|
-
setattr(object, param, param_range[0])
|
|
250
|
-
elif param_value > param_range[1]:
|
|
251
|
-
setattr(object, param, param_range[1])
|
|
252
|
-
|
|
253
|
-
except AttributeError:
|
|
254
|
-
# Parameter or hard range doesn't exist, skip validation
|
|
255
|
-
continue
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def create_no_update_response(
|
|
259
|
-
config=None,
|
|
260
|
-
existing_warnings: List[str] = [],
|
|
261
|
-
n: int = None,
|
|
262
|
-
n_rangeslider: int = None,
|
|
263
|
-
) -> Tuple:
|
|
264
|
-
"""
|
|
265
|
-
Create a no-update response tuple for Dash callbacks based on configuration parameters.
|
|
266
|
-
|
|
267
|
-
This function generates a standardized response tuple containing `no_update` values
|
|
268
|
-
for all UI components defined in a configuration object. This is typically used in
|
|
269
|
-
Dash callbacks when no updates should be made to the UI components, preserving
|
|
270
|
-
their current state while potentially updating warning messages.
|
|
271
|
-
|
|
272
|
-
Parameters
|
|
273
|
-
----------
|
|
274
|
-
config : Any, optional
|
|
275
|
-
Configuration object that defines the UI components and their parameters.
|
|
276
|
-
Should contain attributes like 'parameter_list', 'range_slider_parameters',
|
|
277
|
-
'dropdown_menu', 'radioitem_parameters', and 'text_parameters'.
|
|
278
|
-
If None, only basic response structure is created.
|
|
279
|
-
existing_warnings : List[str], default []
|
|
280
|
-
List of existing warning messages to include in the response.
|
|
281
|
-
These warnings will be preserved in the callback output.
|
|
282
|
-
n : int, optional
|
|
283
|
-
Number of regular slider/input parameters. If None, determined from
|
|
284
|
-
config.parameter_list length. Used to create the correct number of
|
|
285
|
-
no_update responses for sliders and inputs.
|
|
286
|
-
n_rangeslider : int, optional
|
|
287
|
-
Number of range slider parameters. If None, determined from
|
|
288
|
-
config.range_slider_parameters length. Used to create the correct
|
|
289
|
-
number of no_update responses for range sliders.
|
|
290
|
-
|
|
291
|
-
Returns
|
|
292
|
-
-------
|
|
293
|
-
Tuple
|
|
294
|
-
A tuple containing warning messages followed by no_update responses for all
|
|
295
|
-
UI components defined in the configuration:
|
|
296
|
-
- warnings: List[str] - Warning messages (first element)
|
|
297
|
-
- cache_key: no_update - Cache key for callback optimization
|
|
298
|
-
- slider_values: List[no_update] - Current slider values
|
|
299
|
-
- slider_mins: List[no_update] - Slider minimum values
|
|
300
|
-
- slider_maxs: List[no_update] - Slider maximum values
|
|
301
|
-
- slider_marks: List[no_update] - Slider tick marks
|
|
302
|
-
- slider_steps: List[no_update] - Slider step sizes
|
|
303
|
-
- input_steps: List[no_update] - Input field step sizes
|
|
304
|
-
|
|
305
|
-
Additional elements based on config attributes:
|
|
306
|
-
- dropdown_value: no_update (if config.dropdown_menu exists)
|
|
307
|
-
- range_slider_*: List[no_update] (if config.range_slider_parameters exists)
|
|
308
|
-
- radioitem_values: List[no_update] (if config.radioitem_parameters exists)
|
|
309
|
-
- text_values: List[no_update] (if config.text_parameters exists)
|
|
310
|
-
|
|
311
|
-
Examples
|
|
312
|
-
--------
|
|
313
|
-
>>> class Config:
|
|
314
|
-
... parameter_list = ['temperature', 'pressure']
|
|
315
|
-
... dropdown_menu = True
|
|
316
|
-
... range_slider_parameters = ['voltage_window']
|
|
317
|
-
... radioitem_parameters = ['mode']
|
|
318
|
-
... text_parameters = ['label']
|
|
319
|
-
>>>
|
|
320
|
-
>>> config = Config()
|
|
321
|
-
>>> warnings = ['Temperature out of range']
|
|
322
|
-
>>> response = create_no_update_response(config, warnings)
|
|
323
|
-
>>> print(len(response)) # Number of response elements
|
|
324
|
-
>>> print(response[0]) # ['Temperature out of range']
|
|
325
|
-
>>> print(response[1]) # no_update (cache_key)
|
|
326
|
-
|
|
327
|
-
Notes
|
|
328
|
-
-----
|
|
329
|
-
- This function is primarily used in Dash callback error handling or when
|
|
330
|
-
callback conditions are not met and no UI updates should occur
|
|
331
|
-
- The response tuple structure must match the callback's Output specification
|
|
332
|
-
- Optional UI components (dropdown, range sliders, radio items, text inputs)
|
|
333
|
-
are only included in the response if they exist in the configuration
|
|
334
|
-
- All UI component responses use Dash's `no_update` to preserve current state
|
|
335
|
-
- The function automatically determines response tuple length based on config
|
|
336
|
-
|
|
337
|
-
Raises
|
|
338
|
-
------
|
|
339
|
-
AttributeError
|
|
340
|
-
If config object doesn't have expected attributes when accessed
|
|
341
|
-
"""
|
|
342
|
-
n = len(config.parameter_list) if n is None else n
|
|
343
|
-
|
|
344
|
-
response = (
|
|
345
|
-
no_update, # cache_key
|
|
346
|
-
[no_update] * n, # slider values
|
|
347
|
-
[no_update] * n, # slider mins
|
|
348
|
-
[no_update] * n, # slider maxs
|
|
349
|
-
[no_update] * n, # slider marks
|
|
350
|
-
[no_update] * n, # slider steps
|
|
351
|
-
[no_update] * n, # input steps
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
if hasattr(config, "dropdown_menu") and config.dropdown_menu:
|
|
355
|
-
response += (no_update,)
|
|
356
|
-
|
|
357
|
-
if hasattr(config, "range_slider_parameters") and config.range_slider_parameters:
|
|
358
|
-
n_rangeslider = len(config.range_slider_parameters) if n_rangeslider is None else n_rangeslider
|
|
359
|
-
response += (
|
|
360
|
-
[no_update] * n_rangeslider, # range_slider_values
|
|
361
|
-
[no_update] * n_rangeslider, # range slider mins
|
|
362
|
-
[no_update] * n_rangeslider, # range slider maxs
|
|
363
|
-
[no_update] * n_rangeslider, # range slider marks
|
|
364
|
-
[no_update] * n_rangeslider, # range slider steps
|
|
365
|
-
[no_update] * n_rangeslider, # range slider start values
|
|
366
|
-
[no_update] * n_rangeslider, # range slider end values
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
if hasattr(config, "radioitem_parameters") and config.radioitem_parameters:
|
|
370
|
-
num_radioitem_params = len(config.radioitem_parameters)
|
|
371
|
-
response += ([no_update] * num_radioitem_params,) # radioitem values
|
|
372
|
-
|
|
373
|
-
if hasattr(config, "text_parameters") and config.text_parameters:
|
|
374
|
-
num_text_params = len(config.text_parameters)
|
|
375
|
-
response += ([no_update] * num_text_params,) # text item values
|
|
376
|
-
|
|
377
|
-
return (existing_warnings,) + tuple(response)
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
from dash import no_update
|
|
2
|
-
from typing import List
|
|
3
|
-
|
|
4
|
-
def update_style_display(current_style, target_display):
|
|
5
|
-
"""
|
|
6
|
-
Helper function to update style display property with safe None handling.
|
|
7
|
-
|
|
8
|
-
Args:
|
|
9
|
-
current_style: Current style dict or None
|
|
10
|
-
target_display: Target display value ("block" or "none")
|
|
11
|
-
|
|
12
|
-
Returns:
|
|
13
|
-
no_update if style already has target display, otherwise updated style dict
|
|
14
|
-
"""
|
|
15
|
-
current_display = (current_style or {}).get("display")
|
|
16
|
-
if current_display == target_display:
|
|
17
|
-
return no_update
|
|
18
|
-
return {**(current_style or {}), "display": target_display}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def update_tab_styles(active_tab: str, tab_names: List[str], current_styles: List[dict]) -> List:
|
|
23
|
-
"""
|
|
24
|
-
Helper function to update tab styles based on the active tab.
|
|
25
|
-
Only updates styles when the display property needs to change.
|
|
26
|
-
|
|
27
|
-
Parameters
|
|
28
|
-
----------
|
|
29
|
-
active_tab : str
|
|
30
|
-
The currently active tab name
|
|
31
|
-
tab_names : List[str]
|
|
32
|
-
List of all tab names to check against
|
|
33
|
-
current_styles : List[dict]
|
|
34
|
-
List of current style dictionaries for each tab
|
|
35
|
-
|
|
36
|
-
Returns
|
|
37
|
-
-------
|
|
38
|
-
List
|
|
39
|
-
List of updated styles or no_update for each tab
|
|
40
|
-
"""
|
|
41
|
-
updated_styles = []
|
|
42
|
-
|
|
43
|
-
for tab_name, current_style in zip(tab_names, current_styles):
|
|
44
|
-
# Determine what the display should be
|
|
45
|
-
should_display = "block" if active_tab == tab_name else "none"
|
|
46
|
-
|
|
47
|
-
# Get current display value (handle None style case)
|
|
48
|
-
current_display = (current_style or {}).get("display")
|
|
49
|
-
|
|
50
|
-
# If display is already correct, return no_update
|
|
51
|
-
if current_display == should_display:
|
|
52
|
-
updated_styles.append(no_update)
|
|
53
|
-
else:
|
|
54
|
-
# Create new style preserving existing properties
|
|
55
|
-
new_style = {**(current_style or {}), "display": should_display}
|
|
56
|
-
updated_styles.append(new_style)
|
|
57
|
-
|
|
58
|
-
return updated_styles
|
|
59
|
-
|
|
60
|
-
|