feat(ui): shared |bytes |rate |duration |ago filters and status_class from Settings → Thresholds (#5093)
CI / lint (push) Successful in 3s
CI / unit (push) Successful in 1m28s
CI / integration (push) Successful in 1m7s
CI / publish (push) Successful in 2m40s

#5093 "Shared format filters and status_class driven by Settings → Thresholds".

- steward/core/formatting.py: human_bytes (binary units), human_rate,
  human_duration (two largest units), ago (a <time> titled with the absolute
  UTC time) and status_class(value, kind, thresholds) -> ok/warn/crit/unknown,
  which raises on a kind the thresholds don't name.
- app.py registers them as the bytes, rate, duration and ago filters and the
  status_class global; threshold_style is now graded by status_class (and caps
  its weight at 500).
- Replaces the copies: docker's _human_bytes/_human_uptime, core alerts'
  _duration, the host-agent fmt_bps/fmt_bytes macros, the Traefik and UniFi
  byte-rate arithmetic, the swarm-failures ago macro and two uptime spans.
- Tests: a table per helper, and every template compiled against the app's
  filters, so an unknown filter fails CI rather than a page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 14:25:06 -04:00
co-authored by Claude Opus 5.5
parent 65411d3ce8
commit bf08169176
17 changed files with 287 additions and 186 deletions
+2 -8
View File
@@ -21,10 +21,7 @@
<tr>
<td style="padding:0.25rem 0.5rem 0.25rem 0;">{{ cat.cat_name or cat.cat_id }}</td>
<td style="padding:0.25rem 0.3rem;text-align:right;color:var(--text-muted);font-size:0.78rem;white-space:nowrap;">
{% if bytes >= 1073741824 %}{{ "%.1f"|format(bytes / 1073741824) }} GB
{% elif bytes >= 1048576 %}{{ "%.1f"|format(bytes / 1048576) }} MB
{% elif bytes >= 1024 %}{{ "%.0f"|format(bytes / 1024) }} KB
{% else %}{{ bytes }} B{% endif %}
{{ bytes|bytes }}
</td>
<td style="padding:0.25rem 0;width:4rem;">
<div style="background:var(--border);border-radius:2px;height:6px;overflow:hidden;">
@@ -50,10 +47,7 @@
</td>
<td style="padding:0.25rem 0;text-align:right;font-size:0.78rem;color:var(--text-muted);white-space:nowrap;">
{% set bytes = c.bytes %}
{% if bytes >= 1073741824 %}{{ "%.1f"|format(bytes / 1073741824) }} GB
{% elif bytes >= 1048576 %}{{ "%.1f"|format(bytes / 1048576) }} MB
{% elif bytes >= 1024 %}{{ "%.0f"|format(bytes / 1024) }} KB
{% else %}{{ bytes }} B{% endif %}
{{ bytes|bytes }}
</td>
</tr>
{% endfor %}
+6 -14
View File
@@ -29,9 +29,7 @@
<span style="color:var(--text-muted);">&#8595; RX</span>
<span style="margin-left:0.3rem;">
{% set bps = latest_wan.rx_bytes_rate %}
{% if bps >= 1048576 %}{{ "%.1f"|format(bps / 1048576) }} MB/s
{% elif bps >= 1024 %}{{ "%.0f"|format(bps / 1024) }} KB/s
{% else %}{{ "%.0f"|format(bps) }} B/s{% endif %}
{{ bps|rate }}
</span>
<span style="margin-left:0.3rem;">{{ sparkline_rx | safe }}</span>
</span>
@@ -39,9 +37,7 @@
<span style="color:var(--text-muted);">&#8593; TX</span>
<span style="margin-left:0.3rem;">
{% set bps = latest_wan.tx_bytes_rate %}
{% if bps >= 1048576 %}{{ "%.1f"|format(bps / 1048576) }} MB/s
{% elif bps >= 1024 %}{{ "%.0f"|format(bps / 1024) }} KB/s
{% else %}{{ "%.0f"|format(bps) }} B/s{% endif %}
{{ bps|rate }}
</span>
<span style="margin-left:0.3rem;">{{ sparkline_tx | safe }}</span>
</span>
@@ -49,7 +45,7 @@
{% if latest_wan.uptime_seconds is not none %}
<span style="color:var(--text-dim);font-size:0.8rem;">
up {{ (latest_wan.uptime_seconds // 3600) }}h {{ ((latest_wan.uptime_seconds % 3600) // 60) }}m
up {{ latest_wan.uptime_seconds|duration }}
</span>
{% endif %}
</div>
@@ -96,7 +92,7 @@
{% if d.state == 1 and d.uptime_seconds %}
<tr>
<td colspan="3" style="padding:0 0 0.3rem 1.2rem;color:var(--text-dim);font-size:0.75rem;">
up {{ (d.uptime_seconds // 86400) }}d {{ ((d.uptime_seconds % 86400) // 3600) }}h
up {{ d.uptime_seconds|duration }}
</td>
</tr>
{% endif %}
@@ -121,13 +117,9 @@
<td style="text-align:right;padding:0.2rem 0;white-space:nowrap;font-size:0.78rem;">
{% set rx = c.rx_rate %}{% set tx = c.tx_rate %}
<span style="color:var(--text-muted);">&#8595;</span>
{% if rx >= 1048576 %}{{ "%.1f"|format(rx/1048576) }}M
{% elif rx >= 1024 %}{{ "%.0f"|format(rx/1024) }}K
{% else %}{{ "%.0f"|format(rx) }}B{% endif %}
{{ rx|rate }}
<span style="color:var(--text-muted);margin-left:0.3rem;">&#8593;</span>
{% if tx >= 1048576 %}{{ "%.1f"|format(tx/1048576) }}M
{% elif tx >= 1024 %}{{ "%.0f"|format(tx/1024) }}K
{% else %}{{ "%.0f"|format(tx) }}B{% endif %}
{{ tx|rate }}
</td>
</tr>
{% endfor %}
+4 -12
View File
@@ -37,16 +37,12 @@
<span>
<span style="color:var(--text-muted);">&#8595;</span>
{% set bps = latest_wan.rx_bytes_rate %}
{% if bps >= 1048576 %}{{ "%.1f"|format(bps / 1048576) }}MB/s
{% elif bps >= 1024 %}{{ "%.0f"|format(bps / 1024) }}KB/s
{% else %}{{ "%.0f"|format(bps) }}B/s{% endif %}
{{ bps|rate }}
</span>
<span>
<span style="color:var(--text-muted);">&#8593;</span>
{% set bps = latest_wan.tx_bytes_rate %}
{% if bps >= 1048576 %}{{ "%.1f"|format(bps / 1048576) }}MB/s
{% elif bps >= 1024 %}{{ "%.0f"|format(bps / 1024) }}KB/s
{% else %}{{ "%.0f"|format(bps) }}B/s{% endif %}
{{ bps|rate }}
</span>
{% endif %}
</div>
@@ -76,14 +72,10 @@
{% set total_bps = c.tx_rate + c.rx_rate %}
<span style="color:var(--text-muted);">&#8595;</span>
{% set rx = c.rx_rate %}
{% if rx >= 1048576 %}{{ "%.1f"|format(rx / 1048576) }}M
{% elif rx >= 1024 %}{{ "%.0f"|format(rx / 1024) }}K
{% else %}{{ "%.0f"|format(rx) }}B{% endif %}
{{ rx|rate }}
<span style="color:var(--text-muted);margin-left:0.4rem;">&#8593;</span>
{% set tx = c.tx_rate %}
{% if tx >= 1048576 %}{{ "%.1f"|format(tx / 1048576) }}M
{% elif tx >= 1024 %}{{ "%.0f"|format(tx / 1024) }}K
{% else %}{{ "%.0f"|format(tx) }}B{% endif %}
{{ tx|rate }}
</div>
</div>
{% endfor %}