Fix double-counting in VCL multi-vertical stats — use only ALL: rollup rows

The Summary sheet in each vertical spreadsheet contains both sub-team rows
(ACCESS-OPS, STEAM, INTELDEV, etc.) AND a rollup row (ALL: NTS-AEO) per
metric. The rollup row already includes all sub-team totals, so summing
all rows was double-counting every device.

Fixed in three places:
- GET /stats endpoint: added AND team LIKE 'ALL:%' filter
- persistMultiVerticalUpload snapshot creation: only sum ALL: entries
- GET /vertical/:code/metrics category aggregation: only use ALL: rows

Also ran a one-time data fix to correct existing compliance_snapshots.
This commit is contained in:
Jordan Ramos
2026-05-14 12:09:44 -06:00
parent 55238ec71e
commit ebaf4cd18c

View File

@@ -141,15 +141,20 @@ async function persistMultiVerticalUpload({ items, summary, reportDate, filename
}
// 6. Create/update compliance_snapshots for this vertical
// Use summary data for accurate totals (compliance_items only has non-compliant devices)
// Use summary data for accurate totals (compliance_items only has non-compliant devices).
// IMPORTANT: Only use "ALL:" rollup rows to avoid double-counting. Each Summary sheet
// has sub-team rows AND a rollup row per metric — the rollup already includes sub-teams.
const currentMonth = new Date().toISOString().slice(0, 7);
let totalDevices = 0, snapshotCompliant = 0, snapshotNonCompliant = 0;
if (summary && summary.entries && summary.entries.length > 0) {
for (const entry of summary.entries) {
totalDevices += entry.total || 0;
snapshotCompliant += entry.compliant || 0;
snapshotNonCompliant += entry.non_compliant || 0;
// Only count rollup rows (team starts with "ALL:") to avoid double-counting
if (entry.team && entry.team.startsWith('ALL:')) {
totalDevices += entry.total || 0;
snapshotCompliant += entry.compliant || 0;
snapshotNonCompliant += entry.non_compliant || 0;
}
}
}
@@ -460,16 +465,18 @@ function createVCLMultiVerticalRouter(upload) {
const latestUploadIds = latestUploads.map(u => u.id);
// Aggregate summary data from the latest upload per vertical
// Each row in vcl_multi_vertical_summary is one metric for one vertical.
// We sum across metrics per vertical to get vertical-level totals.
// Aggregate summary data from the latest upload per vertical.
// IMPORTANT: Only use "ALL:" rollup rows to avoid double-counting.
// Each spreadsheet's Summary sheet contains both sub-team rows (ACCESS-OPS,
// STEAM, etc.) AND a rollup row (ALL: NTS-AEO) per metric. The rollup row
// already includes all sub-team totals, so summing all rows would double-count.
const { rows: verticalSummary } = await pool.query(`
SELECT vertical,
SUM(total)::int AS total_devices,
SUM(compliant)::int AS compliant,
SUM(non_compliant)::int AS non_compliant
FROM vcl_multi_vertical_summary
WHERE upload_id = ANY($1)
WHERE upload_id = ANY($1) AND team LIKE 'ALL:%'
GROUP BY vertical
ORDER BY vertical
`, [latestUploadIds]);
@@ -702,9 +709,10 @@ function createVCLMultiVerticalRouter(upload) {
[uploadId, vertical]
);
// Aggregate by category
// Aggregate by category — only use "ALL:" rollup rows to avoid double-counting
const categoryMap = {};
for (const m of metrics) {
if (!m.team || !m.team.startsWith('ALL:')) continue;
const cat = m.category || 'Other';
if (!categoryMap[cat]) categoryMap[cat] = { category: cat, non_compliant: 0, compliant: 0, total: 0 };
categoryMap[cat].non_compliant += m.non_compliant;