robotframework-dashboard 2.1.0__tar.gz → 2.2.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.2.0}/PKG-INFO +1 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/README.md +2 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/arguments.py +92 -4
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/css/base.css +16 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/css/components.css +156 -71
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/css/dark.css +0 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/database.py +96 -21
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/common.js +114 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/customsections.js +12 -77
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/overview.js +10 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/statistics.js +9 -4
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/layout.js +105 -4
- robotframework_dashboard-2.2.0/robotframework_dashboard/js/linkwidgets.js +196 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/localstorage.js +4 -2
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/log.js +8 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/statwidgets.js +158 -63
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/theme.js +36 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/graphmetadata.js +26 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/information.js +9 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/svg.js +16 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/main.py +1 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/processors.py +9 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/queries.py +2 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/robotdashboard.py +18 -1
- robotframework_dashboard-2.2.0/robotframework_dashboard/robotdashboardlistener.py +327 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/server.py +80 -8
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/templates/admin.html +1 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/templates/dashboard.html +109 -12
- robotframework_dashboard-2.2.0/robotframework_dashboard/version.py +1 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0/robotframework_dashboard.egg-info}/PKG-INFO +1 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard.egg-info/SOURCES.txt +2 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/setup.py +1 -1
- robotframework_dashboard-2.1.0/robotframework_dashboard/version.py +0 -1
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/LICENSE +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/MANIFEST.in +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/pyproject.toml +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/__init__.py +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/abstractdb.py +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/css/colors.css +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dashboard.py +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/bootstrap.css +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/bootstrap.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/chart.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/chartjs-adapter-date-fns.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/chartjs-chart-boxplot.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/chartjs-chart-matrix.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/chartjs-plugin-datalabels.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/datatables.css +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/datatables.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/gridstack.css +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/gridstack.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies/pako.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/dependencies.py +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/admin_page/admin_api.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/admin_page/admin_common.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/admin_page/admin_eventlisteners.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/admin_page/admin_information.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/admin_page/admin_main.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/admin_page/admin_theme.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/database.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/eventlisteners.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/filter.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/all.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/chart_factory.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/compare.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/config_helpers.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/keyword.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/run.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/suite.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/tables.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_creation/test.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/donut.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/duration.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/duration_deviation.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/failed.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/flaky.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/graph_config.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/heatmap.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/helpers.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/messages.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/stats.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/time_consuming.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/graph_data/tooltip_helpers.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/information.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/main.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/menu.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/chartconfig.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/data.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/globals.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/graphs.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/settings.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/js/variables/statwidgetdefs.js +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard/licenses/THIRD_PARTY_LICENSES.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard.egg-info/dependency_links.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard.egg-info/entry_points.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard.egg-info/not-zip-safe +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard.egg-info/requires.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/robotframework_dashboard.egg-info/top_level.txt +0 -0
- {robotframework_dashboard-2.1.0 → robotframework_dashboard-2.2.0}/setup.cfg +0 -0
|
@@ -78,6 +78,7 @@ For detailed usage instructions, advanced examples, and full documentation, visi
|
|
|
78
78
|
- 🔍 [**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
79
|
- 🎨 [**Customization**](https://marketsquare.github.io/robotframework-dashboard/customization.html) - Customize dashboard sections, graph layouts, and visualizations to suit your workflow.
|
|
80
80
|
- ⚙️ [**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.
|
|
81
|
+
- 📐 [**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
82
|
- 🖥️ [**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
83
|
- 🗄️ [**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
84
|
- 🔔 [**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.
|
|
@@ -122,4 +123,4 @@ This project is licensed under the MIT License.
|
|
|
122
123
|
## 🎬 RoboCon Talk
|
|
123
124
|
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
125
|
|
|
125
|
-
[](https://www.youtube.com/watch?v=olf1_pd9YfM)
|
|
126
|
+
[](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,47 @@ 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
|
+
Scope note (matches issue #309): only 'limit' can be scoped by tags.
|
|
82
|
+
'age' + 'tags' is intentionally NOT supported here — combining them
|
|
83
|
+
just runs as two independent operations, same as on main.
|
|
84
|
+
|
|
85
|
+
Rules:
|
|
86
|
+
- No tags, or tags without a 'limit' partner -> no combination, every
|
|
87
|
+
part stays independent (unchanged behavior, including 'age'+'tags').
|
|
88
|
+
- Only one 'limit' may be combined with tags (error otherwise).
|
|
89
|
+
"""
|
|
90
|
+
limit_parts = [p for p in parts if p.startswith("limit=")]
|
|
91
|
+
tag_parts = [p for p in parts if p.startswith("tag=")]
|
|
92
|
+
other_parts = [
|
|
93
|
+
p for p in parts if not (p.startswith("limit=") or p.startswith("tag="))
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
if not tag_parts or not limit_parts:
|
|
97
|
+
return parts
|
|
98
|
+
if len(limit_parts) > 1:
|
|
99
|
+
print(" ERROR: Only one 'limit' may be combined with 'tag(s)'.")
|
|
100
|
+
exit(3)
|
|
101
|
+
|
|
102
|
+
tag_values = [p.replace("tag=", "") for p in tag_parts]
|
|
103
|
+
limit_value = limit_parts[0].replace("limit=", "")
|
|
104
|
+
combo = limit_parts[0] + "".join(f";tag={value}" for value in tag_values)
|
|
105
|
+
print(
|
|
106
|
+
f" INFO: Combining 'limit' with 'tag(s)' -> keeping the {limit_value} most recent "
|
|
107
|
+
f"run(s) tagged with [{', '.join(tag_values)}] and removing older matching runs "
|
|
108
|
+
f"(runs without these tags are left untouched)."
|
|
109
|
+
)
|
|
110
|
+
# other independent options (index/run_start/alias) run before the combo
|
|
111
|
+
return other_parts + [combo]
|
|
112
|
+
|
|
64
113
|
def _check_argument_warnings(self, arguments, outputs, outputfolderpaths, use_logs, generate_dashboard, no_autoupdate, offline_dependencies):
|
|
65
114
|
"""Checks for argument combinations that are valid but likely unintended and prints warnings"""
|
|
66
115
|
no_outputs = not outputs and not outputfolderpaths
|
|
@@ -267,6 +316,7 @@ class ArgumentParser:
|
|
|
267
316
|
" • '-r run_start=2024-07-30 15:27:20.184407' -> remove specified run\n"
|
|
268
317
|
" • '-r alias=some_alias,tag=prod'\n"
|
|
269
318
|
" • '-r limit=10' -> keep only the 10 most recent runs\n"
|
|
319
|
+
" • '-r limit=10,tag=nightly' -> keep 10 newest 'nightly' runs, leave others\n"
|
|
270
320
|
" • '-r age=10d' -> remove runs older than 10 days\n"
|
|
271
321
|
" • '-r age=-10d' -> remove runs younger than 10 days\n"
|
|
272
322
|
" • (y)ear/(d)ay/(h)our/(m)inute/(s)econd supported\n"
|
|
@@ -275,6 +325,24 @@ class ArgumentParser:
|
|
|
275
325
|
nargs="*",
|
|
276
326
|
default=None,
|
|
277
327
|
)
|
|
328
|
+
db_group.add_argument(
|
|
329
|
+
"--logremoved",
|
|
330
|
+
metavar="TYPES_PATH",
|
|
331
|
+
help=(
|
|
332
|
+
"Log (append if file exists) run data to jsonl before removing from the db\n"
|
|
333
|
+
" • Supply what types to log (default=all) and (optional) a path to the desired .jsonl\n"
|
|
334
|
+
"Examples:\n"
|
|
335
|
+
" • '--logremoved \"/tmp/removed_runs.jsonl\"'\n"
|
|
336
|
+
" • '--logremoved \"run:suite\"'\n"
|
|
337
|
+
" • '--logremoved \"all:/tmp/removed_runs.jsonl\"'\n"
|
|
338
|
+
),
|
|
339
|
+
action="store",
|
|
340
|
+
type=str,
|
|
341
|
+
nargs="?",
|
|
342
|
+
const="all",
|
|
343
|
+
default=None,
|
|
344
|
+
dest="logremoved",
|
|
345
|
+
)
|
|
278
346
|
db_group.add_argument(
|
|
279
347
|
"-c",
|
|
280
348
|
"--databaseclass",
|
|
@@ -505,11 +573,13 @@ class ArgumentParser:
|
|
|
505
573
|
# handles the processing of --removeruns
|
|
506
574
|
remove_runs = None
|
|
507
575
|
if arguments.removeruns:
|
|
508
|
-
|
|
576
|
+
raw_parts = []
|
|
509
577
|
for runs in arguments.removeruns:
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
578
|
+
for part in str(runs[0]).split(","):
|
|
579
|
+
part = part.strip()
|
|
580
|
+
if part:
|
|
581
|
+
raw_parts.append(part)
|
|
582
|
+
remove_runs = self._process_remove_runs(raw_parts)
|
|
513
583
|
|
|
514
584
|
# handles the boolean handling of relevant arguments
|
|
515
585
|
generate_dashboard = self._normalize_bool(
|
|
@@ -605,6 +675,23 @@ class ArgumentParser:
|
|
|
605
675
|
ssl_certfile = arguments.ssl_certfile
|
|
606
676
|
ssl_keyfile = arguments.ssl_keyfile
|
|
607
677
|
|
|
678
|
+
# handles --logremoved: "type1:type2:path" or "path" or "type1:type2"
|
|
679
|
+
log_removed = None
|
|
680
|
+
if arguments.logremoved:
|
|
681
|
+
parts = arguments.logremoved.split(":")
|
|
682
|
+
types, remaining = [], list(parts)
|
|
683
|
+
# grab element types, leave path, for windows drive letter X: parse
|
|
684
|
+
for part in parts:
|
|
685
|
+
if part in _VALID_LOG_TYPES:
|
|
686
|
+
types.append(part)
|
|
687
|
+
remaining.pop(0)
|
|
688
|
+
else:
|
|
689
|
+
break
|
|
690
|
+
if not types:
|
|
691
|
+
types = ["all"]
|
|
692
|
+
path = ":".join(remaining) if remaining else "robot_removed_runs.jsonl"
|
|
693
|
+
log_removed = LogRemovedConfig(types=types, path=path)
|
|
694
|
+
|
|
608
695
|
# validates argument combinations
|
|
609
696
|
self._check_argument_errors(arguments, outputs, outputfolderpaths, force_json_config, database_class)
|
|
610
697
|
self._check_argument_warnings(arguments, outputs, outputfolderpaths, use_logs, generate_dashboard, no_autoupdate, offline_dependencies)
|
|
@@ -640,5 +727,6 @@ class ArgumentParser:
|
|
|
640
727
|
"ssl_certfile": ssl_certfile,
|
|
641
728
|
"ssl_keyfile": ssl_keyfile,
|
|
642
729
|
"log_url": arguments.logurl,
|
|
730
|
+
"log_removed": log_removed,
|
|
643
731
|
}
|
|
644
732
|
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,
|
|
@@ -152,80 +152,44 @@
|
|
|
152
152
|
align-items: center;
|
|
153
153
|
}
|
|
154
154
|
|
|
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;
|
|
155
|
+
/* Color picker buttons inside the Add Stat Widget modal */
|
|
156
|
+
.stat-color-btn {
|
|
157
|
+
min-width: 60px;
|
|
164
158
|
}
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
160
|
+
/* "All" tab stat list inside the Add Stat Widget modal */
|
|
161
|
+
.add-all-widgets-list {
|
|
162
|
+
max-height: 60vh;
|
|
163
|
+
overflow-y: auto;
|
|
169
164
|
}
|
|
170
165
|
|
|
171
|
-
.add-
|
|
166
|
+
.add-all-widgets-row {
|
|
172
167
|
display: flex;
|
|
173
|
-
flex-direction: column;
|
|
174
168
|
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;
|
|
169
|
+
gap: 0.5rem;
|
|
170
|
+
margin-bottom: 0.5rem;
|
|
194
171
|
}
|
|
195
172
|
|
|
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;
|
|
173
|
+
.add-all-widgets-row .form-check {
|
|
174
|
+
margin-bottom: 0;
|
|
205
175
|
}
|
|
206
176
|
|
|
207
|
-
.add-
|
|
208
|
-
|
|
209
|
-
background-color: rgba(128,128,128,0.08);
|
|
177
|
+
.add-all-widgets-row .add-all-widgets-title {
|
|
178
|
+
flex: 1 1 auto;
|
|
210
179
|
}
|
|
211
180
|
|
|
212
|
-
.add-
|
|
213
|
-
|
|
214
|
-
flex-direction: row;
|
|
215
|
-
align-items: center;
|
|
216
|
-
gap: 0.5rem;
|
|
217
|
-
user-select: none;
|
|
181
|
+
.add-all-widgets-row .add-all-widgets-badge {
|
|
182
|
+
flex: 0 0 auto;
|
|
218
183
|
}
|
|
219
184
|
|
|
220
|
-
.add-
|
|
221
|
-
font-
|
|
222
|
-
|
|
223
|
-
|
|
185
|
+
.add-all-widgets-group-heading {
|
|
186
|
+
font-weight: 600;
|
|
187
|
+
margin-top: 0.5rem;
|
|
188
|
+
margin-bottom: 0.25rem;
|
|
224
189
|
}
|
|
225
190
|
|
|
226
|
-
.add-
|
|
227
|
-
|
|
228
|
-
font-weight: 500;
|
|
191
|
+
.add-all-widgets-group-heading:first-child {
|
|
192
|
+
margin-top: 0;
|
|
229
193
|
}
|
|
230
194
|
|
|
231
195
|
/* Custom section divider bar rendered inside the unified GridStack */
|
|
@@ -247,20 +211,16 @@
|
|
|
247
211
|
user-select: none;
|
|
248
212
|
}
|
|
249
213
|
|
|
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 {
|
|
214
|
+
/* Move to first/last + delete controls on custom widgets/sections — pinned to top-right,
|
|
215
|
+
out of flex flow, sized and spaced like the icons in graph headers */
|
|
216
|
+
.custom-widget-move-controls {
|
|
260
217
|
position: absolute;
|
|
261
218
|
top: 5px;
|
|
262
219
|
right: 5px;
|
|
263
220
|
z-index: 1;
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
gap: 8px;
|
|
264
224
|
}
|
|
265
225
|
|
|
266
226
|
/* Compact flex layout for custom stat widgets — works at any GridStack size */
|
|
@@ -284,7 +244,12 @@
|
|
|
284
244
|
overflow: hidden;
|
|
285
245
|
text-overflow: ellipsis;
|
|
286
246
|
white-space: nowrap;
|
|
287
|
-
max-width:
|
|
247
|
+
max-width: 100%;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
251
|
+
.stat-widget-inner:has(.custom-widget-move-controls) .stat-widget-title {
|
|
252
|
+
max-width: calc(100% - 96px);
|
|
288
253
|
}
|
|
289
254
|
|
|
290
255
|
/* When the item is very short (h=1 row ≈ 60px) switch to a side-by-side row */
|
|
@@ -304,14 +269,25 @@
|
|
|
304
269
|
.grid-stack-item[data-gs-h="1"] .stat-widget-title {
|
|
305
270
|
flex: 1 1 0;
|
|
306
271
|
text-align: left;
|
|
307
|
-
max-width: calc(100% - 40px); /*
|
|
272
|
+
max-width: calc(100% - 40px); /* leave room for the value */
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
276
|
+
.grid-stack-item[gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-widget-title,
|
|
277
|
+
.grid-stack-item[data-gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-widget-title {
|
|
278
|
+
max-width: calc(100% - 136px); /* value + move/delete controls */
|
|
308
279
|
}
|
|
309
280
|
|
|
310
281
|
.grid-stack-item[gs-h="1"] .stat-value,
|
|
311
282
|
.grid-stack-item[data-gs-h="1"] .stat-value {
|
|
312
283
|
flex-shrink: 0;
|
|
313
284
|
font-size: 1.1rem;
|
|
314
|
-
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
288
|
+
.grid-stack-item[gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-value,
|
|
289
|
+
.grid-stack-item[data-gs-h="1"] .stat-widget-inner:has(.custom-widget-move-controls) .stat-value {
|
|
290
|
+
margin-right: 96px;
|
|
315
291
|
}
|
|
316
292
|
|
|
317
293
|
.grid-stack-item-content.blue-bg { background-color: rgba(13, 110, 253, 0.15); }
|
|
@@ -319,6 +295,92 @@
|
|
|
319
295
|
.grid-stack-item-content.red-bg { background-color: rgba(206, 62, 1, 0.2); }
|
|
320
296
|
.grid-stack-item-content.yellow-bg { background-color: rgba(254, 216, 79, 0.2); }
|
|
321
297
|
|
|
298
|
+
/* ─── Custom link widget ─────────────────────────────────────────────────── */
|
|
299
|
+
|
|
300
|
+
/* Inner layout — mirrors stat-widget-inner */
|
|
301
|
+
.link-widget-inner {
|
|
302
|
+
position: relative;
|
|
303
|
+
display: flex;
|
|
304
|
+
flex-direction: column;
|
|
305
|
+
justify-content: center;
|
|
306
|
+
align-items: center;
|
|
307
|
+
gap: 0.25rem;
|
|
308
|
+
height: 100%;
|
|
309
|
+
text-align: center;
|
|
310
|
+
overflow: hidden;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Label — main large text, like .stat-value */
|
|
314
|
+
.link-widget-label {
|
|
315
|
+
margin: 0;
|
|
316
|
+
font-weight: 600;
|
|
317
|
+
font-size: 1.5rem;
|
|
318
|
+
line-height: 1.2;
|
|
319
|
+
overflow: hidden;
|
|
320
|
+
text-overflow: ellipsis;
|
|
321
|
+
white-space: nowrap;
|
|
322
|
+
max-width: 100%;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* URL preview — small muted text below the label */
|
|
326
|
+
.link-widget-url {
|
|
327
|
+
font-size: 0.72rem;
|
|
328
|
+
opacity: 0.55;
|
|
329
|
+
overflow: hidden;
|
|
330
|
+
text-overflow: ellipsis;
|
|
331
|
+
white-space: nowrap;
|
|
332
|
+
max-width: 100%;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
336
|
+
.link-widget-inner:has(.custom-widget-move-controls) .link-widget-label,
|
|
337
|
+
.link-widget-inner:has(.custom-widget-move-controls) .link-widget-url {
|
|
338
|
+
max-width: calc(100% - 96px);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* Clickable in non-edit mode */
|
|
342
|
+
.link-widget-clickable {
|
|
343
|
+
cursor: pointer;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* Same padding as stat widget tiles */
|
|
347
|
+
.grid-stack-item-content:has(.link-widget-inner) {
|
|
348
|
+
padding: 8px 12px;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* h=1 compact row — mirrors stat widget compact layout */
|
|
352
|
+
.grid-stack-item[gs-h="1"] .link-widget-inner,
|
|
353
|
+
.grid-stack-item[data-gs-h="1"] .link-widget-inner {
|
|
354
|
+
flex-direction: row;
|
|
355
|
+
justify-content: space-between;
|
|
356
|
+
align-items: center;
|
|
357
|
+
gap: 0.5rem;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.grid-stack-item[gs-h="1"] .link-widget-label,
|
|
361
|
+
.grid-stack-item[data-gs-h="1"] .link-widget-label {
|
|
362
|
+
flex: 1 1 0;
|
|
363
|
+
font-size: 1.1rem;
|
|
364
|
+
text-align: left;
|
|
365
|
+
max-width: calc(60% - 10px);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.grid-stack-item[gs-h="1"] .link-widget-url,
|
|
369
|
+
.grid-stack-item[data-gs-h="1"] .link-widget-url {
|
|
370
|
+
flex-shrink: 0;
|
|
371
|
+
font-size: 0.72rem;
|
|
372
|
+
text-align: right;
|
|
373
|
+
max-width: 40%;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Leave room for the move/delete controls pinned to the top-right in edit mode */
|
|
377
|
+
.grid-stack-item[gs-h="1"] .link-widget-inner:has(.custom-widget-move-controls) .link-widget-url {
|
|
378
|
+
max-width: calc(40% - 96px);
|
|
379
|
+
margin-right: 96px;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* ─────────────────────────────────────────────────────────────────────────── */
|
|
383
|
+
|
|
322
384
|
.fullscreen .stat-value {
|
|
323
385
|
font-size: 4rem;
|
|
324
386
|
}
|
|
@@ -361,6 +423,22 @@
|
|
|
361
423
|
height: 200px;
|
|
362
424
|
}
|
|
363
425
|
|
|
426
|
+
/* relative run time, pinned to the bottom right corner of a run card without
|
|
427
|
+
affecting the layout of the stats/duration columns next to the donut */
|
|
428
|
+
.overview-card .card-body {
|
|
429
|
+
position: relative;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.run-card-run-time {
|
|
433
|
+
position: absolute;
|
|
434
|
+
right: 1rem;
|
|
435
|
+
bottom: 0.55rem;
|
|
436
|
+
color: var(--color-text-muted);
|
|
437
|
+
cursor: help;
|
|
438
|
+
font-size: 0.85em;
|
|
439
|
+
white-space: nowrap;
|
|
440
|
+
}
|
|
441
|
+
|
|
364
442
|
.overview-card {
|
|
365
443
|
cursor: pointer;
|
|
366
444
|
min-width: 300px;
|
|
@@ -425,6 +503,13 @@
|
|
|
425
503
|
gap: 8px;
|
|
426
504
|
}
|
|
427
505
|
|
|
506
|
+
/* Icon row in section card headers (add stat/link widget, add section, move/show/hide section) */
|
|
507
|
+
.section-header-icons {
|
|
508
|
+
display: flex;
|
|
509
|
+
align-items: center;
|
|
510
|
+
gap: 8px;
|
|
511
|
+
}
|
|
512
|
+
|
|
428
513
|
.grid-stack-item-content .graph-body {
|
|
429
514
|
flex: 1 1 auto;
|
|
430
515
|
display: flex;
|