Files
Lotto-Tip-Generator/scripts/utils/verify_draws.py
T

402 lines
13 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Ziehungs-Verifizierer für Lotto 6aus49 und Eurojackpot
Verifiziert Ziehungen gegen offizielle Datenquellen:
1. GitHub Lotto Archive (für Lotto 6aus49)
2. Eurojackpot-zahlen.eu (für Eurojackpot)
Prüft:
- Vollständigkeit (fehlende Ziehungen)
- Korrektheit (falsche Zahlen)
- Duplikate
"""
import pandas as pd
import requests
from datetime import datetime, timedelta
from typing import Dict, List, Tuple, Optional
import sys
import os
class DrawVerifier:
"""Verifiziert Ziehungen gegen offizielle Quellen."""
def __init__(self, csv_file: str, lottery_type: str):
self.csv_file = csv_file
self.lottery_type = lottery_type
self.df_local = None
self.df_official = None
self.errors = []
self.warnings = []
self.info = []
def load_local_data(self) -> bool:
"""Lädt lokale CSV-Datei."""
try:
if not os.path.exists(self.csv_file):
self.errors.append(f"❌ Datei existiert nicht: {self.csv_file}")
return False
self.df_local = pd.read_csv(self.csv_file, sep=';')
self.df_local['datum'] = pd.to_datetime(
self.df_local['datum'],
format='%Y-%m-%d',
errors='coerce'
)
self.info.append(f"✅ Lokale Daten: {len(self.df_local)} Ziehungen")
return True
except Exception as e:
self.errors.append(f"❌ Fehler beim Laden: {e}")
return False
def fetch_official_lotto_data(self) -> bool:
"""Holt offizielle Lotto 6aus49 Daten von GitHub Archive."""
try:
url = 'https://johannesfriedrich.github.io/LottoNumberArchive/Lottonumbers_tidy_complete.json'
print("🌐 Lade offizielle Lotto-Daten von GitHub Archive...")
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
data = response.json()
# Parse gruppierte Daten
draws_by_id = {}
for entry in data:
draw_id = entry.get('id')
if draw_id not in draws_by_id:
draws_by_id[draw_id] = {
'date': entry.get('date'),
'numbers': [],
'superzahl': None
}
variable = entry.get('variable')
value = entry.get('value')
if variable == 'Lottozahl':
draws_by_id[draw_id]['numbers'].append(value)
elif variable == 'Superzahl':
draws_by_id[draw_id]['superzahl'] = value
# Konvertiere zu DataFrame
official_draws = []
for draw_id, draw_data in draws_by_id.items():
try:
date_str = draw_data['date']
day, month, year = date_str.split('.')
date_obj = datetime(int(year), int(month), int(day))
numbers = sorted(draw_data['numbers'])
if len(numbers) == 6:
official_draws.append({
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'Z6': numbers[5],
'SZ': draw_data['superzahl']
})
except Exception:
continue
self.df_official = pd.DataFrame(official_draws)
self.info.append(f"✅ Offizielle Daten: {len(self.df_official)} Ziehungen")
return True
except Exception as e:
self.errors.append(f"❌ Fehler beim Laden offizieller Daten: {e}")
return False
def fetch_official_eurojackpot_data(self) -> bool:
"""
Holt offizielle Eurojackpot Daten.
Note: Da es keine vollständige öffentliche API gibt,
beschränken wir uns auf Plausibilitätsprüfungen.
"""
self.warnings.append("⚠️ Keine vollständige offizielle Eurojackpot-API verfügbar")
self.warnings.append(" Nur Plausibilitätsprüfungen möglich")
# Erstelle minimale "offizielle" Daten basierend auf erwarteten Ziehungsterminen
# Dies ist nur für Vollständigkeitsprüfung
if len(self.df_local) > 0:
start_date = self.df_local['datum'].min()
end_date = datetime.now()
expected_dates = []
current = start_date
while current <= end_date:
# Eurojackpot: Dienstag (1) und Freitag (4)
if current.weekday() in [1, 4]:
expected_dates.append(current)
current += timedelta(days=1)
self.df_official = pd.DataFrame({'datum': expected_dates})
self.info.append(f"️ Erwartete Ziehungstermine: {len(expected_dates)}")
return True
def compare_completeness(self) -> List[datetime]:
"""Prüft auf fehlende Ziehungen."""
if self.df_official is None or self.df_local is None:
return []
official_dates = set(self.df_official['datum'].dt.date)
local_dates = set(self.df_local['datum'].dt.date)
missing = official_dates - local_dates
extra = local_dates - official_dates
if missing:
self.errors.append(f"❌ {len(missing)} fehlende Ziehung(en):")
for date in sorted(missing)[:10]:
self.errors.append(f" {date.strftime('%Y-%m-%d')}")
if len(missing) > 10:
self.errors.append(f" ... und {len(missing) - 10} weitere")
if extra:
self.warnings.append(f"⚠️ {len(extra)} zusätzliche Ziehung(en) (nicht in offiziellen Daten):")
for date in sorted(extra)[:5]:
self.warnings.append(f" {date.strftime('%Y-%m-%d')}")
if len(extra) > 5:
self.warnings.append(f" ... und {len(extra) - 5} weitere")
return list(missing)
def compare_accuracy(self) -> int:
"""Vergleicht Zahlenwerte mit offiziellen Daten."""
if self.df_official is None or self.df_local is None:
return 0
if self.lottery_type == 'eurojackpot':
# Keine detaillierten offiziellen Daten verfügbar
return 0
mismatches = 0
# Merge auf Datum
merged = pd.merge(
self.df_local,
self.df_official,
on='datum',
suffixes=('_local', '_official'),
how='inner'
)
if self.lottery_type == 'lotto':
cols_to_check = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'Z6', 'SZ']
else: # eurojackpot
cols_to_check = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
for idx, row in merged.iterrows():
mismatch_cols = []
for col in cols_to_check:
local_col = f"{col}_local"
official_col = f"{col}_official"
if local_col in row and official_col in row:
# Vergleiche nur wenn beide Werte vorhanden
if pd.notna(row[local_col]) and pd.notna(row[official_col]):
if row[local_col] != row[official_col]:
mismatch_cols.append(
f"{col}: {int(row[local_col])}{int(row[official_col])}"
)
if mismatch_cols:
mismatches += 1
if mismatches == 1:
self.errors.append("❌ Zahlen-Abweichungen gefunden:")
if mismatches <= 10:
date_str = row['datum'].strftime('%Y-%m-%d')
self.errors.append(f" {date_str}: {', '.join(mismatch_cols)}")
if mismatches > 10:
self.errors.append(f" ... und {mismatches - 10} weitere Abweichungen")
return mismatches
def check_recent_draws(self, days: int = 30) -> None:
"""Prüft besonders die letzten N Tage."""
if self.df_local is None:
return
cutoff = datetime.now() - timedelta(days=days)
recent = self.df_local[self.df_local['datum'] >= cutoff]
self.info.append(f"️ Letzte {days} Tage: {len(recent)} Ziehungen")
if len(recent) == 0:
self.warnings.append(f"⚠️ Keine Ziehungen in den letzten {days} Tagen!")
# Erwartete Anzahl berechnen
if self.lottery_type == 'lotto':
# 2x pro Woche
expected = (days / 7) * 2
else: # eurojackpot
# 2x pro Woche
expected = (days / 7) * 2
if len(recent) < expected * 0.8: # Toleranz 20%
self.warnings.append(
f"⚠️ Weniger Ziehungen als erwartet: {len(recent)} vs. ~{int(expected)}"
)
def verify_all(self) -> bool:
"""Führt komplette Verifikation durch."""
print(f"\n{'='*70}")
print(f" ZIEHUNGS-VERIFIZIERER")
print(f" Typ: {self.lottery_type.upper()}")
print(f"{'='*70}")
print(f"\n📁 Datei: {os.path.basename(self.csv_file)}")
# Lade lokale Daten
if not self.load_local_data():
return False
# Lade offizielle Daten
print()
if self.lottery_type == 'lotto':
if not self.fetch_official_lotto_data():
return False
else: # eurojackpot
if not self.fetch_official_eurojackpot_data():
return False
print(f"\n🔍 VERIFIKATION")
print("="*70)
# Prüfungen
print("Prüfe Vollständigkeit...")
missing = self.compare_completeness()
if self.lottery_type == 'lotto':
print("Prüfe Zahlenwerte...")
mismatches = self.compare_accuracy()
print("Prüfe aktuelle Ziehungen...")
self.check_recent_draws(30)
# Statistiken
print(f"\n📊 STATISTIKEN")
print("="*70)
for info in self.info:
print(info)
# Zusammenfassung
if self.df_local is not None and self.df_official is not None:
if self.lottery_type == 'lotto':
overlap = len(pd.merge(
self.df_local,
self.df_official,
on='datum',
how='inner'
))
if overlap > 0:
print(f"\n{overlap} Ziehungen in beiden Quellen")
# Genauigkeit
if self.lottery_type == 'lotto':
accuracy = ((overlap - (mismatches if 'mismatches' in locals() else 0)) / overlap * 100)
print(f"✅ Genauigkeit: {accuracy:.1f}%")
# Fehler
if self.errors:
print(f"\n❌ FEHLER ({len(self.errors)})")
print("="*70)
for error in self.errors:
print(error)
# Warnungen
if self.warnings:
print(f"\n⚠️ WARNUNGEN ({len(self.warnings)})")
print("="*70)
for warning in self.warnings:
print(warning)
# Ergebnis
print(f"\n{'='*70}")
if not self.errors:
if self.warnings:
print("✅ VERIFIKATION OK - Nur Warnungen")
else:
print("✅ VERIFIKATION ERFOLGREICH - Alle Ziehungen korrekt!")
else:
print("❌ VERIFIKATION FEHLGESCHLAGEN - Fehler gefunden")
print("="*70)
return len(self.errors) == 0
def main():
"""Hauptfunktion."""
# Standard-Dateien
files = {
'lotto': {
'path': '/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Lotto/data/AlleLottozahlen.csv',
'type': 'lotto'
},
'eurojackpot': {
'path': '/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleEurojackpotzahlen.csv',
'type': 'eurojackpot'
}
}
if len(sys.argv) > 1:
# Einzelne Datei
csv_file = sys.argv[1]
lottery_type = sys.argv[2] if len(sys.argv) > 2 else 'lotto'
verifier = DrawVerifier(csv_file, lottery_type)
success = verifier.verify_all()
sys.exit(0 if success else 1)
else:
# Beide Dateien
print("\n🎲 Verifiziere beide Lottery-Dateien...\n")
results = {}
for name, config in files.items():
if os.path.exists(config['path']):
verifier = DrawVerifier(config['path'], config['type'])
results[name] = verifier.verify_all()
else:
print(f"\n⚠️ {name.upper()}: Datei nicht gefunden")
results[name] = False
# Gesamtergebnis
print("\n" + "="*70)
print(" GESAMTERGEBNIS")
print("="*70)
for name, success in results.items():
status = "✅" if success else "❌"
print(f"{status} {name.upper()}")
print("="*70)
all_success = all(results.values())
sys.exit(0 if all_success else 1)
if __name__ == "__main__":
main()