Skip to content

Commit 2843cf6

Browse files
chore: remove debug _debug_default_recipient script and finalize email services unification
1 parent 2459b77 commit 2843cf6

22 files changed

Lines changed: 128 additions & 1069 deletions

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,7 @@ scripts-python/
310310
│ │ ├── __init__.py
311311
│ │ ├── agedys/ # Integración del sistema AGEDYS
312312
│ │ ├── brass/ # Integración del sistema brass
313-
│ │ ├── correo_tareas/ # Integración del sistema de correo tareas
314-
│ │ ├── correos/ # Integración del sistema de correos
313+
│ │ ├── email_services/ # Integración del servicio unificado de correos
315314
│ │ ├── database/ # Integración con bases de datos
316315
│ │ ├── expedientes/ # Integración del sistema de expedientes
317316
│ │ ├── no_conformidades/ # Integración no conformidades
@@ -321,7 +320,7 @@ scripts-python/
321320
│ ├── agedys/ # Tests específicos AGEDYS
322321
│ ├── brass/ # Tests específicos BRASS
323322
│ ├── common/ # Tests módulos comunes
324-
│ ├── correos/ # Tests del módulo de correos
323+
│ ├── email_services/ # Tests del servicio unificado de correos
325324
│ ├── expedientes/ # Tests del módulo de expedientes
326325
│ ├── no_conformidades/ # Tests no conformidades
327326
│ └── riesgos/ # Tests del módulo de riesgos
@@ -722,7 +721,7 @@ El sistema soporta dos configuraciones SMTP:
722721
| `src/common/task_registry.py` | 64% ||
723722
| `src/common/notifications.py` | 100% ||
724723
| `src/common/utils.py` | 49% ||
725-
| `src/correos/correos_manager.py` | 91% ||
724+
| `src/email_services/email_manager.py` | 91% ||
726725
| `src/expedientes/expedientes_manager.py` | 98% ||
727726
| `src/riesgos/riesgos_manager.py` | 90% ||
728727

@@ -1076,6 +1075,9 @@ python scripts/run_riesgos.py --force # Fuerza ejecución
10761075
python scripts/run_riesgos.py --dry-run # Modo simulación
10771076

10781077
# Email Services - Servicio unificado de correo (remplaza correos y correo_tareas)
1078+
1079+
### Manejo de errores transitorios SMTP (Refactor 2025)
1080+
Los errores de conexión SMTP (p.ej. desconexión inesperada, `SMTPConnectError`, `ConnectionRefusedError`) ahora se consideran **transitorios** y no marcan el correo como fallido en la base de datos. El registro permanece pendiente para reintentos en futuros ciclos. Sólo errores definitivos (credenciales inválidas, destinatario rechazado, formato de mensaje inválido) marcan el correo como fallido. Esto incrementa la resiliencia ante caídas puntuales del servidor de correo.
10791081
python scripts/run_email_services.py # Ejecución normal
10801082
python scripts/run_email_services.py --force # (Reservado) Fuerza ejecución
10811083

scripts/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Este directorio contiene los scripts de producción del sistema de monitoreo con
77
```
88
scripts/
99
├── run_master.py # 🎯 Script maestro - daemon principal del sistema
10-
├── run_correos.py # 📧 Gestión y envío de correos electrónicos
10+
├── run_email_services.py # 📧 Servicios unificados de envío de correos
1111
├── run_brass.py # 🔧 Procesamiento de datos BRASS
1212
├── run_expedientes.py # 📋 Gestión de expedientes y ofertas
1313
├── run_riesgos.py # ⚠️ Análisis y gestión de riesgos
@@ -31,18 +31,18 @@ El **Master Runner** es el corazón del sistema de monitoreo continuo. Ejecuta d
3131
- **Configuración flexible**: Variables de entorno para todos los parámetros
3232

3333
### Scripts Gestionados:
34-
1. **correos**`run_correos.py` (continuo)
34+
1. **email_services**`run_email_services.py` (continuo unificado)
3535
2. **riesgos**`run_riesgos.py` (diario)
3636
3. **brass**`run_brass.py` (diario)
3737
4. **expedientes**`run_expedientes.py` (diario)
3838
5. **no_conformidades**`run_no_conformidades.py` (diario)
3939

4040
## 📧 Scripts de Producción
4141

42-
### run_correos.py
43-
- **Función**: Gestión y envío de correos electrónicos
42+
### run_email_services.py
43+
- **Función**: Gestión unificada de servicios de correo (reemplaza correos y correo_tareas)
4444
- **Tipo**: Tarea continua (ejecutada en cada ciclo)
45-
- **Descripción**: Procesa colas de correo y envía notificaciones
45+
- **Descripción**: Procesa colas de correo de múltiples orígenes (correos, tareas) y envía notificaciones
4646

4747
### run_brass.py
4848
- **Función**: Procesamiento de datos BRASS
@@ -89,7 +89,7 @@ python run_master.py --dry-run
8989
### Ejecución Individual de Scripts:
9090
```bash
9191
# Ejecutar script específico
92-
python run_correos.py
92+
python run_email_services.py
9393
python run_brass.py
9494
python run_expedientes.py
9595
python run_riesgos.py

