aboutsummaryrefslogtreecommitdiffstats
path: root/sb_builder/dashboard.py
diff options
context:
space:
mode:
Diffstat (limited to 'sb_builder/dashboard.py')
-rw-r--r--sb_builder/dashboard.py81
1 files changed, 81 insertions, 0 deletions
diff --git a/sb_builder/dashboard.py b/sb_builder/dashboard.py
new file mode 100644
index 0000000..aac1b9f
--- /dev/null
+++ b/sb_builder/dashboard.py
@@ -0,0 +1,81 @@
+# sb-builder - Dashboard tab
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""Dashboard tab: tiles filled from sbb-remote stats and jobs."""
+
+import time
+
+from PyQt6.QtWidgets import (QGridLayout, QGroupBox, QHBoxLayout, QLabel,
+ QPushButton, QVBoxLayout, QWidget)
+
+from .fmt import NA, describe, fmt_age, fmt_bytes, fmt_duration
+
+TILES = ("VM", "Repo", "slackrepo", "Sync", "Jobs", "Orphans")
+
+
+class Dashboard(QWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.labels = {}
+ grid = QGridLayout()
+ for i, name in enumerate(TILES):
+ box = QGroupBox(name)
+ label = QLabel(NA)
+ label.setWordWrap(True)
+ QVBoxLayout(box).addWidget(label)
+ grid.addWidget(box, i // 3, i % 3)
+ self.labels[name] = label
+ self.labels["Orphans"].setText("coming in phase 5")
+ self.stale = QLabel()
+ self.refresh_btn = QPushButton("Refresh")
+ top = QHBoxLayout()
+ top.addWidget(self.stale)
+ top.addStretch()
+ top.addWidget(self.refresh_btn)
+ layout = QVBoxLayout(self)
+ layout.addLayout(top)
+ layout.addLayout(grid)
+ layout.addStretch()
+
+ def show_stats(self, s):
+ L = self.labels
+ load, disk = s.get("load"), s.get("disk_free") or {}
+ L["VM"].setText(
+ f"load: {NA if load is None else ' '.join(f'{x:.2f}' for x in load)}\n"
+ f"uptime: {fmt_duration(s.get('uptime'))}\n"
+ f"free: repo {fmt_bytes(disk.get('repo'))}, state {fmt_bytes(disk.get('state'))}")
+ p = s.get("packages")
+ L["Repo"].setText(NA if p is None else "\n".join(f"{k}: {v}" for k, v in p.items()))
+ sr = s.get("slackrepo")
+ if sr is None:
+ L["slackrepo"].setText(NA)
+ else:
+ never = NA if p is None else p["total"] - sr["built"]
+ L["slackrepo"].setText("\n".join(
+ [f"built: {sr['built']}", f"never built: {never}",
+ *(f"{k}: {v}" for k, v in sorted(sr["results"].items()))]))
+ sync = s.get("sync") or {}
+ L["Sync"].setText("\n".join(f"{o}: {fmt_age(t)}" for o, t in sync.items()) or NA)
+
+ def show_jobs(self, jobs):
+ self.labels["Jobs"].setText("\n".join(describe(j) for j in jobs[:10]) or "no jobs yet")
+
+ def set_stale(self, since):
+ """Grey the tiles when the VM stopped answering; None clears it."""
+ for label in self.labels.values():
+ label.setEnabled(since is None)
+ self.stale.setText("" if since is None else
+ f"stale since {time.strftime('%H:%M', time.localtime(since))}")