dynamic-systems-testing-framework 1.0.1__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 (185) hide show
  1. dstf/__init__.py +41 -0
  2. dstf/__main__.py +10 -0
  3. dstf/authoring/__init__.py +13 -0
  4. dstf/authoring/builtin/__init__.py +32 -0
  5. dstf/authoring/builtin/accept.py +205 -0
  6. dstf/authoring/builtin/edit_criteria.py +479 -0
  7. dstf/authoring/builtin/edit_family.py +228 -0
  8. dstf/authoring/builtin/edit_on_plot.py +1354 -0
  9. dstf/authoring/builtin/edit_tree.py +1399 -0
  10. dstf/authoring/builtin/enqueue.py +144 -0
  11. dstf/authoring/capabilities.py +463 -0
  12. dstf/authoring/errors.py +49 -0
  13. dstf/authoring/family_rows.py +526 -0
  14. dstf/authoring/forms.py +631 -0
  15. dstf/authoring/layout.py +104 -0
  16. dstf/authoring/live_rows.py +337 -0
  17. dstf/authoring/live_runs.py +385 -0
  18. dstf/authoring/needle.py +127 -0
  19. dstf/authoring/picker_rows.py +92 -0
  20. dstf/authoring/queue_rows.py +354 -0
  21. dstf/authoring/session.py +3016 -0
  22. dstf/authoring/shell/__init__.py +9 -0
  23. dstf/authoring/shell/actions.py +702 -0
  24. dstf/authoring/shell/app.py +53 -0
  25. dstf/authoring/shell/drawer_controls.py +190 -0
  26. dstf/authoring/shell/family_fold.py +468 -0
  27. dstf/authoring/shell/figure_hook.py +249 -0
  28. dstf/authoring/shell/grips.js +1000 -0
  29. dstf/authoring/shell/header.py +783 -0
  30. dstf/authoring/shell/live_view.py +93 -0
  31. dstf/authoring/shell/picker.py +267 -0
  32. dstf/authoring/shell/placing.py +122 -0
  33. dstf/authoring/shell/plot_card.py +1171 -0
  34. dstf/authoring/shell/queue_panel.py +433 -0
  35. dstf/authoring/shell/rail.py +593 -0
  36. dstf/authoring/shell/shell.py +528 -0
  37. dstf/authoring/shell/stats_card.py +501 -0
  38. dstf/authoring/shell/strip.py +204 -0
  39. dstf/authoring/shell/style.py +721 -0
  40. dstf/authoring/shell/suite.py +277 -0
  41. dstf/authoring/shell/suite_grid.js +414 -0
  42. dstf/authoring/shell/suite_grid.py +373 -0
  43. dstf/authoring/shell/tab_theme.py +110 -0
  44. dstf/authoring/shell/tabstate.py +146 -0
  45. dstf/authoring/shell/test_list.py +179 -0
  46. dstf/authoring/shell/test_page.py +1404 -0
  47. dstf/authoring/shell/tree_card.py +1171 -0
  48. dstf/authoring/shell/variables_drawer.py +589 -0
  49. dstf/authoring/shell/widgets.py +188 -0
  50. dstf/authoring/situation.py +287 -0
  51. dstf/authoring/spec_document.py +215 -0
  52. dstf/authoring/spec_preview.py +83 -0
  53. dstf/authoring/suite_rows.py +99 -0
  54. dstf/authoring/tree_move.py +516 -0
  55. dstf/authoring/tree_pointer.py +33 -0
  56. dstf/authoring/tree_text.py +61 -0
  57. dstf/authoring/typed_number.js +64 -0
  58. dstf/authoring/typed_number.py +76 -0
  59. dstf/authoring/unscorable.py +141 -0
  60. dstf/authoring/variable_rows.py +216 -0
  61. dstf/backends/__init__.py +283 -0
  62. dstf/backends/base.py +1117 -0
  63. dstf/backends/common/__init__.py +1 -0
  64. dstf/backends/common/json_driver.py +484 -0
  65. dstf/backends/common/live_mat.py +369 -0
  66. dstf/backends/common/mat_reader.py +632 -0
  67. dstf/backends/common/proc_kill.py +238 -0
  68. dstf/backends/common/proc_output.py +20 -0
  69. dstf/backends/common/result_files.py +109 -0
  70. dstf/backends/common/result_variables.py +329 -0
  71. dstf/backends/common/statistics.py +39 -0
  72. dstf/backends/cross_backend.py +193 -0
  73. dstf/backends/dymola/__init__.py +5 -0
  74. dstf/backends/dymola/backend.py +776 -0
  75. dstf/backends/dymola/interface_loader.py +241 -0
  76. dstf/backends/dymola/log_parser.py +396 -0
  77. dstf/backends/dymola/persistent_backend.py +949 -0
  78. dstf/backends/fmpy/__init__.py +11 -0
  79. dstf/backends/fmpy/backend.py +443 -0
  80. dstf/backends/julia/__init__.py +27 -0
  81. dstf/backends/julia/backend.py +156 -0
  82. dstf/backends/julia/persistent_backend.py +541 -0
  83. dstf/backends/julia/run_persistent.jl +167 -0
  84. dstf/backends/julia/run_test.jl +146 -0
  85. dstf/backends/openmodelica/__init__.py +21 -0
  86. dstf/backends/openmodelica/backend.py +368 -0
  87. dstf/backends/openmodelica/init_xml.py +76 -0
  88. dstf/backends/openmodelica/log_parser.py +119 -0
  89. dstf/backends/openmodelica/mos_generator.py +228 -0
  90. dstf/backends/openmodelica/persistent_backend.py +1045 -0
  91. dstf/backends/openmodelica/session_loader.py +63 -0
  92. dstf/backends/persistent.py +1811 -0
  93. dstf/backends/pool.py +602 -0
  94. dstf/backends/progress.py +395 -0
  95. dstf/backends/python/__init__.py +11 -0
  96. dstf/backends/python/backend.py +141 -0
  97. dstf/backends/python/run_test.py +141 -0
  98. dstf/backends/run_metadata.py +103 -0
  99. dstf/backends/types.py +310 -0
  100. dstf/cli/__init__.py +12 -0
  101. dstf/cli/author_cmd.py +61 -0
  102. dstf/cli/baseline_cmds.py +341 -0
  103. dstf/cli/common.py +231 -0
  104. dstf/cli/diag_cmds.py +350 -0
  105. dstf/cli/main.py +561 -0
  106. dstf/cli/review.py +398 -0
  107. dstf/cli/run_cmds.py +589 -0
  108. dstf/cli/spec_cmds.py +123 -0
  109. dstf/comparison/__init__.py +13 -0
  110. dstf/comparison/algorithms.py +2127 -0
  111. dstf/comparison/builtins.py +291 -0
  112. dstf/comparison/comparator.py +1189 -0
  113. dstf/comparison/geometry.py +164 -0
  114. dstf/comparison/handles.py +1194 -0
  115. dstf/comparison/metric_tree.py +332 -0
  116. dstf/comparison/mode_schema.py +176 -0
  117. dstf/comparison/modes.py +788 -0
  118. dstf/comparison/registry.py +230 -0
  119. dstf/comparison/seeds.py +281 -0
  120. dstf/comparison/sides.py +158 -0
  121. dstf/comparison/snap.py +62 -0
  122. dstf/comparison/strict_keys.py +246 -0
  123. dstf/comparison/summaries.py +354 -0
  124. dstf/comparison/tree_eval.py +868 -0
  125. dstf/comparison/tree_spec.py +678 -0
  126. dstf/comparison/types.py +194 -0
  127. dstf/comparison/validator.py +232 -0
  128. dstf/config.py +815 -0
  129. dstf/discovery/__init__.py +0 -0
  130. dstf/discovery/json_recognizer.py +702 -0
  131. dstf/discovery/library_state.py +248 -0
  132. dstf/discovery/mo_parser.py +794 -0
  133. dstf/discovery/patch_apply.py +308 -0
  134. dstf/discovery/patch_envelope.py +63 -0
  135. dstf/discovery/plots.py +318 -0
  136. dstf/discovery/recognizer.py +103 -0
  137. dstf/discovery/scan_cache.py +154 -0
  138. dstf/discovery/spec_parser.py +538 -0
  139. dstf/discovery/spec_shape.py +122 -0
  140. dstf/discovery/spec_update.py +493 -0
  141. dstf/discovery/test_registry.py +488 -0
  142. dstf/discovery/unit_tests.py +95 -0
  143. dstf/figures/__init__.py +14 -0
  144. dstf/figures/assemble.py +48 -0
  145. dstf/figures/decimate.py +150 -0
  146. dstf/figures/families.py +289 -0
  147. dstf/figures/palette.py +170 -0
  148. dstf/figures/plotly.py +1413 -0
  149. dstf/figures/view.py +233 -0
  150. dstf/pipeline.py +435 -0
  151. dstf/plugins.py +104 -0
  152. dstf/reporting/__init__.py +0 -0
  153. dstf/reporting/console_report.py +187 -0
  154. dstf/reporting/context_builder.py +979 -0
  155. dstf/reporting/dashboard_render.py +534 -0
  156. dstf/reporting/junit_report.py +192 -0
  157. dstf/reporting/overlay_loader.py +708 -0
  158. dstf/reporting/report_suite.py +1059 -0
  159. dstf/reporting/rows.py +458 -0
  160. dstf/reporting/schema_export.py +413 -0
  161. dstf/reporting/stats_rows.py +321 -0
  162. dstf/reporting/status.py +388 -0
  163. dstf/reporting/templates/axes.js +61 -0
  164. dstf/reporting/templates/dashboard.html +284 -0
  165. dstf/reporting/templates/dashboard.js +604 -0
  166. dstf/reporting/templates/filters.js +100 -0
  167. dstf/reporting/templates/interactive.html +420 -0
  168. dstf/reporting/templates/report.js +116 -0
  169. dstf/reporting/templates/vendor/plotly.min.js +8 -0
  170. dstf/reporting/theme.py +324 -0
  171. dstf/reporting/tree_rows.py +73 -0
  172. dstf/reporting/verdicts.py +124 -0
  173. dstf/storage/__init__.py +0 -0
  174. dstf/storage/accept.py +216 -0
  175. dstf/storage/baseline.py +43 -0
  176. dstf/storage/batch_manifest.py +204 -0
  177. dstf/storage/ref_index.py +295 -0
  178. dstf/storage/reference_store.py +661 -0
  179. dstf/storage/roles.py +576 -0
  180. dstf/storage/run_stats.py +85 -0
  181. dynamic_systems_testing_framework-1.0.1.dist-info/METADATA +266 -0
  182. dynamic_systems_testing_framework-1.0.1.dist-info/RECORD +185 -0
  183. dynamic_systems_testing_framework-1.0.1.dist-info/WHEEL +4 -0
  184. dynamic_systems_testing_framework-1.0.1.dist-info/entry_points.txt +2 -0
  185. dynamic_systems_testing_framework-1.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,179 @@
