Add watchdog for missed launchd runs, fix backup path bug + retention
Watchdog (scripts/utils/watchdog.py, com.lotto.watchdog.plist, daily 21:30) checks whether the latest draw or the trained models are older than the launchd schedule allows and sends a Telegram alert - this would have caught the Aug 15 update run that silently never fired (the Mac was presumably asleep; launchd does not retry missed StartCalendarInterval jobs), instead of that only surfacing on the next manual check. Also fixes a real path bug in LottoAPIUpdater.create_backup(): since self.data_file already points into data/, joining "data","backups" wrote backups to data/data/backups/ instead of data/backups/ - 73 backups (6.3MB) had been piling up there undetected for months, with data/data/ only ever gitignored rather than the root cause fixed. Merges the misplaced backups into the correct location and adds retention (keep last 30) so backups no longer grow unbounded; applied the same retention to Eurojackpot's updater in a matching commit there, which didn't have the path bug but had the same unbounded growth (85 backups, 2.4MB). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
427e704f3c
commit
612334dc1a
8 files changed
+178
-9899
No files matched your search
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Watchdog für die Lotto-Automatisierung.
|
||||
|
||||
Prüft die Aktualität von Ziehungsdaten und trainierten Modellen anhand des
|
||||
Ziehungskalenders (Mi + Sa) und meldet sich per Telegram, wenn etwas länger
|
||||
stillsteht als der launchd-Zeitplan erlaubt. Das fängt genau den Fall ab, in
|
||||
dem ein StartCalendarInterval-Job verpasst wurde (z.B. weil der Mac im Schlaf
|
||||
war) - launchd holt das selbst nicht nach, und ohne diesen Check fällt es
|
||||
sonst erst bei der nächsten manuellen Nachfrage auf.
|
||||
|
||||
Aufruf: python3 scripts/utils/watchdog.py
|
||||
Gedacht für einen täglichen launchd-Job (siehe com.lotto.watchdog.plist).
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from notifier import LottoNotifier
|
||||
|
||||
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
DATA_FILE = os.path.join(PROJECT_ROOT, "data", "AlleLottozahlen.csv")
|
||||
MODEL_FILE = os.path.join(PROJECT_ROOT, "data", "ultimate_ml_models", "trained_models.pkl")
|
||||
|
||||
# Lotto zieht Mi + Sa -> maximal 4 Tage zwischen zwei Ziehungen. 5 Tage
|
||||
# Toleranz lassen dem Update-Job (selber Abend, 20 Uhr) Luft, ohne echte
|
||||
# Ausfälle zu verpassen.
|
||||
MAX_DAYS_SINCE_DRAW = 5
|
||||
|
||||
|
||||
def check() -> list[str]:
|
||||
"""Prüft Daten- und Modell-Aktualität, gibt gefundene Probleme zurück."""
|
||||
problems = []
|
||||
|
||||
df = pd.read_csv(DATA_FILE, sep=';')
|
||||
last_draw = pd.to_datetime(df['datum'], format='%Y-%m-%d').max().date()
|
||||
days_since_draw = (datetime.now().date() - last_draw).days
|
||||
|
||||
if days_since_draw > MAX_DAYS_SINCE_DRAW:
|
||||
problems.append(
|
||||
f"Letzte Ziehung in den Daten ist {last_draw.isoformat()} "
|
||||
f"({days_since_draw} Tage her) - der automatische Update-Lauf "
|
||||
f"scheint ausgefallen zu sein."
|
||||
)
|
||||
|
||||
if os.path.exists(MODEL_FILE):
|
||||
model_date = datetime.fromtimestamp(os.path.getmtime(MODEL_FILE)).date()
|
||||
if model_date < last_draw and (datetime.now().date() - model_date).days > 1:
|
||||
problems.append(
|
||||
f"Modelle sind vom {model_date.isoformat()}, aber die neueste "
|
||||
f"Ziehung ({last_draw.isoformat()}) wurde noch nicht eingelernt."
|
||||
)
|
||||
else:
|
||||
problems.append("Modell-Datei trained_models.pkl fehlt komplett.")
|
||||
|
||||
return problems
|
||||
|
||||
|
||||
def main():
|
||||
problems = check()
|
||||
if not problems:
|
||||
print("✅ Watchdog: alles aktuell.")
|
||||
return
|
||||
|
||||
print("⚠️ Watchdog hat Probleme gefunden:")
|
||||
for p in problems:
|
||||
print(f" - {p}")
|
||||
|
||||
message = "🐶 *LOTTO WATCHDOG - PROBLEM ERKANNT*\n\n" + "\n".join(f"⚠️ {p}" for p in problems)
|
||||
LottoNotifier().send_alert(message)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user