email sending implementation
This commit is contained in:
parent
dbba37a407
commit
89997585c6
5 changed files with 194 additions and 19 deletions
Binary file not shown.
106
deployment/api/email_service.py
Normal file
106
deployment/api/email_service.py
Normal 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
|
||||
|
|
@ -14,6 +14,9 @@ PARENT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
|
|||
sys.path.append(PARENT_DIR)
|
||||
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)
|
||||
load_dotenv(dotenv_path=os.path.join(PARENT_DIR, ".env"))
|
||||
|
||||
|
|
@ -36,18 +39,20 @@ class ReportRequest(BaseModel):
|
|||
tweet: str
|
||||
prediction: str
|
||||
|
||||
# 🧠 Stockage des signalements d’erreurs
|
||||
# 🧠 Stockage des signalements d'erreurs
|
||||
error_reports = {}
|
||||
|
||||
# 🔍 Fonction pour charger modèle + tokenizer
|
||||
def load_model_and_artifacts(model_name=MODEL_NAME, stage=STAGE):
|
||||
client = MlflowClient()
|
||||
versions = client.get_latest_versions(model_name, [stage])
|
||||
|
||||
if not versions:
|
||||
raise RuntimeError(f"Aucune version du modèle '{model_name}' en stage '{stage}'")
|
||||
|
||||
version_info = versions[0]
|
||||
run_id = version_info.run_id
|
||||
|
||||
if not run_id:
|
||||
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
|
||||
local_dir = "./downloaded_artifacts"
|
||||
os.makedirs(local_dir, exist_ok=True)
|
||||
|
||||
base_path = "local_artifacts"
|
||||
tokenizer_file = "tokenizer.pickle"
|
||||
|
||||
|
|
@ -134,8 +138,23 @@ def report_error(request: ReportRequest):
|
|||
error_reports[tweet] = prediction
|
||||
|
||||
report_sent = False
|
||||
|
||||
# Condition : à chaque multiple de 3 signalements totaux
|
||||
if len(error_reports) % 3 == 0:
|
||||
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}
|
||||
19
deployment/api/requirements.txt
Normal file
19
deployment/api/requirements.txt
Normal 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
|
||||
31
deployment/api/test_alias_email.py
Normal file
31
deployment/api/test_alias_email.py
Normal 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'}")
|
||||
Loading…
Add table
Reference in a new issue