ansys-solutions-dash-super-components 0.2.dev7__py3-none-any.whl

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.
Files changed (26) hide show
  1. ansys/solutions/dash_super_components/__init__.py +44 -0
  2. ansys/solutions/dash_super_components/assets/__init__.py +20 -0
  3. ansys/solutions/dash_super_components/assets/dashAgGridComponentFunctions.js +71 -0
  4. ansys/solutions/dash_super_components/authenticator.py +277 -0
  5. ansys/solutions/dash_super_components/dual_input_range_slider.py +397 -0
  6. ansys/solutions/dash_super_components/folder_selector.py +830 -0
  7. ansys/solutions/dash_super_components/input_form.py +908 -0
  8. ansys/solutions/dash_super_components/input_row_array.py +368 -0
  9. ansys/solutions/dash_super_components/logs_supervisor.py +887 -0
  10. ansys/solutions/dash_super_components/transaction_method_status_badge.py +366 -0
  11. ansys/solutions/dash_super_components/transaction_supervisor.py +587 -0
  12. ansys/solutions/dash_super_components/tree.py +396 -0
  13. ansys/solutions/dash_super_components/utils/__init__.py +18 -0
  14. ansys/solutions/dash_super_components/utils/aio_ids.py +83 -0
  15. ansys/solutions/dash_super_components/utils/api_requests.py +69 -0
  16. ansys/solutions/dash_super_components/utils/assets_server.py +52 -0
  17. ansys/solutions/dash_super_components/utils/config.py +77 -0
  18. ansys/solutions/dash_super_components/utils/logs_parser.py +151 -0
  19. ansys/solutions/dash_super_components/utils/path_validator.py +163 -0
  20. ansys/solutions/dash_super_components/utils/status_badge_properties.py +60 -0
  21. ansys/solutions/dash_super_components/utils/svg_icons.py +395 -0
  22. ansys_solutions_dash_super_components-0.2.dev7.dist-info/METADATA +169 -0
  23. ansys_solutions_dash_super_components-0.2.dev7.dist-info/RECORD +26 -0
  24. ansys_solutions_dash_super_components-0.2.dev7.dist-info/WHEEL +4 -0
  25. ansys_solutions_dash_super_components-0.2.dev7.dist-info/licenses/AUTHORS +12 -0
  26. ansys_solutions_dash_super_components-0.2.dev7.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,366 @@
