dashboard: printer supplies, expiring warranties, mis-numbered bays
Wave one complete. Three cards, no new data and no migrations. Printer supplies reuses the existing low-supplies query and its five-minute cache; a Zabbix round-trip per printer on every dashboard load would make this the slowest page in the app. One row per printer listing every depleted cartridge, criticals first - a row per cartridge would report one printer three times and read as three problems, and showing only the worst class would hide a low cartridge behind a critical one on the same machine when whoever walks out there wants to carry both. While there: the low-supplies REPORT itself was including healthy cartridges. A printer with one empty black and three full colour ones listed all four, so the reader had to find the problem inside the row. It now lists only what needs replacing, and the test that asserted the old behaviour now asserts the new. Expiring warranties keeps already-expired entries on the list rather than dropping them the day they lapse, which is how they get missed. Horizon is warranty_expiringdays, default 90, because that suits a site budgeting quarterly and nobody else. Mis-numbered bays promotes check-shared-machines out of a CLI command nobody will remember to run - it found seven bays that had been wrong for weeks. It reports only numbers with NO child assets, so part markers legitimately sharing an operation stay silent: that distinction is the whole card, and without it it would list correct data beside faults and be ignored. Printers also loses its dead component-named widget; notifications, network and machines still have theirs.
This commit is contained in:
@@ -994,3 +994,73 @@ def dashboard_quiet():
|
||||
# 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])
|
||||
|
||||
|
||||
@computers_bp.route('/dashboard/sharedmachines', methods=['GET'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.view')
|
||||
def dashboard_sharedmachines():
|
||||
"""Machine numbers claimed by more than one PC, with nothing filed under
|
||||
them.
|
||||
|
||||
Several devices genuinely sharing a number is legitimate - part markers do
|
||||
it - and those are modelled: each device is its own asset filed `partof` the
|
||||
operation, so the operation has CHILD ASSETS. Two PCs carrying the same
|
||||
machine number by mistake looks identical from a count and has none. That
|
||||
distinction is the whole card; without it this would list correct data
|
||||
alongside faults and get ignored.
|
||||
|
||||
Promoted from `flask relationships check-shared-machines`, which answers the
|
||||
same question and which nobody will remember to run. This one found seven
|
||||
mis-numbered bays that had been that way for weeks.
|
||||
"""
|
||||
from shopdb.api import AssetRelationship, RelationshipType
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
controls = RelationshipType.query.filter_by(
|
||||
relationshiptype='controls').first()
|
||||
partof = RelationshipType.query.filter_by(relationshiptype='partof').first()
|
||||
if not controls:
|
||||
return success_response([])
|
||||
|
||||
pcasset = aliased(Asset)
|
||||
machineasset = aliased(Asset)
|
||||
|
||||
rows = (db.session.query(machineasset.assetid, machineasset.assetnumber,
|
||||
pcasset.assetnumber)
|
||||
.select_from(AssetRelationship)
|
||||
.join(pcasset, AssetRelationship.sourceassetid == pcasset.assetid)
|
||||
.join(machineasset,
|
||||
AssetRelationship.targetassetid == machineasset.assetid)
|
||||
.filter(AssetRelationship.relationshiptypeid ==
|
||||
controls.relationshiptypeid,
|
||||
AssetRelationship.label == 'collector:machine',
|
||||
AssetRelationship.isactive.is_(True))
|
||||
.all())
|
||||
|
||||
bymachine = {}
|
||||
for assetid, machinenumber, pcnumber in rows:
|
||||
bymachine.setdefault((assetid, machinenumber), []).append(pcnumber)
|
||||
|
||||
out = []
|
||||
for (assetid, machinenumber), pcs in bymachine.items():
|
||||
if len(pcs) < 2:
|
||||
continue
|
||||
children = 0
|
||||
if partof:
|
||||
children = (AssetRelationship.query
|
||||
.filter_by(targetassetid=assetid,
|
||||
relationshiptypeid=partof.relationshiptypeid,
|
||||
isactive=True)
|
||||
.count())
|
||||
if children:
|
||||
continue # modelled: the devices are assets in their own right
|
||||
out.append({
|
||||
'assetid': assetid,
|
||||
'machinenumber': machinenumber,
|
||||
'pccount': len(pcs),
|
||||
'pcs': ', '.join(sorted(p for p in pcs if p)),
|
||||
})
|
||||
|
||||
out.sort(key=lambda r: -r['pccount'])
|
||||
return success_response(out)
|
||||
|
||||
@@ -1280,6 +1280,22 @@ class ComputersPlugin(BasePlugin):
|
||||
'link': '/pcs/{computerid}',
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 'computers-sharedmachines',
|
||||
'title': 'Machine numbers on two PCs',
|
||||
'endpoint': '/api/computers/dashboard/sharedmachines',
|
||||
'render': 'exceptions',
|
||||
'severity': 'critical',
|
||||
'permission': 'computers.view',
|
||||
'empty': 'hide',
|
||||
'position': 15,
|
||||
'map': {
|
||||
'title': 'machinenumber',
|
||||
'detail': 'pcs',
|
||||
'meta': [{'key': 'pccount', 'suffix': ' PCs'}],
|
||||
'link': '/machines/{assetid}',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
def get_navigation_items(self) -> List[Dict]:
|
||||
|
||||
Reference in New Issue
Block a user