src/email_services/email_manager.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
logger = logging.getLogger(__name__)
2626

2727

28+
class TransientEmailSendError(Exception):
29+
"""Error transitorio (p.ej. fallo de conexión SMTP) que NO debe marcar el correo como no enviado aún."""
30+
pass
31+
32+
2833
class EmailManager:
2934
"""Gestor unificado para envío de correos pendientes.
3035
@@ -69,12 +74,19 @@ def process_pending_emails(self) -> int:
6974
logger.info("%s correos pendientes (%s)", len(rows), self.email_source)
7075
for correo in rows:
7176
try:
72-
if self._enviar_correo_individual(correo):
77+
resultado = self._enviar_correo_individual(correo)
78+
if resultado is True:
7379
self._marcar_correo_enviado(correo['IDCorreo'], datetime.now())
7480
enviados += 1
75-
else:
81+
elif resultado is False:
82+
# Fallo definitivo en construcción o envío (no transitorio)
7683
self._marcar_correo_no_enviado(correo['IDCorreo'], 'Error envío')
77-
except Exception as e: # pragma: no cover - path de error
84+
except TransientEmailSendError as e: # fallo transitorio -> reintentar en ciclo futuro
85+
logger.warning(
86+
"Fallo transitorio SMTP para correo ID %s: %s (se reintentará sin marcar)",
87+
correo.get('IDCorreo'), e
88+
)
89+
except Exception as e: # pragma: no cover - path de error inesperado
7890
logger.error("Error procesando correo ID %s: %s", correo.get('IDCorreo'), e)
7991
self._marcar_correo_no_enviado(correo.get('IDCorreo', -1), str(e))
8092
return enviados
@@ -84,6 +96,7 @@ def process_pending_emails(self) -> int:
8496

8597
# ------------------ Lógica interna ------------------
8698
def _enviar_correo_individual(self, correo: Dict[str, Any]) -> bool:
99+
"""Devuelve True si enviado, False si fallo definitivo, o levanta TransientEmailSendError."""
87100
try:
88101
msg = MIMEMultipart()
89102
aplicacion = correo.get('Aplicacion', 'Sistema')
@@ -109,6 +122,8 @@ def _enviar_correo_individual(self, correo: Dict[str, Any]) -> bool:
109122
logger.warning("Correo ID %s sin destinatarios", correo.get('IDCorreo'))
110123
return False
111124
return self._enviar_smtp(msg, destinatarios)
125+
except TransientEmailSendError:
126+
raise
112127
except Exception as e:
113128
logger.error("Error en _enviar_correo_individual: %s", e)
114129
return False
@@ -162,6 +177,9 @@ def _enviar_smtp(self, msg: MIMEMultipart, destinatarios: List[str]) -> bool:
162177
servidor.login(self.smtp_user, self.smtp_password)
163178
servidor.sendmail(self.smtp_user, destinatarios, msg.as_string())
164179
return True
180+
except (ConnectionRefusedError, smtplib.SMTPConnectError, smtplib.SMTPServerDisconnected) as e:
181+
logger.error("Fallo conexión SMTP: %s", e)
182+
raise TransientEmailSendError(str(e))
165183
except Exception as e:
166184
logger.error("Error enviando correo SMTP: %s", e)
167185
return False

tests/common/test_config.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

tests/common/test_utils_tasks.py

Lines changed: 0 additions & 73 deletions
This file was deleted.

tests/email_services/test_email_manager.py

Lines changed: 0 additions & 99 deletions
This file was deleted.

tests/email_services/test_email_manager_edges.py

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)