#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
metrics_alerts.py - Alertes Telegram + Email pour les métriques de performance
Usage:
python3 metrics_alerts.py --daily # Alerte quotidienne (si notable)
python3 metrics_alerts.py --weekly # Rapport hebdomadaire
python3 metrics_alerts.py --test # Test formatage
python3 metrics_alerts.py --daily --email # Alerte + email si ROI > 1.0€
python3 metrics_alerts.py --weekly --email # Rapport hebdo par email
"""
import sqlite3
import argparse
import requests
from datetime import datetime, timedelta
from pathlib import Path
DB_PATH = "/home/h3r7/turf_scraper/turf.db"
TELEGRAM_DIR = "/home/h3r7/turf_scraper"
# Email alert configuration (via combined_api Resend endpoint on port 8765)
COMBINED_API_URL = "http://localhost:8765"
ALERT_EMAIL_TO = "ronanyves26@gmail.com" # destinataire des alertes email
# Seuil ROI pour déclencher une alerte email automatique (euros par mise)
ROI_ALERT_THRESHOLD = 1.0
# =============================================================================
# FONCTIONS UTILITAIRES
# =============================================================================
def get_db():
"""Connexion a la base de donnees"""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def save_telegram_message(message, filename):
"""Sauvegarde le message Telegram dans un fichier"""
filepath = Path(TELEGRAM_DIR) / filename
with open(filepath, 'w', encoding='utf-8') as f:
f.write(message)
print("Message Telegram sauvegarde: {}".format(filepath))
return filepath
def send_email_alert(subject, message_text):
"""
Envoie une alerte email via le endpoint /api/send-email de combined_api (port 8765).
Convertit le message texte en HTML style.
Retourne True si envoi reussi, False sinon.
"""
# Convertir le texte brut en HTML lisible
html_lines = []
for line in message_text.splitlines():
stripped = line.strip()
if not stripped:
html_lines.append("
")
else:
# Echapper les caracteres HTML basiques
escaped = stripped.replace("&", "&").replace("<", "<").replace(">", ">")
html_lines.append(
"
{}
".format(escaped)
)
html_body = """
H3R7Tech — Alerte Performance Turf
{}
Alerte automatique generee par metrics_alerts.py — H3R7Tech
Dashboard: Turf Dashboard
""".format("".join(html_lines))
try:
resp = requests.post(
"{}/api/send-email".format(COMBINED_API_URL),
json={
"to": ALERT_EMAIL_TO,
"subject": subject,
"html": html_body,
},
timeout=15,
)
if resp.status_code in (200, 201):
result = resp.json()
print("Email envoye a {} — id: {}".format(ALERT_EMAIL_TO, result.get("id", "?")))
return True
else:
try:
data = resp.json()
error_msg = data.get("error", data.get("details", resp.text[:300]))
except Exception:
error_msg = resp.text[:300]
print("Erreur Resend ({}): {}".format(resp.status_code, error_msg))
return False
except Exception as e:
print("Impossible d'envoyer l'email: {}".format(e))
return False
# =============================================================================
# ALERTE QUOTIDIENNE
# =============================================================================
def check_daily_alerts(date_str):
"""Verifie les evenements notables du jour. Retourne (message, has_roi_alert) ou None."""
conn = get_db()
# Recuperer les metriques du jour
metrics = conn.execute("""
SELECT
source,
SUM(nb_predictions) as total_pred,
SUM(nb_gagnants) as total_gagn,
SUM(nb_places) as total_place,
SUM(nb_top5) as total_top5,
AVG(taux_gagnant) as taux_g,
AVG(taux_place) as taux_p,
AVG(roi_sg_net) as roi_sg,
SUM(quinte_5sur5) as q5
FROM prediction_metrics
WHERE date = ?
GROUP BY source
""", (date_str,)).fetchall()
if not metrics:
print("Aucune metrique pour {}".format(date_str))
return None
# Verifier les criteres d'alerte
alerts = []
has_roi_alert = False
for m in metrics:
# Quinte 5/5
if m['q5'] and m['q5'] > 0:
alerts.append("QUINTE 5/5 {}: Quinte 5/5 trouve!".format(m['source']))
# ROI exceptionnel (> seuil ROI_ALERT_THRESHOLD)
if m['roi_sg'] and m['roi_sg'] > ROI_ALERT_THRESHOLD:
alerts.append("ROI ELEVE {}: ROI SG +{:.2f}€/mise".format(m['source'], m['roi_sg']))
has_roi_alert = True
# Taux place excellent
if m['taux_p'] and m['taux_p'] > 70:
alerts.append("TAUX PLACE {}: {:.1f}% place (excellent)".format(m['source'], m['taux_p']))
if not alerts:
print("Aucun evenement notable aujourd'hui")
return None
# Formater le message
date_fmt = datetime.strptime(date_str, '%Y-%m-%d').strftime('%d/%m/%Y')
message = "ALERTE PERFORMANCE\nDate: {}\n{}\n\n{}\n\nDetails: Dashboard Turf".format(
date_fmt,
"=" * 30,
"\n".join(alerts)
)
conn.close()
return message, has_roi_alert
# =============================================================================
# RAPPORT HEBDOMADAIRE
# =============================================================================
def generate_weekly_report():
"""Genere le rapport hebdomadaire de performance. Retourne (message, total_roi_sg)."""
conn = get_db()
# Dates de la semaine
today = datetime.now()
start_date = (today - timedelta(days=7)).strftime('%Y-%m-%d')
end_date = today.strftime('%Y-%m-%d')
# Periode precedente pour comparaison
prev_start = (today - timedelta(days=14)).strftime('%Y-%m-%d')
prev_end = start_date
# Metriques de la semaine
current_metrics = conn.execute("""
SELECT
source,
COUNT(*) as nb_courses,
SUM(nb_predictions) as total_pred,
SUM(nb_gagnants) as total_gagn,
SUM(nb_places) as total_place,
ROUND(AVG(taux_gagnant), 1) as taux_g,
ROUND(AVG(taux_place), 1) as taux_p,
ROUND(SUM(roi_sg_net), 3) as roi_sg_cumul,
ROUND(SUM(roi_sp_net), 3) as roi_sp_cumul,
SUM(quinte_5sur5) as q5,
SUM(quinte_4sur5) as q4
FROM prediction_metrics
WHERE date BETWEEN ? AND ?
GROUP BY source
ORDER BY taux_p DESC
""", (start_date, end_date)).fetchall()
# Metriques semaine precedente
prev_metrics = conn.execute("""
SELECT
source,
ROUND(AVG(taux_gagnant), 1) as taux_g,
ROUND(AVG(taux_place), 1) as taux_p
FROM prediction_metrics
WHERE date BETWEEN ? AND ?
GROUP BY source
""", (prev_start, prev_end)).fetchall()
prev_dict = {m['source']: m for m in prev_metrics}
# Formater le message
start_fmt = datetime.strptime(start_date, '%Y-%m-%d').strftime('%d/%m')
end_fmt = datetime.strptime(end_date, '%Y-%m-%d').strftime('%d/%m')
message = "RAPPORT PERFORMANCE\nSemaine {} - {}\n{}\n\n".format(
start_fmt, end_fmt, "=" * 30
)
# Bilan global
total_roi_sg = sum(m['roi_sg_cumul'] or 0 for m in current_metrics)
total_roi_sp = sum(m['roi_sp_cumul'] or 0 for m in current_metrics)
total_q5 = sum(m['q5'] or 0 for m in current_metrics)
total_q4 = sum(m['q4'] or 0 for m in current_metrics)
# Detail par source
for m in current_metrics:
source_name = m['source'].replace('canalturf_', '').replace('_', ' ').title()
# Comparaison avec semaine precedente
prev = prev_dict.get(m['source'])
diff_g = 0
diff_p = 0
if prev:
diff_g = m['taux_g'] - prev['taux_g']
diff_p = m['taux_p'] - prev['taux_p']
arrow_g = 'hausse' if diff_g > 0 else 'baisse' if diff_g < 0 else 'stable'
arrow_p = 'hausse' if diff_p > 0 else 'baisse' if diff_p < 0 else 'stable'
message += "{}\n Courses: {}\n Taux gagnant: {}% ({})\n Taux place: {}% ({})\n ROI SG: {:+.2f}€\n ROI SP: {:+.2f}€\n\n".format(
source_name,
m['nb_courses'],
m['taux_g'], arrow_g,
m['taux_p'], arrow_p,
m['roi_sg_cumul'],
m['roi_sp_cumul']
)
# Bilan global
bilan = "Semaine positive" if total_roi_sg > 0 else "Semaine negative"
message += "{}\nQuinte: 5/5 = {} courses | 4/5 = {} courses\n\nBilan global:\n ROI SG cumule: {:+.2f}€\n ROI SP cumule: {:+.2f}€\n\n{}\n{}".format(
"=" * 30,
total_q5, total_q4,
total_roi_sg, total_roi_sp,
bilan, "=" * 30
)
conn.close()
return message, total_roi_sg
# =============================================================================
# POINT D'ENTREE
# =============================================================================
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Alertes metriques de performance")
parser.add_argument("--daily", "-d", action="store_true", help="Alerte quotidienne")
parser.add_argument("--weekly", "-w", action="store_true", help="Rapport hebdomadaire")
parser.add_argument("--test", "-t", action="store_true", help="Test formatage")
parser.add_argument(
"--email", "-e", action="store_true",
help="Envoyer par email (auto si ROI > {:.1f}€)".format(ROI_ALERT_THRESHOLD)
)
parser.add_argument("--date", help="Date YYYY-MM-DD (defaut: hier)")
args = parser.parse_args()
if args.test:
date_str = args.date or (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
result = check_daily_alerts(date_str)
if result:
msg, has_roi = result
print(msg)
if args.email:
print("\n--- Test envoi email ---")
send_email_alert("[TEST] Alerte Performance Turf — {}".format(date_str), msg)
result_w = generate_weekly_report()
if result_w:
msg_w, roi_w = result_w
print(msg_w)
elif args.daily:
date_str = args.date or (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
result = check_daily_alerts(date_str)
if result:
msg, has_roi = result
filename = "telegram_metrics_daily_{}.txt".format(date_str.replace('-', ''))
save_telegram_message(msg, filename)
# Envoi email si ROI > seuil (automatique) OU si flag --email passe
if args.email or has_roi:
date_fmt = datetime.strptime(date_str, '%Y-%m-%d').strftime('%d/%m/%Y')
subject = "Alerte Turf — ROI exceptionnel {}".format(date_fmt)
sent = send_email_alert(subject, msg)
if not sent:
print("Email non envoye (voir logs ci-dessus)")
elif args.weekly:
result = generate_weekly_report()
if result:
msg, total_roi = result
filename = "telegram_metrics_weekly_{}.txt".format(datetime.now().strftime('%Y%m%d'))
save_telegram_message(msg, filename)
# Envoi email du rapport hebdo si --email active
if args.email:
start_fmt = (datetime.now() - timedelta(days=7)).strftime('%d/%m')
end_fmt = datetime.now().strftime('%d/%m')
roi_status = "positif" if total_roi > 0 else "negatif"
subject = "Rapport Hebdo Turf {}-{} — ROI {} ({:+.2f}€)".format(
start_fmt, end_fmt, roi_status, total_roi
)
sent = send_email_alert(subject, msg)
if not sent:
print("Email non envoye (voir logs ci-dessus)")
else:
parser.print_help()