1
+ # Copyright (C) 2026 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ """``dmc.Button`` that displays and updates SAF transaction method status."""
19
+
20
+ import copy
21
+ from typing import Any
22
+ import uuid
23
+
24
+ try:
25
+ # dash >=3.2.0
26
+ from dash import NoUpdate
27
+ except ImportError:
28
+ # dash >=2.18.2, <3.2.0
29
+ from dash._callback import NoUpdate # pyright: ignore[reportPrivateImportUsage]
30
+
31
+ try:
32
+ # dash-extensions < 2.0.5
33
+ from dash_extensions.enrich import _Wildcard # pyright: ignore[reportAttributeAccessIssue]
34
+ except ImportError:
35
+ # dash-extensions >= 2.0.5
36
+ from dash_extensions.enrich import (
37
+ Wildcard as _Wildcard, # pyright: ignore[reportAttributeAccessIssue]
38
+ )
39
+ from dash_extensions.enrich import (
40
+ MATCH,
41
+ Input,
42
+ Output,
43
+ State,
44
+ callback,
45
+ dcc,
46
+ html,
47
+ no_update,
48
+ )
49
+ import dash_mantine_components as dmc
50
+
51
+ from ansys.solutions.dash_super_components.utils.aio_ids import AIOIds
52
+ from ansys.solutions.dash_super_components.utils.api_requests import GlowAPIRequest
53
+ from ansys.solutions.dash_super_components.utils.status_badge_properties import (
54
+ STATUS_BADGE_PROPERTIES,
55
+ )
56
+ from ansys.solutions.dash_super_components.utils.svg_icons import (
57
+ IconNames,
58
+ create_base64_svg_src,
59
+ )
60
+
61
+
62
+ class TransactionMethodStatusBadge(html.Div):
63
+ """
64
+ A Dash component that displays and updates the status of a SAF transaction method.
65
+
66
+ TransactionMethodStatusBadge is based on the Dash All-in-One (AIO) component pattern.
67
+ It renders a compact button badge that periodically polls the GLOW API and reflects the
68
+ current status of the specified method with
69
+ color-coded states.
70
+
71
+ .. warning::
72
+
73
+ This component requires a SAF-based solution with access to the GLOW API. It must
74
+ be instantiated inside a callback (not directly in the layout) because it needs the
75
+ project URL from ``DashClient``.
76
+
77
+ To activate monitoring from a callback use::
78
+
79
+ TransactionMethodStatusBadge.ids.activate_monitoring(aio_id) # Store; set to True
80
+
81
+ Parameters
82
+ ----------
83
+ url : str
84
+ The URL of the GLOW API server.
85
+ step_name : str
86
+ The name of the GLOW step (StepModel) that contains the method.
87
+ method_name : str
88
+ The name of the GLOW transaction method to monitor.
89
+ stop_monitoring_on_termination : bool, optional
90
+ If ``True``, monitoring stops automatically when the method reaches a terminal
91
+ state (completed, failed, aborted). Default is ``True``.
92
+ button_properties : dict, optional
93
+ Additional properties forwarded to the status badge :class:`dmc.Button`.
94
+ label_properties : dict, optional
95
+ Additional properties forwarded to the label :class:`dmc.Text` component.
96
+ interval_properties : dict, optional
97
+ Additional properties forwarded to the :class:`dcc.Interval` polling component.
98
+ aio_id : str, optional
99
+ Unique identifier for this component instance. A UUID is generated if not provided.
100
+ """
101
+
102
+ class TransactionMethodStatusBadgeIds(AIOIds):
103
+ """Provides IDs for the subcomponents of :class:`TransactionMethodStatusBadge`."""
104
+
105
+ @classmethod
106
+ def _interval(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
107
+ """Return the ID for the interval component.
108
+
109
+ Parameters
110
+ ----------
111
+ aio_id : str or _Wildcard
112
+ The component's unique identifier.
113
+
114
+ Returns
115
+ -------
116
+ dict[str, Any]
117
+ The unique ID dictionary for the interval subcomponent.
118
+ """
119
+ return cls.make_id_dict("transaction-method-status-badge", "interval", aio_id)
120
+
121
+ @classmethod
122
+ def _data_storage(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
123
+ """Return the ID for the data storage component.
124
+
125
+ Parameters
126
+ ----------
127
+ aio_id : str or _Wildcard
128
+ The component's unique identifier.
129
+
130
+ Returns
131
+ -------
132
+ dict[str, Any]
133
+ The unique ID dictionary for the data storage subcomponent.
134
+ """
135
+ return cls.make_id_dict("transaction-method-status-badge", "data-storage", aio_id)
136
+
137
+ @classmethod
138
+ def label(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
139
+ """Return the ID for the label component.
140
+
141
+ Parameters
142
+ ----------
143
+ aio_id : str or _Wildcard
144
+ The component's unique identifier.
145
+
146
+ Returns
147
+ -------
148
+ dict[str, Any]
149
+ The unique ID dictionary for the label subcomponent.
150
+ """
151
+ return cls.make_id_dict("transaction-method-status-badge", "label", aio_id)
152
+
153
+ @classmethod
154
+ def status_badge(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
155
+ """Return the ID for the button component.
156
+
157
+ Parameters
158
+ ----------
159
+ aio_id : str or _Wildcard
160
+ The component's unique identifier.
161
+
162
+ Returns
163
+ -------
164
+ dict[str, Any]
165
+ The unique ID dictionary for the button subcomponent.
166
+ """
167
+ return cls.make_id_dict("transaction-method-status-badge", "button", aio_id)
168
+
169
+ @classmethod
170
+ def activate_monitoring(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
171
+ """Return the ID for the activate monitoring component.
172
+
173
+ Parameters
174
+ ----------
175
+ aio_id : str or _Wildcard
176
+ The component's unique identifier.
177
+
178
+ Returns
179
+ -------
180
+ dict[str, Any]
181
+ The unique ID dictionary for the activate monitoring subcomponent.
182
+ """
183
+ return cls.make_id_dict(
184
+ "transaction-method-status-badge", "activate-monitoring", aio_id
185
+ )
186
+
187
+ ids = TransactionMethodStatusBadgeIds
188
+
189
+ def __init__(
190
+ self,
191
+ url: str,
192
+ step_name: str,
193
+ method_name: str,
194
+ stop_monitoring_on_termination: bool = True,
195
+ button_properties: dict[str, Any] | None = None,
196
+ label_properties: dict[str, Any] | None = None,
197
+ interval_properties: dict[str, Any] | None = None,
198
+ aio_id: str | None = None,
199
+ ):
200
+ button_properties = {} if button_properties is None else copy.deepcopy(button_properties)
201
+ label_properties = {} if label_properties is None else copy.deepcopy(label_properties)
202
+ interval_properties = (
203
+ {} if interval_properties is None else copy.deepcopy(interval_properties)
204
+ )
205
+
206
+ if aio_id is None:
207
+ aio_id = str(uuid.uuid4())
208
+
209
+ self.aio_id = aio_id
210
+
211
+ default_label_properties = {
212
+ "style": {
213
+ "fontSize": "15px",
214
+ "textAlign": "left",
215
+ "display": "block" if label_properties else "none",
216
+ },
217
+ }
218
+
219
+ default_button_properties = {
220
+ "loading": False,
221
+ "size": "compact-xs",
222
+ "radius": "xl",
223
+ }
224
+
225
+ default_interval_properties = {
226
+ "interval": 5000,
227
+ }
228
+
229
+ data = {
230
+ "url": url,
231
+ "step_name": step_name,
232
+ "method_name": method_name,
233
+ "stop_monitoring_on_termination": stop_monitoring_on_termination,
234
+ }
235
+
236
+ # Merge label properties, handling the style dict specially
237
+ for key, value in default_label_properties.items():
238
+ if key not in label_properties:
239
+ label_properties[key] = value
240
+ elif key == "style" and isinstance(label_properties[key], dict):
241
+ # Merge style dicts: defaults first, then user overrides
242
+ merged_style = copy.deepcopy(value)
243
+ merged_style.update(label_properties[key])
244
+ label_properties[key] = merged_style
245
+
246
+ # properties in default_button_properties can be overwritten:
247
+ for key, value in default_button_properties.items():
248
+ if key not in button_properties:
249
+ button_properties[key] = value
250
+
251
+ # button properties "children", "color", "variant" and "leftSection" are fully controlled by
252
+ # the component - will be overwritten by correct values in the callbacks:
253
+ button_properties["children"] = ""
254
+ button_properties["color"] = "gray"
255
+ button_properties["variant"] = "outline"
256
+ button_properties["leftSection"] = None
257
+
258
+ # properties in default_interval_properties can be overwritten:
259
+ for key, value in default_interval_properties.items():
260
+ if key not in interval_properties:
261
+ interval_properties[key] = value
262
+
263
+ # interval property "disabled" is fully controlled by the component:
264
+ interval_properties["disabled"] = True
265
+
266
+ super().__init__(
267
+ [
268
+ html.Div(
269
+ [
270
+ html.Div(id=self.ids.label(aio_id), **label_properties),
271
+ dmc.Button(id=self.ids.status_badge(aio_id), **button_properties),
272
+ ],
273
+ style={
274
+ "display": "flex",
275
+ "flexDirection": "column",
276
+ "alignItems": "left",
277
+ },
278
+ ),
279
+ dcc.Interval(id=self.ids._interval(aio_id), **interval_properties),
280
+ dcc.Store(
281
+ id=self.ids._data_storage(aio_id),
282
+ storage_type="session",
283
+ data=data,
284
+ ),
285
+ dcc.Store(
286
+ id=self.ids.activate_monitoring(aio_id),
287
+ storage_type="session",
288
+ data=False,
289
+ ),
290
+ ],
291
+ )
292
+
293
+ @staticmethod
294
+ @callback(
295
+ Output(ids._interval(MATCH), "disabled", allow_duplicate=True),
296
+ Input(ids.activate_monitoring(MATCH), "data"),
297
+ prevent_initial_call=True,
298
+ )
299
+ def activate_deactivate_monitoring(
300
+ activate_monitoring: bool,
301
+ ) -> bool:
302
+ """Activate the monitoring."""
303
+ return not activate_monitoring
304
+
305
+ @staticmethod
306
+ @callback(
307
+ Output(ids.status_badge(MATCH), "children"),
308
+ Output(ids.status_badge(MATCH), "color"),
309
+ Output(ids.status_badge(MATCH), "variant"),
310
+ Output(ids.status_badge(MATCH), "leftSection"),
311
+ Output(ids.activate_monitoring(MATCH), "data", allow_duplicate=True),
312
+ Input(ids._interval(MATCH), "n_intervals"),
313
+ Input(ids._interval(MATCH), "disabled"),
314
+ State(ids._data_storage(MATCH), "data"),
315
+ State(ids.activate_monitoring(MATCH), "data"),
316
+ )
317
+ def update_badge(
318
+ n_intervals: int,
319
+ interval_disabled: bool,
320
+ data_storage: dict[str, Any],
321
+ monitoring_active: bool,
322
+ ) -> tuple[str, str, str, html.Img | None, bool | NoUpdate]:
323
+ """Update button status."""
324
+ glow_api_request = GlowAPIRequest(data_storage["url"])
325
+ method_status = glow_api_request.get_transaction_method_status(
326
+ data_storage["step_name"],
327
+ data_storage["method_name"],
328
+ )
329
+
330
+ button_children = method_status.upper()
331
+ color = STATUS_BADGE_PROPERTIES[method_status.lower()]["color"]
332
+
333
+ if not monitoring_active:
334
+ variant = "outline"
335
+ left_section = None
336
+ activate_monitoring = no_update
337
+ return (
338
+ button_children,
339
+ color,
340
+ variant,
341
+ left_section,
342
+ activate_monitoring,
343
+ )
344
+
345
+ stop_monitoring_on_termination = data_storage["stop_monitoring_on_termination"]
346
+ variant = "filled"
347
+ left_section = html.Img(src=create_base64_svg_src(IconNames.EOS_LOADING))
348
+
349
+ if (
350
+ method_status.lower() in ["completed", "failed"] and stop_monitoring_on_termination
351
+ ): # stop monitoring
352
+ variant = "outline"
353
+ left_section = None
354
+ activate_monitoring = False
355
+ else:
356
+ # method needs to be monitored (is running/will be running shortly, or monitoring
357
+ # shouldn't be stopped)
358
+ activate_monitoring = no_update
359
+
360
+ return (
361
+ button_children,
362
+ color,
363
+ variant,
364
+ left_section,
365
+ activate_monitoring,
366
+ )