email sending implementation

This commit is contained in:
Grégoire Mureau 2025-07-05 16:05:46 +02:00
parent dbba37a407
commit 89997585c6
5 changed files with 194 additions and 19 deletions

View file

@ -0,0 +1,106 @@
import smtplib
import os
import json
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime
def send_error_report_email(error_reports):
"""
Envoie un email avec le rapport d'erreurs et le JSON intégré
"""
smtp_server = os.getenv("SMTP_SERVER")
smtp_port = int(os.getenv("SMTP_PORT", 587))
smtp_email = os.getenv("SMTP_EMAIL")
smtp_password = os.getenv("SMTP_PASSWORD")
smtp_from_alias = os.getenv("SMTP_FROM_ALIAS", smtp_email)
admin_email = os.getenv("ADMIN_EMAIL")
if not all([smtp_server, smtp_email, smtp_password, admin_email]):
print("❌ Configuration email manquante dans .env")
return False
try:
# Créer le message
msg = MIMEMultipart()
msg['From'] = f"Air Paradis Monitor <{smtp_from_alias}>"
msg['To'] = admin_email
msg['Subject'] = f"🚨 Rapport d'erreurs - Prédictions sentiments ({datetime.now().strftime('%d/%m/%Y %H:%M')})"
# Préparer les données JSON
json_data = {
"rapport_genere_le": datetime.now().isoformat(),
"nombre_signalements": len(error_reports),
"signalements": [
{
"id": i,
"tweet": tweet,
"prediction_incorrecte": prediction,
"longueur_tweet": len(tweet)
}
for i, (tweet, prediction) in enumerate(error_reports.items(), 1)
],
"metadata": {
"modele": "SentimentAnalysisLSTM",
"version_api": "1.0",
"seuil_rapport": 3
}
}
# Corps du message avec JSON intégré
body = f"""
Bonjour,
Un nouveau rapport d'erreurs a été généré pour l'API de prédiction de sentiments Air Paradis.
📊 Nombre total de signalements : {len(error_reports)}
📅 Date du rapport : {datetime.now().strftime('%d/%m/%Y à %H:%M')}
📋 Résumé des signalements :
"""
for i, (tweet, prediction) in enumerate(error_reports.items(), 1):
body += f"{i}. Tweet: \"{tweet[:100]}{'...' if len(tweet) > 100 else ''}\"\n"
body += f" Prédiction signalée comme incorrecte: {prediction.upper()}\n\n"
body += """
📈 Actions recommandées :
- Analyser les tweets signalés pour identifier des patterns
- Considérer un réentraînement du modèle si nécessaire
- Vérifier la qualité des données de training
📊 DONNÉES COMPLÈTES (JSON) :
═══════════════════════════════════════════════════════════════════════════════
"""
# ✅ Ajouter le JSON directement dans le corps
json_string = json.dumps(json_data, ensure_ascii=False, indent=2)
body += f"```json\n{json_string}\n```"
body += """
═══════════════════════════════════════════════════════════════════════════════
---
Cordialement,
🤖 API Air Paradis - Système de monitoring automatique
📧 Envoyé depuis : """ + smtp_from_alias
# Ajouter le corps du message
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# Envoyer l'email
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(smtp_email, smtp_password)
server.send_message(msg)
print(f"✅ Email de rapport envoyé avec succès depuis {smtp_from_alias}")
print(f"📊 JSON intégré dans le corps du message")
return True
except Exception as e:
print(f"❌ Erreur lors de l'envoi de l'email : {e}")
return False

View file

