100 lines
4.4 KiB
Python
100 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
path = "/var/www/html/business-kpi-dashboard.php"
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
c = f.read()
|
|
|
|
if "V73 FIX" in c:
|
|
print("ALREADY_FIXED")
|
|
exit(0)
|
|
|
|
# Replace the summary parsing logic with correct counting from catalog
|
|
old = """ const catalog = full.catalog || {};
|
|
const summary = (full.summary || {});
|
|
|
|
// ============= Chart 1: Status doughnut =============
|
|
const counts = {ok: summary.ok||0, warn: summary.warn||0, fail: summary.fail||0, wire_needed: summary.wire_needed||0};"""
|
|
|
|
new = """ const catalog = full.catalog || {};
|
|
// V73 FIX - action=full does NOT return summary, compute from catalog
|
|
let summary = full.summary || {};
|
|
if (!summary.ok) {
|
|
// Fetch summary separately as fallback
|
|
try {
|
|
const rS = await fetch("/api/wevia-v83-business-kpi.php?action=summary",{cache:"no-store"});
|
|
const sJ = await rS.json();
|
|
summary = sJ.summary || {};
|
|
} catch(e) { console.warn("[V71] summary fetch fail", e); }
|
|
}
|
|
// Always recompute from catalog (source of truth) if still missing
|
|
if (!summary.ok) {
|
|
summary = {ok:0, warn:0, fail:0, wire_needed:0, total_kpis:0, data_completeness_pct:0};
|
|
for (const c of Object.values(catalog)) {
|
|
for (const k of (c.kpis||[])) {
|
|
summary.total_kpis++;
|
|
if (k.status && summary[k.status] !== undefined) summary[k.status]++;
|
|
}
|
|
}
|
|
summary.data_completeness_pct = summary.total_kpis ? Math.round((summary.ok/summary.total_kpis)*1000)/10 : 0;
|
|
}
|
|
|
|
// ============= Chart 1: Status doughnut =============
|
|
const counts = {ok: summary.ok||0, warn: summary.warn||0, fail: summary.fail||0, wire_needed: summary.wire_needed||0};"""
|
|
|
|
if old in c:
|
|
c = c.replace(old, new, 1)
|
|
print("SUMMARY_FIX_APPLIED")
|
|
else:
|
|
print("OLD_PATTERN_NOT_FOUND - maybe already patched")
|
|
|
|
# Fix 2: Top 8 KPIs should show MIXED percentages, not all 100%
|
|
old_top = """ if (typeof k.value === \"number\" && typeof k.target === \"number\" && k.target > 0 && k.value > 0) {
|
|
allKpis.push({label: k.label, value: k.value, target: k.target, unit: k.unit, pct: Math.min(100, (k.value/k.target)*100)});
|
|
}"""
|
|
|
|
new_top = """ if (typeof k.value === \"number\" && typeof k.target === \"number\" && k.target > 0) {
|
|
// V73: show actual pct (can be <100 for below-target, cap at 150 visually for over-performers)
|
|
const rawPct = (k.value / k.target) * 100;
|
|
allKpis.push({label: k.label, value: k.value, target: k.target, unit: k.unit, pct: Math.min(150, rawPct), rawPct: Math.round(rawPct), status: k.status});
|
|
}"""
|
|
|
|
if old_top in c:
|
|
c = c.replace(old_top, new_top, 1)
|
|
print("TOP8_FIX_APPLIED")
|
|
|
|
# Update top8 sort - show mix (below-target first, then near-target)
|
|
old_sort = """allKpis.sort((a,b) => b.pct - a.pct);
|
|
const top8 = allKpis.slice(0, 8);
|
|
document.getElementById(\"v71-top-bars\").innerHTML = top8.map(k => `
|
|
<div class=\"v71-bar-row\">
|
|
<div class=\"l\" title=\"${k.label}\">${k.label.substring(0,22)}</div>
|
|
<div class=\"bar-wrap\"><div class=\"bar\" style=\"width:${k.pct}%\"></div></div>
|
|
<div class=\"v\">${Math.round(k.pct)}%</div>
|
|
</div>`).join(\"\");"""
|
|
|
|
new_sort = """// V73: show mix of below/on/above target for insight
|
|
allKpis.sort((a,b) => {
|
|
// Prioritize variance from 100 (show extremes)
|
|
return Math.abs(b.pct - 100) - Math.abs(a.pct - 100);
|
|
});
|
|
const top8 = allKpis.slice(0, 8);
|
|
const barColor = (pct, status) => {
|
|
if (status === \"fail\" || pct < 50) return \"linear-gradient(90deg,#fc8181,#f6ad55)\";
|
|
if (status === \"warn\" || pct < 90) return \"linear-gradient(90deg,#f6ad55,#ecc94b)\";
|
|
if (pct > 120) return \"linear-gradient(90deg,#48bb78,#6c9ef8)\";
|
|
return \"linear-gradient(90deg,#48bb78,#38b2ac)\";
|
|
};
|
|
document.getElementById(\"v71-top-bars\").innerHTML = top8.map(k => `
|
|
<div class=\"v71-bar-row\" title=\"${k.label}: ${k.value.toLocaleString(\"fr-FR\")} / target ${k.target.toLocaleString(\"fr-FR\")} ${k.unit}\">
|
|
<div class=\"l\">${k.label.substring(0,22)}</div>
|
|
<div class=\"bar-wrap\"><div class=\"bar\" style=\"width:${Math.min(100, k.pct)}%;background:${barColor(k.pct, k.status)}\"></div></div>
|
|
<div class=\"v\">${k.rawPct}%</div>
|
|
</div>`).join(\"\");"""
|
|
|
|
if old_sort in c:
|
|
c = c.replace(old_sort, new_sort, 1)
|
|
print("SORT_FIX_APPLIED")
|
|
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
f.write(c)
|
|
print(f"DONE - size {len(c)}")
|