monitor_health: giro orario dei sorveglianti da 3h con marcatore gia' detto (#30)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 15:11:10 +00:00
parent 841d4c78cd
commit d59aea7a0a
3 changed files with 262 additions and 3 deletions
+76 -3
View File
@@ -10,20 +10,91 @@ monitor e la soglia di copertura 0.80 (la stessa del veto d'integrita' di DVOLSP
uv run python scripts/live/monitor_health.py # report
uv run python scripts/live/monitor_health.py --quiet # stampa/allerta solo se scatta
uv run python scripts/live/monitor_health.py --solo-orari # (#30) solo i sorveglianti a
# max_age<=6h (cuscino_watch, venue_watch, collect_chain), notifica solo il NUOVO
# rispetto a data/live/monitor_health_detto.json — pensato per girare ogni ora
# (cron_cuscino.sh), non per sostituire il giro giornaliero completo.
"""
from __future__ import annotations
import json
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live.monitor_health import MIN_COVERAGE, alerts_from, check_all # noqa: E402
from src.live.monitor_health import ( # noqa: E402
MIN_COVERAGE,
alert_nuovi,
alerts_from,
check_all,
marcatore_aggiornato,
monitors_orari,
)
from src.live.notifier import notify # noqa: E402
# (#30) marcatore "gia' detto" del giro orario: {"detti": [<chiave alert>], "ts": ...}. Vive fuori
# da src/ perche' e' I/O, non parte della misura pura.
DETTO = ROOT / "data" / "live" / "monitor_health_detto.json"
def _carica_detti(path: Path = DETTO) -> set[str]:
if not path.exists():
return set()
try:
return set(json.loads(path.read_text()).get("detti", []))
except Exception:
return set()
def _salva_detti(detti: set[str], path: Path = DETTO) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(
{"detti": sorted(detti), "ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")},
indent=2))
def main_orari() -> int:
"""(#30) Giro orario: solo i sorveglianti a max_age<=6h, notifica solo il NUOVO rispetto al
marcatore. Exit 2 se c'e' un alert (anche gia' detto), 0 altrimenti — il giro giornaliero
resta l'unico posto dove "nessun alert nuovo" vuol dire davvero "tutto OK"."""
quiet = "--quiet" in sys.argv
rows = check_all(ROOT, monitors=monitors_orari())
alerts = alerts_from(rows)
detti = _carica_detti(DETTO) # DETTO letto qui, non nel default del parametro (monkeypatch)
nuovi = alert_nuovi(rows, detti)
aggiornato = marcatore_aggiornato(rows, detti)
if not quiet or alerts:
print("=" * 92)
print(" MONITOR HEALTH — giro orario (sorveglianti con max_age <= 6h)")
print("=" * 92)
for r in rows:
cov = f"{r['coverage']:.0%}" if r["coverage"] is not None else "-"
age = f"{r['age_h']:.0f}h" if r["age_h"] is not None else "-"
print(f" {r['name']:<20}{r['status']:>10}{age:>13}{cov:>10}")
inviato = True
if nuovi:
msgs = alerts_from(nuovi)
inviato = notify("🩺 MONITOR HEALTH — un sorvegliante orario non sta registrando",
{f"alert {i+1}": m for i, m in enumerate(msgs)})
for m in msgs:
print(f" ALERT (nuovo): {m}")
if not inviato:
print(" ⚠️ invio FALLITO — marcatore NON aggiornato: il prossimo giro ritenta")
if not nuovi or inviato:
_salva_detti(aggiornato, DETTO)
return 2 if alerts else 0
def main() -> int:
if "--solo-orari" in sys.argv:
return main_orari()
rows = check_all(ROOT)
alerts = alerts_from(rows)
@@ -55,15 +126,17 @@ def main() -> int:
return 0
USO = """uso: monitor_health.py [--quiet]
USO = """uso: monitor_health.py [--quiet] [--solo-orari]
monitor_health.py — i forward-monitor stanno registrando? Riporta e allerta, non tocca nulla.
uv run python scripts/live/monitor_health.py # report
uv run python scripts/live/monitor_health.py --quiet # stampa/allerta solo se scatta
uv run python scripts/live/monitor_health.py --solo-orari # (#30) solo i sorveglianti a
max_age<=6h, notifica solo il NUOVO rispetto al marcatore
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("monitor_health.py", USO, flag=("--quiet",), con_valore=())
valida("monitor_health.py", USO, flag=("--quiet", "--solo-orari"), con_valore=())
raise SystemExit(main())