dashboard: PCs not reporting, and the card styling standard it broke
Second wave-one card. GET /api/computers/dashboard/quiet lists two populations and deliberately does not merge them into one count. A PC that reported and went quiet is probably off, moved or broken. A PC that has NEVER reported is worse: not enrolled, or enrolled against the wrong pc-type, so nothing enforces anything on it and no backup of it exists. That one hides indefinitely because nothing about it fails loudly - the same shape as the bay that carried a wrong machine number for weeks. Never-reported sorts above the merely quiet, then longest silence first: the order someone should work down the list, not the order rows left the table. A soft-deleted PC is excluded - a decommissioned machine is silent on purpose, and listing it would train people to ignore the card, which is the failure this whole board exists to avoid. The window is computers_quietreporthours, default 24, because every site will disagree with any number picked here (ADR-015). A malformed value falls back rather than failing the card. This also replaces the computers plugin's old widget declaration, which named a component nobody ever wrote. Four such declarations remain and will convert as their cards arrive. Two fixes to the renderer found while wiring this up. Meta specs now support a trailing unit, so a row reads 'quiet for 3 days' rather than 'quiet for 3'. And the card styles hardcoded hex colours against the frontend standard, including a var(--card-bg) that DOES NOT EXIST - the variable is --bg-card - so the fallback would have painted every card white and broken dark mode entirely. Now --bg-card, --border, --danger, --warning, --primary and --link throughout.
This commit is contained in:
@@ -934,3 +934,63 @@ def dashboard_summary():
|
||||
'shopfloor': shopfloor_count,
|
||||
'nonshopfloor': total - shopfloor_count
|
||||
})
|
||||
|
||||
|
||||
@computers_bp.route('/dashboard/quiet', methods=['GET'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.view')
|
||||
def dashboard_quiet():
|
||||
"""PCs that have stopped reporting, and PCs that never started.
|
||||
|
||||
Two populations, deliberately distinguished rather than merged into one
|
||||
count. A PC that reported and went quiet is probably switched off, moved or
|
||||
broken. A PC that has NEVER reported is worse: it is either not enrolled, or
|
||||
enrolled against the wrong pc-type, so nothing enforces anything on it and
|
||||
no backup of it exists. That one hides indefinitely because nothing about it
|
||||
is failing loudly - and it is exactly the shape of the bay that carried a
|
||||
wrong machine number for weeks.
|
||||
|
||||
Silence is the only signal available here. There is no heartbeat separate
|
||||
from the report, so 'stopped reporting' is measured against the collector's
|
||||
own last write.
|
||||
"""
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
hours = 24
|
||||
setting = Setting.query.filter_by(key='computers_quietreporthours').first()
|
||||
if setting and (setting.value or '').strip():
|
||||
try:
|
||||
hours = int(setting.value)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
cutoff = datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(hours=hours)
|
||||
|
||||
query = (db.session.query(Computer, Asset)
|
||||
.join(Asset, Asset.assetid == Computer.assetid)
|
||||
.filter(Asset.isactive.is_(True)))
|
||||
|
||||
rows = []
|
||||
for comp, asset in query.all():
|
||||
last = comp.lastreporteddate
|
||||
if last is None:
|
||||
rows.append({
|
||||
'computerid': comp.computerid,
|
||||
'hostname': comp.hostname,
|
||||
'state': 'never reported',
|
||||
'lastreported': None,
|
||||
'quietdays': None,
|
||||
})
|
||||
elif last < cutoff:
|
||||
rows.append({
|
||||
'computerid': comp.computerid,
|
||||
'hostname': comp.hostname,
|
||||
'state': 'stopped reporting',
|
||||
'lastreported': last.isoformat() + 'Z',
|
||||
'quietdays': (datetime.now(timezone.utc).replace(tzinfo=None)
|
||||
- last).days,
|
||||
})
|
||||
|
||||
# Never-reported first, then longest silence: the order someone should work
|
||||
# down the list, not the order the rows came out of the table.
|
||||
rows.sort(key=lambda r: (r['quietdays'] is not None, -(r['quietdays'] or 0)))
|
||||
return success_response(rows[:50])
|
||||
|
||||
Reference in New Issue
Block a user