|
|
""" |
|
|
Servicio de notificaciones por email para validación interna |
|
|
|
|
|
Este módulo gestiona el envío de correos a los validadores internos |
|
|
con la información del vídeo para su revisión regulatoria. |
|
|
""" |
|
|
|
|
|
import os |
|
|
import json |
|
|
import base64 |
|
|
from typing import Dict, Any, List, Optional |
|
|
from datetime import datetime |
|
|
from dataclasses import dataclass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass |
|
|
class ValidationRequest: |
|
|
"""Solicitud de validación para envío por email""" |
|
|
document_id: str |
|
|
user_email: str |
|
|
user_name: str |
|
|
video_title: str |
|
|
video_hash: str |
|
|
timestamp: str |
|
|
video_url: str |
|
|
consent_data: Dict[str, Any] |
|
|
|
|
|
class EmailNotificationService: |
|
|
"""Servicio de notificaciones por email para validadores""" |
|
|
|
|
|
def __init__(self): |
|
|
|
|
|
self.smtp_server = os.getenv("SMTP_SERVER", "smtp.gmail.com") |
|
|
self.smtp_port = int(os.getenv("SMTP_PORT", "587")) |
|
|
self.sender_email = os.getenv("SENDER_EMAIL", "[email protected]") |
|
|
self.sender_password = os.getenv("SENDER_PASSWORD", "") |
|
|
|
|
|
|
|
|
self.validators = self._load_validators() |
|
|
|
|
|
def _load_validators(self) -> List[str]: |
|
|
"""Carga lista de validadores desde configuración""" |
|
|
|
|
|
default_validators = [ |
|
|
"[email protected]", |
|
|
"[email protected]", |
|
|
"[email protected]" |
|
|
] |
|
|
|
|
|
|
|
|
env_validators = os.getenv("VALIDATOR_EMAILS", "") |
|
|
if env_validators: |
|
|
return [email.strip() for email in env_validators.split(",")] |
|
|
|
|
|
return default_validators |
|
|
|
|
|
def _generate_validation_email(self, request: ValidationRequest) -> str: |
|
|
"""Genera contenido del email de validación""" |
|
|
|
|
|
approval_link = f"https://veureu-demo.hf.space/validate?doc_id={request.document_id}&action=approve" |
|
|
rejection_link = f"https://veureu-demo.hf.space/validate?doc_id={request.document_id}&action=reject" |
|
|
|
|
|
email_content = f""" |
|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<title>Solicitud de Validación - Veureu</title> |
|
|
</head> |
|
|
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;"> |
|
|
|
|
|
<div style="background-color: #f8f9fa; padding: 20px; border-radius: 8px;"> |
|
|
<h1 style="color: #1f6feb; text-align: center;">🔐 Solicitud de Validación Regulatoria</h1> |
|
|
|
|
|
<div style="background-color: white; padding: 20px; border-radius: 6px; margin: 20px 0;"> |
|
|
<h2 style="color: #333;">📋 Información del Envío</h2> |
|
|
<table style="width: 100%; border-collapse: collapse;"> |
|
|
<tr> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>Usuario:</strong></td> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{request.user_name} ({request.user_email})</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>Vídeo:</strong></td> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{request.video_title}</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>Hash:</strong></td> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;"><code>{request.video_hash[:32]}...</code></td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>Timestamp:</strong></td> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{request.timestamp}</td> |
|
|
</tr> |
|
|
<tr> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>ID Documento:</strong></td> |
|
|
<td style="padding: 8px; border-bottom: 1px solid #ddd;"><code>{request.document_id}</code></td> |
|
|
</tr> |
|
|
</table> |
|
|
</div> |
|
|
|
|
|
<div style="background-color: #fff3cd; padding: 15px; border-radius: 6px; margin: 20px 0; border-left: 4px solid #ffc107;"> |
|
|
<h3 style="color: #856404; margin-top: 0;">✅ Consentimientos Aceptados</h3> |
|
|
<ul style="color: #856404; margin: 0;"> |
|
|
<li>Derechos sobre el vídeo: {'✅' if request.consent_data.get('rights') else '❌'}</li> |
|
|
<li>Consentimiento biométrico: {'✅' if request.consent_data.get('biometric') else '❌'}</li> |
|
|
<li>Contenido permitido: {'✅' if request.consent_data.get('content') else '❌'}</li> |
|
|
<li>Privacidad y datos: {'✅' if request.consent_data.get('privacy') else '❌'}</li> |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
<div style="background-color: #d1ecf1; padding: 15px; border-radius: 6px; margin: 20px 0; border-left: 4px solid #17a2b8;"> |
|
|
<h3 style="color: #0c5460; margin-top: 0;">🎥 Acceso al Vídeo</h3> |
|
|
<p style="color: #0c5460; margin: 0;">Puedes revisar el vídeo en el siguiente enlace:</p> |
|
|
<p style="margin: 10px 0;"><a href="{request.video_url}" style="background-color: #17a2b8; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; display: inline-block;">📹 Ver Vídeo</a></p> |
|
|
</div> |
|
|
|
|
|
<div style="text-align: center; margin: 30px 0;"> |
|
|
<h3 style="color: #333;">🔍 Decisión de Validación</h3> |
|
|
<p style="color: #666;">Por favor, revisa el contenido y toma una decisión:</p> |
|
|
|
|
|
<div style="display: flex; justify-content: center; gap: 20px; margin: 20px 0;"> |
|
|
<a href="{approval_link}" style="background-color: #28a745; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: bold;"> |
|
|
✅ APROBAR |
|
|
</a> |
|
|
<a href="{rejection_link}" style="background-color: #dc3545; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: bold;"> |
|
|
❌ RECHAZAR |
|
|
</a> |
|
|
</div> |
|
|
|
|
|
<p style="color: #666; font-size: 12px; margin-top: 20px;"> |
|
|
Esta decisión quedará registrada en AWS QLDB para cumplimiento normativo (AI Act, GDPR). |
|
|
</p> |
|
|
</div> |
|
|
|
|
|
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 6px; margin: 20px 0; text-align: center; color: #666; font-size: 12px;"> |
|
|
<p>Este es un mensaje automático del sistema de validación regulatoria de Veureu.</p> |
|
|
<p>Para consultas, contacta a [email protected]</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
|
|
|
return email_content |
|
|
|
|
|
def send_validation_request(self, request: ValidationRequest) -> bool: |
|
|
""" |
|
|
Envía solicitud de validación a todos los validadores |
|
|
|
|
|
Returns: |
|
|
True si éxito, False si error |
|
|
""" |
|
|
try: |
|
|
subject = f"🔐 Validación Regulatoria - {request.video_title}" |
|
|
html_content = self._generate_validation_email(request) |
|
|
|
|
|
|
|
|
""" |
|
|
# Configurar mensaje |
|
|
msg = MIMEMultipart('alternative') |
|
|
msg['Subject'] = subject |
|
|
msg['From'] = self.sender_email |
|
|
msg['To'] = ', '.join(self.validators) |
|
|
|
|
|
# Adjuntar contenido HTML |
|
|
html_part = MIMEText(html_content, 'html') |
|
|
msg.attach(html_part) |
|
|
|
|
|
# Enviar email |
|
|
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server: |
|
|
server.starttls() |
|
|
server.login(self.sender_email, self.sender_password) |
|
|
server.send_message(msg) |
|
|
""" |
|
|
|
|
|
|
|
|
print(f"[EMAIL - SIMULATED] Enviando solicitud de validación:") |
|
|
print(f"[EMAIL - SIMULATED] Para: {', '.join(self.validators)}") |
|
|
print(f"[EMAIL - SIMULATED] Asunto: {subject}") |
|
|
print(f"[EMAIL - SIMULATED] Documento: {request.document_id}") |
|
|
print(f"[EMAIL - SIMULATED] Usuario: {request.user_email}") |
|
|
print(f"[EMAIL - SIMULATED] Vídeo: {request.video_title}") |
|
|
|
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
print(f"[EMAIL ERROR] Error enviando validación: {e}") |
|
|
return False |
|
|
|
|
|
def send_decision_notification(self, validator_email: str, |
|
|
decision: str, |
|
|
document_id: str, |
|
|
comments: str = "") -> bool: |
|
|
""" |
|
|
Envía notificación de decisión tomada por validador |
|
|
|
|
|
Returns: |
|
|
True si éxito, False si error |
|
|
""" |
|
|
try: |
|
|
subject = f"🔍 Decisión de Validación - {decision.upper()}" |
|
|
|
|
|
content = f""" |
|
|
Se ha registrado una decisión de validación: |
|
|
|
|
|
Validador: {validator_email} |
|
|
Documento: {document_id} |
|
|
Decisión: {decision} |
|
|
Comentarios: {comments} |
|
|
Timestamp: {datetime.now().isoformat()} |
|
|
|
|
|
Esta decisión ha quedado registrada en AWS QLDB. |
|
|
""" |
|
|
|
|
|
|
|
|
""" |
|
|
msg = MIMEText(content) |
|
|
msg['Subject'] = subject |
|
|
msg['From'] = self.sender_email |
|
|
msg['To'] = '[email protected]' |
|
|
|
|
|
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server: |
|
|
server.starttls() |
|
|
server.login(self.sender_email, self.sender_password) |
|
|
server.send_message(msg) |
|
|
""" |
|
|
|
|
|
|
|
|
print(f"[EMAIL - SIMULATED] Notificación de decisión enviada:") |
|
|
print(f"[EMAIL - SIMULATED] Validador: {validator_email}") |
|
|
print(f"[EMAIL - SIMULATED] Decisión: {decision}") |
|
|
print(f"[EMAIL - SIMULATED] Documento: {document_id}") |
|
|
|
|
|
return True |
|
|
|
|
|
except Exception as e: |
|
|
print(f"[EMAIL ERROR] Error enviando notificación: {e}") |
|
|
return False |
|
|
|
|
|
|
|
|
notification_service = EmailNotificationService() |
|
|
|