aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-19 21:22:34 +0200
committerDanilo M. <danix@danix.xyz>2026-10-19 21:22:34 +0200
commit5609a744ef5e89c4d57a5239199a6afe5497590e (patch)
tree131c8613b5bdadf33a06b08e09066b628bc2f033
parent3865fd7d10d76cd265800fd8d5915a5792be4619 (diff)
downloadsb-builder-5609a744ef5e89c4d57a5239199a6afe5497590e.tar.gz
sb-builder-5609a744ef5e89c4d57a5239199a6afe5497590e.zip
Dashboard: freshness dot on the slackpkg tile
Green when the last upgrade is within a day, yellow after one day, red after two; the age text beside it carries the meaning too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--sb_builder/dashboard.py12
-rw-r--r--tests/test_gui.py21
2 files changed, 31 insertions, 2 deletions
diff --git a/sb_builder/dashboard.py b/sb_builder/dashboard.py
index 474af66..8c98040 100644
--- a/sb_builder/dashboard.py
+++ b/sb_builder/dashboard.py
@@ -34,6 +34,14 @@ GAP = 2 # px of surface between bar segments
SPARK_POINTS = 60 # ~30 min at the default 30 s dashboard refresh
+def upgrade_color(ts, now=None):
+ """Status color for the last upgrade: green within a day, yellow, red after two."""
+ if ts is None:
+ return None
+ age = (now or time.time()) - ts
+ return STATUS["failed"] if age > 2 * 86400 else STATUS["aborted"] if age > 86400 else STATUS["ok"]
+
+
def meter_color(pct, accent):
"""Fill color by severity: accent, then warning from 80%, critical from 90%."""
if pct >= 90:
@@ -306,7 +314,9 @@ class Dashboard(QWidget):
" · ".join(f"{o} {fmt_duration(None if ts is None else time.time() - ts)}"
for o, ts in sync.items()))
pkg = s.get("slackpkg") or {}
- t["slackpkg"].set(fmt_age(pkg.get("upgrade")) if pkg.get("upgrade") else NA,
+ dot = upgrade_color(pkg.get("upgrade"))
+ t["slackpkg"].set(f'<span style="color:{dot}">&#9679;</span> {fmt_age(pkg["upgrade"])}'
+ if dot else NA,
"last upgrade · lists updated " + fmt_duration(
None if pkg.get("update") is None else time.time() - pkg["update"]))
load, mem, disk = s.get("load"), s.get("mem"), s.get("disk") or {}
diff --git a/tests/test_gui.py b/tests/test_gui.py
index a597c01..fb0044c 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -271,6 +271,17 @@ class MeterTest(unittest.TestCase):
self.assertEqual(m.value.text(), "n/a")
+class UpgradeDotTest(unittest.TestCase):
+ def test_thresholds(self):
+ from sb_builder.dashboard import STATUS, upgrade_color
+ now = 1_800_000_000
+ day = 86400
+ self.assertEqual(upgrade_color(now - day + 60, now), STATUS["ok"])
+ self.assertEqual(upgrade_color(now - day - 60, now), STATUS["aborted"])
+ self.assertEqual(upgrade_color(now - 2 * day - 60, now), STATUS["failed"])
+ self.assertIsNone(upgrade_color(None, now))
+
+
class SmokeTest(unittest.TestCase):
def window(self, **answers):
remote = FakeRemote({"version": ({"version": VERSION, "overlays": ["personal"]}, None),
@@ -296,7 +307,7 @@ class SmokeTest(unittest.TestCase):
("85%", "25%", "90%"))
self.assertIn("load 0.50", d.cpu.detail.text())
self.assertEqual(d.disk.detail.text(), "10.0 GiB free of 100.0 GiB")
- self.assertEqual(tiles["slackpkg"].value.text(), "n/a")
+ self.assertEqual(tiles["slackpkg"].value.text(), "n/a") # no upgrade time: no dot
self.assertIn("lists updated", tiles["slackpkg"].detail.text())
self.assertEqual(len(d.cpu.spark.points), 1)
self.assertEqual(w.dashboard.legend.text(), "n/a")
@@ -321,6 +332,14 @@ class SmokeTest(unittest.TestCase):
self.assertEqual(w.conn.text(), "● VM off")
self.assertFalse(w.vm_btn.isHidden())
+ def test_slackpkg_dot(self):
+ old = dict(STATS, slackpkg={"update": None, "upgrade": 1})
+ w, _ = self.window(stats=(old, None))
+ w.poll_jobs()
+ text = w.dashboard.tiles["slackpkg"].value.text()
+ self.assertIn("#d03b3b", text)
+ self.assertTrue(text.endswith(" ago"))
+
def test_results_bar(self):
w, _ = self.window(stats=(dict(STATS, slackrepo={"results": {"failed": 1, "ok": 3, "removed": 1},
"built": 4}), None))