@ -14,6 +14,9 @@ PARENT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
sys.path.append(PARENT_DIR) sys.path.append(PARENT_DIR)
from functions import preprocess_tweet from functions import preprocess_tweet
# 📧 Import du service email
from email_service import send_error_report_email
# 🔐 Charger les variables d'environnement (.env à deux niveaux au-dessus) # 🔐 Charger les variables d'environnement (.env à deux niveaux au-dessus)
load_dotenv(dotenv_path=os.path.join(PARENT_DIR, ".env")) load_dotenv(dotenv_path=os.path.join(PARENT_DIR, ".env"))
@ -36,18 +39,20 @@ class ReportRequest(BaseModel):
tweet: str tweet: str
prediction: str prediction: str
# 🧠 Stockage des signalements d’erreurs # 🧠 Stockage des signalements d'erreurs
error_reports = {} error_reports = {}
# 🔍 Fonction pour charger modèle + tokenizer # 🔍 Fonction pour charger modèle + tokenizer
def load_model_and_artifacts(model_name=MODEL_NAME, stage=STAGE): def load_model_and_artifacts(model_name=MODEL_NAME, stage=STAGE):
client = MlflowClient() client = MlflowClient()
versions = client.get_latest_versions(model_name, [stage]) versions = client.get_latest_versions(model_name, [stage])
if not versions: if not versions:
raise RuntimeError(f"Aucune version du modèle '{model_name}' en stage '{stage}'") raise RuntimeError(f"Aucune version du modèle '{model_name}' en stage '{stage}'")
version_info = versions[0] version_info = versions[0]
run_id = version_info.run_id run_id = version_info.run_id
if not run_id: if not run_id:
raise RuntimeError("Run ID introuvable.") raise RuntimeError("Run ID introuvable.")
@ -58,7 +63,6 @@ def load_model_and_artifacts(model_name=MODEL_NAME, stage=STAGE):
# Déterminer les chemins des artefacts # Déterminer les chemins des artefacts
local_dir = "./downloaded_artifacts" local_dir = "./downloaded_artifacts"
os.makedirs(local_dir, exist_ok=True) os.makedirs(local_dir, exist_ok=True)
base_path = "local_artifacts" base_path = "local_artifacts"
tokenizer_file = "tokenizer.pickle" tokenizer_file = "tokenizer.pickle"
@ -134,8 +138,23 @@ def report_error(request: ReportRequest):
error_reports[tweet] = prediction error_reports[tweet] = prediction
report_sent = False report_sent = False
# Condition : à chaque multiple de 3 signalements totaux # Condition : à chaque multiple de 3 signalements totaux
if len(error_reports) % 3 == 0: if len(error_reports) % 3 == 0:
report_sent = True report_sent = True
# 📧 NOUVEAU : Envoyer l'email de rapport
try:
print(f"📧 Tentative d'envoi d'email pour {len(error_reports)} signalements...")
email_success = send_error_report_email(error_reports.copy())
if email_success:
print(f"✅ Email envoyé avec succès pour {len(error_reports)} signalements")
else:
print(f"❌ Échec de l'envoi de l'email pour {len(error_reports)} signalements")
except Exception as e:
print(f"❌ Erreur lors de l'envoi de l'email : {e}")
# On continue même si l'email échoue pour ne pas bloquer l'API
return {"report_sent": report_sent} return {"report_sent": report_sent}

View file

@ -0,0 +1,19 @@
# === API FastAPI ===
fastapi==0.115.14
uvicorn==0.35.0
pydantic==2.11.7
starlette==0.46.2
# === Interface Streamlit ===
streamlit==1.46.1
requests==2.32.4
# === Machine Learning ===
tensorflow==2.19.0
mlflow==3.1.1
numpy==2.1.3
pandas==2.3.0
# === Traitement de texte ===
nltk==3.9.1
# === Utilitaires ===
python-dotenv==1.1.1
# === Dépendances système ===
protobuf==3.20.3

View file

@ -0,0 +1,31 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jul 5 15:03:24 2025
@author: madfuckinman
"""
# test_alias_email.py
import os
from dotenv import load_dotenv
from email_service import send_error_report_email
load_dotenv()
print("🔧 Configuration Email:")
print(f"Serveur SMTP: {os.getenv('SMTP_SERVER')}")
print(f"Authentification: {os.getenv('SMTP_EMAIL')}")
print(f"Expéditeur (alias): {os.getenv('SMTP_FROM_ALIAS')}")
print(f"Destinataire: {os.getenv('ADMIN_EMAIL')}")
# Test
test_reports = {
"Service client décevant": "positif",
"J'adore cette compagnie": "negatif",
"Vol annulé sans prévenir": "positif"
}
print("\n📧 Test d'envoi depuis l'alias...")
result = send_error_report_email(test_reports)
print(f"Résultat : {'✅ Succès' if result else '❌ Échec'}")