Merge the wilm delivery work into main

Ben Baker's installed-software collection, software delivery (run_script and
update_app), agentless adoption tool and command-queue hardening, with his
authorship preserved.

Reviewed before promotion. Fixed: the endpoints plugin tests were never collected
by pytest, the SBOM snapshot table's key would exhaust on MySQL, ingest did not
truncate two columns strict MySQL would reject, and update_app's version branch
pinned a digest for a file that route never serves. Agent packaging moved off
require_role('admin') onto a new endpoints.agentpackage permission.

Contract 0.27.0. Full suite 2231, frontend 314.
This commit is contained in:
2026-08-30 19:41:15 -04:00
parent 8f4fe74dcb
commit 6680c3c5de
59 changed files with 5557 additions and 68 deletions

View File

@@ -38,6 +38,19 @@ def _count_by_type(assettype):
return query.count()
def _inactive_excluded():
"""Counted-type assets held back by isactive, i.e. soft-deleted.
Reported so the page can account for itself. A total that silently drops
rows is the reason somebody compares it with a list page and finds a
difference they cannot explain.
"""
return (db.session.query(Asset).join(AssetType)
.filter(Asset.isactive == False,
AssetType.assettype.in_(COUNTEDTYPES))
.count())
COUNTEDTYPES = ('machine', 'computer', 'network_device', 'printer',
'measuring_tool')
@@ -69,6 +82,10 @@ def _countedassets():
@require_permission('assets.view')
def get_dashboard():
"""Get dashboard summary data (asset-based)."""
# Imported here, as in the helpers above: the dualpath service reaches back
# into models, so a module-level import is circular.
from shopdb.core.services.dualpath import dualpath_single_machine_enabled
machine_count = _count_by_type('machine')
pc_count = _count_by_type('computer')
network_count = _count_by_type('network_device')
@@ -99,6 +116,22 @@ def get_dashboard():
'totalmeasuringtool': measuringtool_count,
'activeassets': status_dict.get('In Use', 0),
'inrepair': status_dict.get('In Repair', 0),
# What the totals actually cover, so the page can say so rather than
# leaving a reader to work it out from a number that surprised them.
# Every count here filters isactive, and the dual-path collapse is a
# per-site toggle - asserting it unconditionally would be false at a
# site that has it off.
'countsactiveonly': True,
'dualpathcollapsed': bool(dualpath_single_machine_enabled()),
# Every status, not just the two that had callers. The counts were
# already built for the rollup above and then thrown away, so a reader
# could see "In Use" and had no way to learn where the rest went.
'statuses': status_dict,
# A DIFFERENT thing to a status, and the one that makes a total
# disagree with a list page. isactive is the soft-delete flag: these
# rows are excluded from every count above, so they are in no status
# bucket and cannot be found by adding the buckets up.
'inactiveexcluded': _inactive_excluded(),
# Structured data
'counts': {
'machines': machine_count,