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)
|
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}
|
||||||
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