robotframework-dashboard 2.1.0__tar.gz → 2.3.0__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.
- {robotframework_dashboard-2.1.0/robotframework_dashboard.egg-info → robotframework_dashboard-2.3.0}/PKG-INFO +4 -3
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/README.md +5 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/arguments.py +91 -4
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/base.css +16 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/components.css +191 -72
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/dark.css +0 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/dashboard.py +7 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/database.py +146 -22
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/bootstrap.css +6 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/bootstrap.js +7 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chart.js +14 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/dependencies/chartjs-adapter-date-fns.js +1 -1
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chartjs-chart-boxplot.js +2 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chartjs-chart-matrix.js +8 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chartjs-plugin-datalabels.js +7 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/datatables.css +21 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/datatables.js +22 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/gridstack.css +1 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/gridstack.js +9 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/pako.js +2 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/dependencies.py +29 -25
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_api.js +21 -3
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_eventlisteners.js +0 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_information.js +1 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/common.js +133 -11
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/customsections.js +12 -86
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/eventlisteners.js +50 -62
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/filter.js +52 -157
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/all.js +14 -3
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/chart_factory.js +0 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/compare.js +2 -4
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/config_helpers.js +4 -5
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/keyword.js +84 -4
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/overview.js +95 -224
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/run.js +0 -3
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/suite.js +0 -4
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/tables.js +19 -6
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/test.js +3 -4
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/duration.js +0 -2
- robotframework_dashboard-2.3.0/robotframework_dashboard/js/graph_data/exceptions.js +94 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/failed.js +7 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/flaky.js +30 -49
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/graph_config.js +6 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/helpers.js +80 -10
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/messages.js +6 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/statistics.js +40 -21
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/stats.js +6 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/tooltip_helpers.js +0 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/information.js +1 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/layout.js +110 -28
- robotframework_dashboard-2.3.0/robotframework_dashboard/js/linkwidgets.js +181 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/localstorage.js +6 -10
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/log.js +8 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/menu.js +10 -40
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/statwidgets.js +154 -84
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/theme.js +36 -10
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/chartconfig.js +5 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/data.js +3 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/globals.js +2 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/graphmetadata.js +43 -7
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/information.js +18 -5
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/settings.js +3 -3
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/statwidgetdefs.js +3 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/svg.js +16 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/licenses/THIRD_PARTY_LICENSES.txt +65 -56
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/main.py +1 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/processors.py +123 -12
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/queries.py +9 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/robotdashboard.py +18 -1
- robotframework_dashboard-2.3.0/robotframework_dashboard/robotdashboardlistener.py +321 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/server.py +76 -12
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/templates/admin.html +2 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/templates/dashboard.html +153 -29
- robotframework_dashboard-2.3.0/robotframework_dashboard/version.py +1 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0/robotframework_dashboard.egg-info}/PKG-INFO +4 -3
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/SOURCES.txt +3 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/setup.py +4 -3
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/bootstrap.css +0 -6
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/bootstrap.js +0 -7
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/chart.js +0 -20
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/chartjs-chart-boxplot.js +0 -2
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/chartjs-chart-matrix.js +0 -8
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/chartjs-plugin-datalabels.js +0 -7
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/datatables.css +0 -18
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/datatables.js +0 -25
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/gridstack.css +0 -1
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/gridstack.js +0 -9
- robotframework_dashboard-2.1.0/robotframework_dashboard/dependencies/pako.js +0 -2
- robotframework_dashboard-2.1.0/robotframework_dashboard/version.py +0 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/LICENSE +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/MANIFEST.in +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/pyproject.toml +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/__init__.py +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/abstractdb.py +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/colors.css +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_common.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_main.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_theme.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/database.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/donut.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/duration_deviation.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/heatmap.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/time_consuming.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/main.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/graphs.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/dependency_links.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/entry_points.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/not-zip-safe +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/requires.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/top_level.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-dashboard
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Output processor and dashboard generator for Robot Framework output files
|
|
5
5
|
Home-page: https://github.com/marketsquare/robotframework-dashboard
|
|
6
6
|
Author: Tim de Groot
|
|
@@ -90,8 +90,9 @@ For all available CLI options see the [Basic CLI docs](https://marketsquare.gith
|
|
|
90
90
|
- 🛠️ **Custom Database Classes** - Extend or replace the database processor for custom backends.
|
|
91
91
|
- 🖥️ **Server Mode** - Host your dashboard for multi-user access and automatic updates.
|
|
92
92
|
- 🎧 **Listener Integration** - Automatically updates dashboard after every test run.
|
|
93
|
-
- 📝 **Message Config Support** - Group similar test failures using regex-based patterns.
|
|
94
|
-
-
|
|
93
|
+
- 📝 **Message Config Support** - Group similar test failures using regex-based patterns.
|
|
94
|
+
- 🚨 **Exception Tracking** - Track exception messages caught by TRY/EXCEPT blocks across runs, with a dedicated graph and table.
|
|
95
|
+
- ⚙️ **Configurable Defaults** - Preload dashboard settings via JSON for consistent appearance.
|
|
95
96
|
|
|
96
97
|
…and many more advanced features to help you visualize, analyze, and manage your Robot Framework test results with ease!
|
|
97
98
|
|
|
@@ -57,10 +57,12 @@ For all available CLI options see the [Basic CLI docs](https://marketsquare.gith
|
|
|
57
57
|
- 🔎 **Global Filters** - Filter runs by name, tags, date, metadata, or quantity.
|
|
58
58
|
- ⚖️ **Comparison Mode** - Compare up to 4 runs side by side with visual statistics.
|
|
59
59
|
- 🔗 **Automatic Log Linking** - Open Robot Framework logs directly from the dashboard.
|
|
60
|
+
- 🔁 **Rerun History** - Merged `--rerunfailed` outputs keep the status of every attempt per test.
|
|
60
61
|
- 🛠️ **Custom Database Classes** - Extend or replace the database processor for custom backends.
|
|
61
62
|
- 🖥️ **Server Mode** - Host your dashboard for multi-user access and automatic updates.
|
|
62
63
|
- 🎧 **Listener Integration** - Automatically updates dashboard after every test run.
|
|
63
64
|
- 📝 **Message Config Support** - Group similar test failures using regex-based patterns.
|
|
65
|
+
- 🚨 **Exception Tracking** - Track exception messages caught by TRY/EXCEPT blocks across runs, with a dedicated graph and table.
|
|
64
66
|
- ⚙️ **Configurable Defaults** - Preload dashboard settings via JSON for consistent appearance.
|
|
65
67
|
|
|
66
68
|
…and many more advanced features to help you visualize, analyze, and manage your Robot Framework test results with ease!
|
|
@@ -78,10 +80,12 @@ For detailed usage instructions, advanced examples, and full documentation, visi
|
|
|
78
80
|
- 🔍 [**Filtering**](https://marketsquare.github.io/robotframework-dashboard/filtering.html) - Apply filters to analyze trends in your test data and highlight specific tags, amounts or datetime ranges.
|
|
79
81
|
- 🎨 [**Customization**](https://marketsquare.github.io/robotframework-dashboard/customization.html) - Customize dashboard sections, graph layouts, and visualizations to suit your workflow.
|
|
80
82
|
- ⚙️ [**Settings**](https://marketsquare.github.io/robotframework-dashboard/settings.html) - Configure dashboard preferences including themes, default views, graph options, and save your settings for consistent team-wide use.
|
|
83
|
+
- 📐 [**Architecture**](https://marketsquare.github.io/robotframework-dashboard/architecture.html) - See how robotdashboard is structured internally, from CLI invocation to a rendered dashboard, in both Regular Mode and Server Mode.
|
|
81
84
|
- 🖥️ [**Dashboard Server**](https://marketsquare.github.io/robotframework-dashboard/dashboard-server.html) - Host the dashboard for multi-user access, programmatic updates, and remote server integration.
|
|
82
85
|
- 🗄️ [**Custom Database Class**](https://marketsquare.github.io/robotframework-dashboard/custom-database-class.html) - Extend or replace the default database backend to suit your storage needs, including SQLite, MySQL, or custom implementations.
|
|
83
86
|
- 🔔 [**Listener Integration**](https://marketsquare.github.io/robotframework-dashboard/listener-integration.html) - Use a listener to automatically push test results to the dashboard for every executed run, integrating seamlessly into CI/CD pipelines.
|
|
84
87
|
- 📂 [**Log Linking**](https://marketsquare.github.io/robotframework-dashboard/log-linking.html) - Enable clickable log navigation from dashboard graphs, covering file naming conventions, local and server usage, and remote log uploads.
|
|
88
|
+
- 🔁 [**Reruns**](https://marketsquare.github.io/robotframework-dashboard/reruns.html) - Feed merged `--rerunfailed` outputs to the dashboard and see the attempt history of every re-executed test.
|
|
85
89
|
- 📈 [**Performance**](https://marketsquare.github.io/robotframework-dashboard/performance.html) - Performance results across different workload sizes, guidance on scaling to hundreds of runs, and tips for large result sets.
|
|
86
90
|
|
|
87
91
|
|
|
@@ -122,4 +126,4 @@ This project is licensed under the MIT License.
|
|
|
122
126
|
## 🎬 RoboCon Talk
|
|
123
127
|
See also my 2025 [RoboCon](https://robocon.io) talk, where I explain robotdashboard and its use cases. Note that the UI has improved significantly since then.
|
|
124
128
|
|
|
125
|
-
[](https://www.youtube.com/watch?v=olf1_pd9YfM)
|
|
129
|
+
[](https://www.youtube.com/watch?v=olf1_pd9YfM)
|
|
@@ -4,8 +4,16 @@ from sys import exit
|
|
|
4
4
|
from re import split
|
|
5
5
|
from os import getcwd
|
|
6
6
|
from os.path import join, exists
|
|
7
|
+
from typing import NamedTuple
|
|
7
8
|
from .version import __version__
|
|
8
9
|
|
|
10
|
+
_VALID_LOG_TYPES = {"run", "suite", "test", "keyword", "all"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LogRemovedConfig(NamedTuple):
|
|
14
|
+
types: list
|
|
15
|
+
path: str
|
|
16
|
+
|
|
9
17
|
|
|
10
18
|
class dotdict(dict):
|
|
11
19
|
"""dot.notation access to dictionary attributes"""
|
|
@@ -61,6 +69,46 @@ class ArgumentParser:
|
|
|
61
69
|
print(" ERROR: Mixing --projectversion and version_ tags not supported")
|
|
62
70
|
exit(2)
|
|
63
71
|
|
|
72
|
+
def _process_remove_runs(self, parts):
|
|
73
|
+
"""Process and validate the comma-separated --removeruns parts.
|
|
74
|
+
|
|
75
|
+
Detects a scoped-retention combination ('limit' + 'tags') and rewrites
|
|
76
|
+
it into the internal 'limit=N;tag=...' form so the limit acts on the
|
|
77
|
+
tagged subset instead of running as two independent operations. This
|
|
78
|
+
lets users write the combo with the regular comma separator (e.g.
|
|
79
|
+
'-r "limit=10,tag=nightly"') instead of ';'.
|
|
80
|
+
|
|
81
|
+
Only 'limit' can be scoped by tags; 'age' + 'tags' run as two
|
|
82
|
+
independent operations.
|
|
83
|
+
|
|
84
|
+
Rules:
|
|
85
|
+
- No tags, or tags without a 'limit' partner -> no combination, every
|
|
86
|
+
part stays independent (unchanged behavior, including 'age'+'tags').
|
|
87
|
+
- Only one 'limit' may be combined with tags (error otherwise).
|
|
88
|
+
"""
|
|
89
|
+
limit_parts = [p for p in parts if p.startswith("limit=")]
|
|
90
|
+
tag_parts = [p for p in parts if p.startswith("tag=")]
|
|
91
|
+
other_parts = [
|
|
92
|
+
p for p in parts if not (p.startswith("limit=") or p.startswith("tag="))
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
if not tag_parts or not limit_parts:
|
|
96
|
+
return parts
|
|
97
|
+
if len(limit_parts) > 1:
|
|
98
|
+
print(" ERROR: Only one 'limit' may be combined with 'tag(s)'.")
|
|
99
|
+
exit(3)
|
|
100
|
+
|
|
101
|
+
tag_values = [p.replace("tag=", "") for p in tag_parts]
|
|
102
|
+
limit_value = limit_parts[0].replace("limit=", "")
|
|
103
|
+
combo = limit_parts[0] + "".join(f";tag={value}" for value in tag_values)
|
|
104
|
+
print(
|
|
105
|
+
f" INFO: Combining 'limit' with 'tag(s)' -> keeping the {limit_value} most recent "
|
|
106
|
+
f"run(s) tagged with [{', '.join(tag_values)}] and removing older matching runs "
|
|
107
|
+
f"(runs without these tags are left untouched)."
|
|
108
|
+
)
|
|
109
|
+
# other independent options (index/run_start/alias) run before the combo
|
|
110
|
+
return other_parts + [combo]
|
|
111
|
+
|
|
64
112
|
def _check_argument_warnings(self, arguments, outputs, outputfolderpaths, use_logs, generate_dashboard, no_autoupdate, offline_dependencies):
|
|
65
113
|
"""Checks for argument combinations that are valid but likely unintended and prints warnings"""
|
|
66
114
|
no_outputs = not outputs and not outputfolderpaths
|
|
@@ -267,6 +315,7 @@ class ArgumentParser:
|
|
|
267
315
|
" • '-r run_start=2024-07-30 15:27:20.184407' -> remove specified run\n"
|
|
268
316
|
" • '-r alias=some_alias,tag=prod'\n"
|
|
269
317
|
" • '-r limit=10' -> keep only the 10 most recent runs\n"
|
|
318
|
+
" • '-r limit=10,tag=nightly' -> keep 10 newest 'nightly' runs, leave others\n"
|
|
270
319
|
" • '-r age=10d' -> remove runs older than 10 days\n"
|
|
271
320
|
" • '-r age=-10d' -> remove runs younger than 10 days\n"
|
|
272
321
|
" • (y)ear/(d)ay/(h)our/(m)inute/(s)econd supported\n"
|
|
@@ -275,6 +324,24 @@ class ArgumentParser:
|
|
|
275
324
|
nargs="*",
|
|
276
325
|
default=None,
|
|
277
326
|
)
|
|
327
|
+
db_group.add_argument(
|
|
328
|
+
"--logremoved",
|
|
329
|
+
metavar="TYPES_PATH",
|
|
330
|
+
help=(
|
|
331
|
+
"Log (append if file exists) run data to jsonl before removing from the db\n"
|
|
332
|
+
" • Supply what types to log (default=all) and (optional) a path to the desired .jsonl\n"
|
|
333
|
+
"Examples:\n"
|
|
334
|
+
" • '--logremoved \"/tmp/removed_runs.jsonl\"'\n"
|
|
335
|
+
" • '--logremoved \"run:suite\"'\n"
|
|
336
|
+
" • '--logremoved \"all:/tmp/removed_runs.jsonl\"'\n"
|
|
337
|
+
),
|
|
338
|
+
action="store",
|
|
339
|
+
type=str,
|
|
340
|
+
nargs="?",
|
|
341
|
+
const="all",
|
|
342
|
+
default=None,
|
|
343
|
+
dest="logremoved",
|
|
344
|
+
)
|
|
278
345
|
db_group.add_argument(
|
|
279
346
|
"-c",
|
|
280
347
|
"--databaseclass",
|
|
@@ -505,11 +572,13 @@ class ArgumentParser:
|
|
|
505
572
|
# handles the processing of --removeruns
|
|
506
573
|
remove_runs = None
|
|
507
574
|
if arguments.removeruns:
|
|
508
|
-
|
|
575
|
+
raw_parts = []
|
|
509
576
|
for runs in arguments.removeruns:
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
577
|
+
for part in str(runs[0]).split(","):
|
|
578
|
+
part = part.strip()
|
|
579
|
+
if part:
|
|
580
|
+
raw_parts.append(part)
|
|
581
|
+
remove_runs = self._process_remove_runs(raw_parts)
|
|
513
582
|
|
|
514
583
|
# handles the boolean handling of relevant arguments
|
|
515
584
|
generate_dashboard = self._normalize_bool(
|
|
@@ -605,6 +674,23 @@ class ArgumentParser:
|
|
|
605
674
|
ssl_certfile = arguments.ssl_certfile
|
|
606
675
|
ssl_keyfile = arguments.ssl_keyfile
|
|
607
676
|
|
|
677
|
+
# handles --logremoved: "type1:type2:path" or "path" or "type1:type2"
|
|
678
|
+
log_removed = None
|
|
679
|
+
if arguments.logremoved:
|
|
680
|
+
parts = arguments.logremoved.split(":")
|
|
681
|
+
types, remaining = [], list(parts)
|
|
682
|
+
# grab element types, leave path, for windows drive letter X: parse
|
|
683
|
+
for part in parts:
|
|
684
|
+
if part in _VALID_LOG_TYPES:
|
|
685
|
+
types.append(part)
|
|
686
|
+
remaining.pop(0)
|
|
687
|
+
else:
|
|
688
|
+
break
|
|
689
|
+
if not types:
|
|
690
|
+
types = ["all"]
|
|
691
|
+
path = ":".join(remaining) if remaining else "robot_removed_runs.jsonl"
|
|
692
|
+
log_removed = LogRemovedConfig(types=types, path=path)
|
|
693
|
+
|
|
608
694
|
# validates argument combinations
|
|
609
695
|
self._check_argument_errors(arguments, outputs, outputfolderpaths, force_json_config, database_class)
|
|
610
696
|
self._check_argument_warnings(arguments, outputs, outputfolderpaths, use_logs, generate_dashboard, no_autoupdate, offline_dependencies)
|
|
@@ -640,5 +726,6 @@ class ArgumentParser:
|
|
|
640
726
|
"ssl_certfile": ssl_certfile,
|
|
641
727
|
"ssl_keyfile": ssl_keyfile,
|
|
642
728
|
"log_url": arguments.logurl,
|
|
729
|
+
"log_removed": log_removed,
|
|
643
730
|
}
|
|
644
731
|
return dotdict(provided_args)
|
|
@@ -124,6 +124,14 @@ body {
|
|
|
124
124
|
.boxplot-graph:hover svg,
|
|
125
125
|
.shown-graph:hover svg,
|
|
126
126
|
.hidden-graph:hover svg,
|
|
127
|
+
.move-to-first-graph:hover svg,
|
|
128
|
+
.move-to-last-graph:hover svg,
|
|
129
|
+
.add-stat-widget-header:hover svg,
|
|
130
|
+
.add-link-widget-header:hover svg,
|
|
131
|
+
.add-section-header:hover svg,
|
|
132
|
+
.delete-custom-stat-widget:hover svg,
|
|
133
|
+
.delete-custom-link-widget:hover svg,
|
|
134
|
+
.delete-custom-section:hover svg,
|
|
127
135
|
.shown-section:hover svg,
|
|
128
136
|
.hidden-section:hover svg,
|
|
129
137
|
.collapse-icon:hover svg,
|
|
@@ -188,6 +196,14 @@ body.lock-scroll {
|
|
|
188
196
|
.boxplot-graph,
|
|
189
197
|
.shown-graph,
|
|
190
198
|
.hidden-graph,
|
|
199
|
+
.move-to-first-graph,
|
|
200
|
+
.move-to-last-graph,
|
|
201
|
+
.add-stat-widget-header,
|
|
202
|
+
.add-link-widget-header,
|
|
203
|
+
.add-section-header,
|
|
204
|
+
.delete-custom-stat-widget,
|
|
205
|
+
.delete-custom-link-widget,
|
|
206
|
+
.delete-custom-section,
|
|
191
207
|
.shown-section,
|
|
192
208
|
.hidden-section,
|
|
193
209
|
.move-up-table,
|
|
@@ -197,7 +213,6 @@ body.lock-scroll {
|
|
|
197
213
|
cursor: pointer;
|
|
198
214
|
}
|
|
199
215
|
|
|
200
|
-
/* ── Info label + small icon (tooltip on label hover) ── */
|
|
201
216
|
.info-label {
|
|
202
217
|
display: inline-flex !important;
|
|
203
218
|
align-items: center;
|
|
@@ -267,7 +282,6 @@ body.lock-scroll {
|
|
|
267
282
|
display: block;
|
|
268
283
|
}
|
|
269
284
|
|
|
270
|
-
/* ── Sidebar (off-canvas menu for overflowed items) ── */
|
|
271
285
|
.sidenav-backdrop {
|
|
272
286
|
position: fixed;
|
|
273
287
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
flex-wrap: wrap;
|
|
38
38
|
align-items: center;
|
|
39
39
|
column-gap: 0.75rem;
|
|
40
|
-
row-gap:
|
|
40
|
+
row-gap: 6px;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
.section-filters .filter-group {
|
|
@@ -115,6 +115,37 @@
|
|
|
115
115
|
display: block;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
.filter-search-wrapper {
|
|
119
|
+
position: relative;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.filter-search-wrapper .form-control {
|
|
123
|
+
padding-right: 1.5rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.filter-search-clear {
|
|
127
|
+
position: absolute;
|
|
128
|
+
top: 0;
|
|
129
|
+
right: 0;
|
|
130
|
+
bottom: 0;
|
|
131
|
+
width: 1.5rem;
|
|
132
|
+
display: flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
justify-content: center;
|
|
135
|
+
padding: 0 0 0.15rem 0;
|
|
136
|
+
border: none;
|
|
137
|
+
background: none;
|
|
138
|
+
color: inherit;
|
|
139
|
+
opacity: 0.6;
|
|
140
|
+
font-size: 1.4rem;
|
|
141
|
+
line-height: 1;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.filter-search-clear:hover {
|
|
146
|
+
opacity: 1;
|
|
147
|
+
}
|
|
148
|
+
|
|
118
149
|
.filter-profile-bar {
|
|
119
150
|
border-bottom: 1px solid var(--bs-border-color);
|
|
120
151
|
}
|
|
@@ -152,80 +183,44 @@
|
|
|
152
183
|
align-items: center;
|
|
153
184
|
}
|
|
154
185
|
|
|
155
|
-
/*
|
|
156
|
-
.
|
|
157
|
-
|
|
158
|
-
display: flex;
|
|
159
|
-
align-items: center;
|
|
160
|
-
justify-content: center;
|
|
161
|
-
border: 2px dashed var(--bs-border-color, rgba(128,128,128,0.4));
|
|
162
|
-
border-radius: 8px;
|
|
163
|
-
transition: border-color 0.15s, background-color 0.15s;
|
|
186
|
+
/* Color picker buttons inside the Add Stat Widget modal */
|
|
187
|
+
.stat-color-btn {
|
|
188
|
+
min-width: 60px;
|
|
164
189
|
}
|
|
165
190
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
191
|
+
/* "All" tab stat list inside the Add Stat Widget modal */
|
|
192
|
+
.add-all-widgets-list {
|
|
193
|
+
max-height: 60vh;
|
|
194
|
+
overflow-y: auto;
|
|
169
195
|
}
|
|
170
196
|
|
|
171
|
-
.add-
|
|
197
|
+
.add-all-widgets-row {
|
|
172
198
|
display: flex;
|
|
173
|
-
flex-direction: column;
|
|
174
199
|
align-items: center;
|
|
175
|
-
gap: 0.
|
|
176
|
-
|
|
177
|
-
user-select: none;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.add-stat-widget-plus {
|
|
181
|
-
font-size: 2rem;
|
|
182
|
-
font-weight: 300;
|
|
183
|
-
line-height: 1;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.add-stat-widget-tile-label {
|
|
187
|
-
font-size: 0.8rem;
|
|
188
|
-
font-weight: 500;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/* Color picker buttons inside the Add Stat Widget modal */
|
|
192
|
-
.stat-color-btn {
|
|
193
|
-
min-width: 60px;
|
|
200
|
+
gap: 0.5rem;
|
|
201
|
+
margin-bottom: 0.5rem;
|
|
194
202
|
}
|
|
195
203
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
cursor: pointer;
|
|
199
|
-
display: flex;
|
|
200
|
-
align-items: center;
|
|
201
|
-
justify-content: center;
|
|
202
|
-
border: 2px dashed var(--bs-border-color, rgba(128,128,128,0.4));
|
|
203
|
-
border-radius: 8px;
|
|
204
|
-
transition: border-color 0.15s, background-color 0.15s;
|
|
204
|
+
.add-all-widgets-row .form-check {
|
|
205
|
+
margin-bottom: 0;
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
.add-
|
|
208
|
-
|
|
209
|
-
background-color: rgba(128,128,128,0.08);
|
|
208
|
+
.add-all-widgets-row .add-all-widgets-title {
|
|
209
|
+
flex: 1 1 auto;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
.add-
|
|
213
|
-
|
|
214
|
-
flex-direction: row;
|
|
215
|
-
align-items: center;
|
|
216
|
-
gap: 0.5rem;
|
|
217
|
-
user-select: none;
|
|
212
|
+
.add-all-widgets-row .add-all-widgets-badge {
|
|
213
|
+
flex: 0 0 auto;
|
|
218
214
|
}
|
|
219
215
|
|
|
220
|
-
.add-
|
|
221
|
-
font-
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
.add-all-widgets-group-heading {
|
|
217
|
+
font-weight: 600;
|
|
218
|
+
margin-top: 0.5rem;
|
|
219
|
+
margin-bottom: 0.25rem;
|
|
224
220
|
}
|
|
225
221
|
|
|
226
|
-
.add-
|
|
227
|
-
|
|
228
|
-
font-weight: 500;
|
|
222
|
+
.add-all-widgets-group-heading:first-child {
|
|
223
|
+
margin-top: 0;
|
|
229
224
|
}
|
|
230
225
|
|
|
231
226
|
/* Custom section divider bar rendered inside the unified GridStack */
|
|
@@ -247,20 +242,16 @@
|
|
|
247
242
|
user-select: none;
|
|
248
243
|
}
|
|
249
244
|
|
|
250
|
-
/*
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
top: 5px;
|
|
254
|
-
right: 5px;
|
|
255
|
-
z-index: 1;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/* Delete button on custom stat widgets — pinned to top-right, out of flex flow */
|
|
259
|
-
.delete-custom-stat-widget {
|
|
245
|
+
/* Move to first/last + delete controls on custom widgets/sections — pinned to top-right,
|
|
246
|
+
out of flex flow, sized and spaced like the icons in graph headers */
|
|
247
|
+
.custom-widget-move-controls {
|
|
260
248
|
position: absolute;
|
|
261
249
|
top: 5px;
|
|
262
250
|
right: 5px;
|
|
263
251
|
z-index: 1;
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
gap: 8px;
|
|
264
255
|
}
|
|
265
256
|
|
|
266
257
|
/* Compact flex layout for custom stat widgets — works at any GridStack size */
|
|
@@ -284,7 +275,12 @@
|
|
|
284
275
|
overflow: hidden;
|
|
285
276
|
text-overflow: ellipsis;
|
|
286
277
|
white-space: nowrap;
|
|
287
|
-
max-width:
|
|
278
|
+
max-width: 100%;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
282
|
+
.stat-widget-inner:has(.custom-widget-move-controls) .stat-widget-title {
|
|
283
|
+
max-width: calc(100% - 96px);
|
|
288
284
|
}
|
|
289
285
|
|
|
290
286
|
/* When the item is very short (h=1 row ≈ 60px) switch to a side-by-side row */
|
|
@@ -304,14 +300,25 @@
|
|
|
304
300
|
.grid-stack-item[data-gs-h="1"] .stat-widget-title {
|
|
305
301
|
flex: 1 1 0;
|
|
306
302
|
text-align: left;
|
|
307
|
-
max-width: calc(100% - 40px); /*
|
|
303
|
+
max-width: calc(100% - 40px); /* leave room for the value */
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
307
|
+
.grid-stack-item[gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-widget-title,
|
|
308
|
+
.grid-stack-item[data-gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-widget-title {
|
|
309
|
+
max-width: calc(100% - 136px); /* value + move/delete controls */
|
|
308
310
|
}
|
|
309
311
|
|
|
310
312
|
.grid-stack-item[gs-h="1"] .stat-value,
|
|
311
313
|
.grid-stack-item[data-gs-h="1"] .stat-value {
|
|
312
314
|
flex-shrink: 0;
|
|
313
315
|
font-size: 1.1rem;
|
|
314
|
-
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
319
|
+
.grid-stack-item[gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-value,
|
|
320
|
+
.grid-stack-item[data-gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-value {
|
|
321
|
+
margin-right: 96px;
|
|
315
322
|
}
|
|
316
323
|
|
|
317
324
|
.grid-stack-item-content.blue-bg { background-color: rgba(13, 110, 253, 0.15); }
|
|
@@ -319,6 +326,88 @@
|
|
|
319
326
|
.grid-stack-item-content.red-bg { background-color: rgba(206, 62, 1, 0.2); }
|
|
320
327
|
.grid-stack-item-content.yellow-bg { background-color: rgba(254, 216, 79, 0.2); }
|
|
321
328
|
|
|
329
|
+
/* Inner layout — mirrors stat-widget-inner */
|
|
330
|
+
.link-widget-inner {
|
|
331
|
+
position: relative;
|
|
332
|
+
display: flex;
|
|
333
|
+
flex-direction: column;
|
|
334
|
+
justify-content: center;
|
|
335
|
+
align-items: center;
|
|
336
|
+
gap: 0.25rem;
|
|
337
|
+
height: 100%;
|
|
338
|
+
text-align: center;
|
|
339
|
+
overflow: hidden;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* Label — main large text, like .stat-value */
|
|
343
|
+
.link-widget-label {
|
|
344
|
+
margin: 0;
|
|
345
|
+
font-weight: 600;
|
|
346
|
+
font-size: 1.5rem;
|
|
347
|
+
line-height: 1.2;
|
|
348
|
+
overflow: hidden;
|
|
349
|
+
text-overflow: ellipsis;
|
|
350
|
+
white-space: nowrap;
|
|
351
|
+
max-width: 100%;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* URL preview — small muted text below the label */
|
|
355
|
+
.link-widget-url {
|
|
356
|
+
font-size: 0.72rem;
|
|
357
|
+
opacity: 0.55;
|
|
358
|
+
overflow: hidden;
|
|
359
|
+
text-overflow: ellipsis;
|
|
360
|
+
white-space: nowrap;
|
|
361
|
+
max-width: 100%;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
365
|
+
.link-widget-inner:has(.custom-widget-move-controls) .link-widget-label,
|
|
366
|
+
.link-widget-inner:has(.custom-widget-move-controls) .link-widget-url {
|
|
367
|
+
max-width: calc(100% - 96px);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* Clickable in non-edit mode */
|
|
371
|
+
.link-widget-clickable {
|
|
372
|
+
cursor: pointer;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* Same padding as stat widget tiles */
|
|
376
|
+
.grid-stack-item-content:has(.link-widget-inner) {
|
|
377
|
+
padding: 8px 12px;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* h=1 compact row — mirrors stat widget compact layout */
|
|
381
|
+
.grid-stack-item[gs-h="1"] .link-widget-inner,
|
|
382
|
+
.grid-stack-item[data-gs-h="1"] .link-widget-inner {
|
|
383
|
+
flex-direction: row;
|
|
384
|
+
justify-content: space-between;
|
|
385
|
+
align-items: center;
|
|
386
|
+
gap: 0.5rem;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.grid-stack-item[gs-h="1"] .link-widget-label,
|
|
390
|
+
.grid-stack-item[data-gs-h="1"] .link-widget-label {
|
|
391
|
+
flex: 1 1 0;
|
|
392
|
+
font-size: 1.1rem;
|
|
393
|
+
text-align: left;
|
|
394
|
+
max-width: calc(60% - 10px);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.grid-stack-item[gs-h="1"] .link-widget-url,
|
|
398
|
+
.grid-stack-item[data-gs-h="1"] .link-widget-url {
|
|
399
|
+
flex-shrink: 0;
|
|
400
|
+
font-size: 0.72rem;
|
|
401
|
+
text-align: right;
|
|
402
|
+
max-width: 40%;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
406
|
+
.grid-stack-item[gs-h="1"] .link-widget-inner:has(.custom-widget-move-controls) .link-widget-url {
|
|
407
|
+
max-width: calc(40% - 96px);
|
|
408
|
+
margin-right: 96px;
|
|
409
|
+
}
|
|
410
|
+
|
|
322
411
|
.fullscreen .stat-value {
|
|
323
412
|
font-size: 4rem;
|
|
324
413
|
}
|
|
@@ -361,6 +450,22 @@
|
|
|
361
450
|
height: 200px;
|
|
362
451
|
}
|
|
363
452
|
|
|
453
|
+
/* relative run time, pinned to the bottom right corner of a run card without
|
|
454
|
+
affecting the layout of the stats/duration columns next to the donut */
|
|
455
|
+
.overview-card .card-body {
|
|
456
|
+
position: relative;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.run-card-run-time {
|
|
460
|
+
position: absolute;
|
|
461
|
+
right: 1rem;
|
|
462
|
+
bottom: 0.55rem;
|
|
463
|
+
color: var(--color-text-muted);
|
|
464
|
+
cursor: help;
|
|
465
|
+
font-size: 0.85em;
|
|
466
|
+
white-space: nowrap;
|
|
467
|
+
}
|
|
468
|
+
|
|
364
469
|
.overview-card {
|
|
365
470
|
cursor: pointer;
|
|
366
471
|
min-width: 300px;
|
|
@@ -425,6 +530,13 @@
|
|
|
425
530
|
gap: 8px;
|
|
426
531
|
}
|
|
427
532
|
|
|
533
|
+
/* Icon row in section card headers (add stat/link widget, add section, move/show/hide section) */
|
|
534
|
+
.section-header-icons {
|
|
535
|
+
display: flex;
|
|
536
|
+
align-items: center;
|
|
537
|
+
gap: 8px;
|
|
538
|
+
}
|
|
539
|
+
|
|
428
540
|
.grid-stack-item-content .graph-body {
|
|
429
541
|
flex: 1 1 auto;
|
|
430
542
|
display: flex;
|
|
@@ -472,6 +584,13 @@ canvas {
|
|
|
472
584
|
filter: brightness(0.5);
|
|
473
585
|
}
|
|
474
586
|
|
|
587
|
+
/* Bootstrap >= 5.3.1 no longer spaces the modal header itself (only its .btn-close gets
|
|
588
|
+
margin-left: auto); the dashboard modals use their own button groups, so keep the
|
|
589
|
+
title left and the buttons right */
|
|
590
|
+
.modal-header {
|
|
591
|
+
justify-content: space-between;
|
|
592
|
+
}
|
|
593
|
+
|
|
475
594
|
#runTag {
|
|
476
595
|
max-height: 400px;
|
|
477
596
|
overflow: auto;
|
|
@@ -68,6 +68,10 @@ class DashboardGenerator:
|
|
|
68
68
|
'"placeholder_keywords"',
|
|
69
69
|
f'"{self._compress_and_encode(data["keywords"])}"',
|
|
70
70
|
)
|
|
71
|
+
dashboard_data = dashboard_data.replace(
|
|
72
|
+
'"placeholder_exceptions"',
|
|
73
|
+
f'"{self._compress_and_encode(data.get("exceptions", []))}"',
|
|
74
|
+
)
|
|
71
75
|
dashboard_data = dashboard_data.replace(
|
|
72
76
|
'"placeholder_amount"', str(quantity)
|
|
73
77
|
)
|
|
@@ -93,9 +97,11 @@ class DashboardGenerator:
|
|
|
93
97
|
f"Robot Framework Dashboard - {str(generation_datetime)[:-7]}",
|
|
94
98
|
)
|
|
95
99
|
if message_config:
|
|
100
|
+
# the placeholder sits inside a single-quoted JS string literal that is
|
|
101
|
+
# JSON.parse'd at runtime, so escape backslashes and single quotes
|
|
96
102
|
dashboard_data = dashboard_data.replace(
|
|
97
103
|
'"placeholder_message_config"',
|
|
98
|
-
|
|
104
|
+
dumps(message_config).replace("\\", "\\\\").replace("'", "\\'"),
|
|
99
105
|
)
|
|
100
106
|
if json_config:
|
|
101
107
|
dashboard_data = dashboard_data.replace(
|