1
+ """The tests as rows, grouped by package or flat (brief §3.2; DESIGN.md
2
+ "Tables"): the Tests drawer's list (the Suite page's is a table,
3
+ `suite_grid.py`). A row's verdict and its unsaved edits repaint in place;
4
+ the list is rebuilt only when the test set, the order or the layout
5
+ moves."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Callable, Sequence
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING
12
+
13
+ from nicegui import ui
14
+ from nicegui.elements.mixins.text_element import TextElement
15
+
16
+ from ...reporting.rows import unsaved_text
17
+ from ...reporting.status import SCORING_CLASS, SCORING_FAILED, classify_with_class
18
+ from ...reporting.theme import STATUS
19
+ from ..spec_document import edit_counts
20
+
21
+ if TYPE_CHECKING:
22
+ from .shell import Shell
23
+
24
+
25
+ @dataclass
26
+ class Row:
27
+ """One test's row: the elements a click or a rescore updates in place —
28
+ the whole list is never resent (roadmap §2 "the row model")."""
29
+
30
+ model_id: str
31
+ group: str
32
+ container: ui.row
33
+ name: ui.button
34
+ badge: ui.badge
35
+ #: ``N unsaved`` while the test has staged edits, hidden otherwise
36
+ edits: ui.label
37
+ verdict: str = "" # the classifier's word, "" while unscored
38
+ pending: int = 0 # the test's staged edits
39
+
40
+
41
+ def _package(model_id: str) -> str:
42
+ return model_id.rpartition(".")[0]
43
+
44
+
45
+ def _name(model_id: str, short: bool) -> str:
46
+ return model_id.rsplit(".", 1)[-1] if short else model_id
47
+
48
+
49
+ def _full_name(button: ui.button, model_id: str) -> None:
50
+ """A Full name, cut at its START (the stylesheet's `.dstf-cut-start`):
51
+ TRANSFORM's ids share long package prefixes, and cut at the end two of
52
+ them read alike. Drawn in place of Quasar's label, in the `.block` span
53
+ Quasar draws one in, the id in a `<bdi>`: the span is right-to-left,
54
+ and a bare `direction: rtl` moved a leading or trailing `_` or `.` to
55
+ the other end (Chromium); inside the isolate the id keeps its order.
56
+ Not isolates from `::before` / `::after`: those entered the button's
57
+ accessible name."""
58
+ with button, ui.element("span").classes("block dstf-cut-start"):
59
+ TextElement(tag="bdi", text=model_id)
60
+
61
+
62
+ class TestList:
63
+ """The list's rows and group labels. ``current`` gets
64
+ ``dstf-row-current`` on its row; ``marker`` prefixes every marker the
65
+ list sets: ``tests``, ``group:<g>``, ``<id>``."""
66
+
67
+ def __init__(
68
+ self,
69
+ shell: Shell,
70
+ *,
71
+ on_open: Callable[[str], None],
72
+ current: str | None = None,
73
+ marker: str,
74
+ ) -> None:
75
+ self.shell = shell
76
+ self.on_open = on_open
77
+ self.current = current
78
+ self.marker = marker
79
+ self.rows: dict[str, Row] = {}
80
+ self.groups: dict[str, ui.label] = {}
81
+ self.column = (
82
+ ui.column().classes("dstf-tests w-full gap-0").mark(f"{marker}tests")
83
+ )
84
+
85
+ def rebuild(self, ids: Sequence[str], *, grouped: bool, short: bool) -> None:
86
+ """The rows from scratch, in ``ids`` order — by package first when
87
+ ``grouped``, each group under its label."""
88
+ self.column.clear()
89
+ self.rows, self.groups = {}, {}
90
+ position = {m: i for i, m in enumerate(ids)}
91
+ order = (
92
+ sorted(ids, key=lambda m: (_package(m), position[m])) if grouped else ids
93
+ )
94
+ with self.column:
95
+ current: str | None = None
96
+ for model_id in order:
97
+ group = _package(model_id)
98
+ if grouped and group != current:
99
+ current = group
100
+ self.groups[group] = (
101
+ ui.label(group or "(top level)")
102
+ .classes("dstf-group")
103
+ .mark(f"{self.marker}group:{group}")
104
+ )
105
+ with ui.row().classes(
106
+ "dstf-row items-center no-wrap gap-2 w-full"
107
+ ) as container:
108
+ name = (
109
+ ui.button(
110
+ _name(model_id, short) if short else "",
111
+ on_click=lambda m=model_id: self.on_open(m),
112
+ )
113
+ .props("flat dense no-caps align=left")
114
+ .classes("dstf-name")
115
+ .mark(f"{self.marker}{model_id}")
116
+ )
117
+ # Cut with an ellipsis in a narrow drawer: the whole id
118
+ # is its title (on the props dict: a props string
119
+ # breaks on a quote).
120
+ name.props["title"] = model_id
121
+ if not short:
122
+ _full_name(name, model_id)
123
+ # `color=None`: a Quasar badge otherwise carries
124
+ # `bg-primary`, whose `!important` beats the inline
125
+ # status fill and paints every verdict blue (the review
126
+ # of Task 4 of the 2026-09-20 Test page plan, in
127
+ # Chromium).
128
+ edits = (
129
+ ui.label()
130
+ .classes("dstf-pill dstf-pill-warn dstf-pill-named")
131
+ .mark(f"{self.marker}edits:{model_id}")
132
+ )
133
+ badge = ui.badge("", color=None).classes("dstf-badge")
134
+ if model_id == self.current:
135
+ container.classes(add="dstf-row-current")
136
+ self.rows[model_id] = Row(
137
+ model_id, group, container, name, badge, edits
138
+ )
139
+ self.paint(model_id)
140
+
141
+ def paint(self, model_id: str) -> None:
142
+ """One row's verdict: the badge's word and fill, the row's tint.
143
+ `…` while unscored; `scoring failed` for a model the open announced
144
+ with `error=True` (Task 1 of the 2026-09-16 Suite page plan). And
145
+ its staged edits: ``N unsaved``, as the Suite's Edits column."""
146
+ row = self.rows.get(model_id)
147
+ if row is None:
148
+ return
149
+ comp = self.shell.session.comparisons.get(model_id)
150
+ if comp is not None:
151
+ verdict, css = classify_with_class(comp)
152
+ elif model_id in self.shell.load.failed_models:
153
+ verdict, css = SCORING_FAILED, "sim-fail"
154
+ else:
155
+ verdict, css = "", SCORING_CLASS
156
+ row.verdict = verdict
157
+ row.pending = edit_counts(self.shell.session.spec.pending)[model_id]
158
+ row.edits.set_text(unsaved_text(row.pending))
159
+ row.edits.set_visibility(row.pending > 0)
160
+ row.badge.set_text(verdict or "…")
161
+ row.badge.style(f"background-color: var(--dstf-{css}-fill)")
162
+ # Add and remove, never replace: NiceGUI hides an element by appending
163
+ # a `hidden` class, and a replaced list dropped it — every filtered-out
164
+ # row came back on a rescore (the review's probe of Task 6 of the
165
+ # 2026-09-16 Suite page plan).
166
+ row.container.classes(
167
+ remove=" ".join(f"dstf-tint-{s}" for s in STATUS), add=f"dstf-tint-{css}"
168
+ )
169
+
170
+ def filter(self, keep: Callable[[Row], bool]) -> None:
171
+ """Visibility only — the rows stay. A group's label shows while any
172
+ of its rows does."""
173
+ shown_by_group: dict[str, bool] = {}
174
+ for row in self.rows.values():
175
+ shown = keep(row)
176
+ row.container.set_visibility(shown)
177
+ shown_by_group[row.group] = shown_by_group.get(row.group, False) or shown
178
+ for group, label in self.groups.items():
179
+ label.set_visibility(shown_by_group.get(group, False))