From 08640d910807d3548b8fb28a51e68346f74cc7f5 Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Mon, 13 Jan 2025 13:30:04 +0200 Subject: [PATCH] feat: add dashboard (#139) --- .../ahriman/templates/build-status.jinja2 | 5 +- .../templates/build-status/dashboard.jinja2 | 76 +++++++++++++++++++ .../build-status/package-info-modal.jinja2 | 8 -- .../templates/build-status/table.jinja2 | 65 +++++++++++++--- .../templates/utils/bootstrap-scripts.jinja2 | 8 ++ src/ahriman/web/schemas/file_schema.py | 2 +- tests/ahriman/core/test_utils.py | 1 + 7 files changed, 146 insertions(+), 19 deletions(-) create mode 100644 package/share/ahriman/templates/build-status/dashboard.jinja2 diff --git a/package/share/ahriman/templates/build-status.jinja2 b/package/share/ahriman/templates/build-status.jinja2 index 6b90fd0d..86b52e8b 100644 --- a/package/share/ahriman/templates/build-status.jinja2 +++ b/package/share/ahriman/templates/build-status.jinja2 @@ -36,7 +36,9 @@
+ + +
+ + + + diff --git a/package/share/ahriman/templates/build-status/package-info-modal.jinja2 b/package/share/ahriman/templates/build-status/package-info-modal.jinja2 index 311c9abe..d44e6908 100644 --- a/package/share/ahriman/templates/build-status/package-info-modal.jinja2 +++ b/package/share/ahriman/templates/build-status/package-info-modal.jinja2 @@ -296,14 +296,6 @@ } function loadPackage(packageBase, onFailure) { - const headerClass = status => { - if (status === "pending") return ["bg-warning"]; - if (status === "building") return ["bg-warning"]; - if (status === "failed") return ["bg-danger", "text-white"]; - if (status === "success") return ["bg-success", "text-white"]; - return ["bg-secondary", "text-white"]; - }; - makeRequest( `/api/v1/packages/${packageBase}`, { diff --git a/package/share/ahriman/templates/build-status/table.jinja2 b/package/share/ahriman/templates/build-status/table.jinja2 index 9874a40c..618e54fb 100644 --- a/package/share/ahriman/templates/build-status/table.jinja2 +++ b/package/share/ahriman/templates/build-status/table.jinja2 @@ -7,7 +7,7 @@ // so far bootstrap-table only operates with jquery elements const table = $(document.getElementById("packages")); - const statusBadge = document.getElementById("badge-status"); + const dashboardButton = document.getElementById("dashboard-button"); const versionBadge = document.getElementById("badge-version"); function doPackageAction(uri, packages, repository, successText, failureText, data) { @@ -141,14 +141,62 @@ data => { versionBadge.innerHTML = ` ahriman ${safe(data.version)}`; - statusBadge.classList.remove(...statusBadge.classList); - statusBadge.classList.add("btn"); - statusBadge.classList.add(badgeClass(data.status.status)); + dashboardButton.classList.remove(...dashboardButton.classList); + dashboardButton.classList.add("btn"); + dashboardButton.classList.add(badgeClass(data.status.status)); - const popover = bootstrap.Popover.getOrCreateInstance(statusBadge); - popover.dispose(); - statusBadge.dataset.bsContent = `${data.status.status} at ${new Date(1000 * data.status.timestamp).toISOStringShort()}`; - bootstrap.Popover.getOrCreateInstance(statusBadge); + dashboardModalHeader.classList.remove(...dashboardModalHeader.classList); + dashboardModalHeader.classList.add("modal-header"); + headerClass(data.status.status).forEach(clz => dashboardModalHeader.classList.add(clz)); + + dashboardName.textContent = data.repository; + dashboardArchitecture.textContent = data.architecture; + dashboardStatus.textContent = data.status.status; + dashboardStatusTimestamp.textContent = new Date(1000 * data.status.timestamp).toISOStringShort(); + + if (dashboardPackagesStatusesChart) { + const labels = [ + "unknown", + "pending", + "building", + "failed", + "success", + ]; + dashboardPackagesStatusesChart.config.data = { + labels: labels, + datasets: [{ + label: "packages in status", + data: labels.map(label => data.packages[label]), + backgroundColor: [ + "rgb(55, 58, 60)", + "rgb(255, 117, 24)", + "rgb(255, 117, 24)", + "rgb(255, 0, 57)", + "rgb(63, 182, 24)", // copy-paste from current style + ], + }], + }; + dashboardPackagesStatusesChart.update(); + } + + if (dashboardPackagesCountChart) { + dashboardPackagesCountChart.config.data = { + labels: ["packages"], + datasets: [ + { + label: "archives", + data: [data.stats.packages], + }, + { + label: "bases", + data: [data.stats.bases], + }, + ], + }; + dashboardPackagesCountChart.update(); + } + + dashboardCanvas.hidden = data.status.total > 0; }, ); } @@ -227,7 +275,6 @@ }); }); - bootstrap.Popover.getOrCreateInstance(statusBadge); selectRepository(); }); diff --git a/package/share/ahriman/templates/utils/bootstrap-scripts.jinja2 b/package/share/ahriman/templates/utils/bootstrap-scripts.jinja2 index 32fcf9e7..b350ef31 100644 --- a/package/share/ahriman/templates/utils/bootstrap-scripts.jinja2 +++ b/package/share/ahriman/templates/utils/bootstrap-scripts.jinja2 @@ -58,6 +58,14 @@ return value.includes(dataList[index].toLowerCase()); } + function headerClass(status) { + if (status === "pending") return ["bg-warning"]; + if (status === "building") return ["bg-warning"]; + if (status === "failed") return ["bg-danger", "text-white"]; + if (status === "success") return ["bg-success", "text-white"]; + return ["bg-secondary", "text-white"]; + } + function listToTable(data) { return Array.from(new Set(data)) .sort() diff --git a/src/ahriman/web/schemas/file_schema.py b/src/ahriman/web/schemas/file_schema.py index 7ff2e1e5..d6455a72 100644 --- a/src/ahriman/web/schemas/file_schema.py +++ b/src/ahriman/web/schemas/file_schema.py @@ -25,6 +25,6 @@ class FileSchema(Schema): request file upload schema """ - archive = fields.Field(required=True, metadata={ + archive = fields.Raw(required=True, metadata={ "description": "Package archive to be uploaded", }) diff --git a/tests/ahriman/core/test_utils.py b/tests/ahriman/core/test_utils.py index ac5b90dd..1b65a90c 100644 --- a/tests/ahriman/core/test_utils.py +++ b/tests/ahriman/core/test_utils.py @@ -479,6 +479,7 @@ def test_walk(resource_path_root: Path) -> None: resource_path_root / "models" / "pkgbuild", resource_path_root / "models" / "utf8", resource_path_root / "web" / "templates" / "build-status" / "alerts.jinja2", + resource_path_root / "web" / "templates" / "build-status" / "dashboard.jinja2", resource_path_root / "web" / "templates" / "build-status" / "key-import-modal.jinja2", resource_path_root / "web" / "templates" / "build-status" / "login-modal.jinja2", resource_path_root / "web" / "templates" / "build-status" / "package-add-modal.jinja2",