#!/usr/bin/env python3 """ Watchdog für die Eurojackpot-Automatisierung. Prüft die Aktualität von Ziehungsdaten und trainierten Modellen anhand des Ziehungskalenders (Di + Fr) 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. Analog zum gleichnamigen Watchdog im Schwesterprojekt Lotto. Aufruf: python3 scripts/utils/watchdog.py Gedacht für einen täglichen launchd-Job (siehe com.eurojackpot.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 EurojackpotNotifier PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) DATA_FILE = os.path.join(PROJECT_ROOT, "data", "AlleEurojackpotzahlen.csv") MODEL_FILE = os.path.join(PROJECT_ROOT, "data", "eurojackpot_ml_models", "trained_models_main.pkl") # Eurojackpot zieht Di + Fr -> maximal 4 Tage zwischen zwei Ziehungen. 5 Tage # Toleranz lassen dem Update-Job (nächster Morgen, 08 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_main.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}") EurojackpotNotifier().send_error("\n".join(problems), context="Watchdog") if __name__ == "__main__": main()