se valida la configuración (.env) al arrancar
config_validator.verificar_configuracion termina con código 1 si faltan variables obligatorias (pipeline, Graph, SAP B1, HANA), si hay ruta de red sin RUC_EMPRESA, y advierte por SSL desactivado, ruta de red ausente o LOG_LEVEL inválido. Se invoca desde main.py y tools/consulta_rapida.py. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
79378a70e1
commit
74be942a3e
6 files changed
+98
-8
No files matched your search
@@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
Validación de la configuración (.env) al arrancar el proceso.
|
||||||
|
|
||||||
|
Falla rápido con un mensaje claro si falta alguna variable obligatoria, en
|
||||||
|
lugar de descubrirlo a mitad de un ciclo (por ejemplo, adjuntos descargados
|
||||||
|
en la raíz del proyecto por un ``DOWNLOAD_FOLDER`` vacío, o consultas a HANA
|
||||||
|
que devuelven 0 en silencio por credenciales ausentes).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Variables obligatorias, agrupadas por servicio para el mensaje de error.
|
||||||
|
OBLIGATORIAS = {
|
||||||
|
"Pipeline": ["EMAIL", "DOWNLOAD_FOLDER", "OUTPUT_FOLDER"],
|
||||||
|
"Microsoft Graph": ["CLIENT_ID", "CLIENT_SECRET", "TENANT_ID"],
|
||||||
|
"SAP B1 Service Layer": ["SAP_B1_URL", "SAP_B1_COMPANY", "SAP_B1_USER", "SAP_B1_PASSWORD"],
|
||||||
|
"SAP HANA": ["HANA_SERVER", "HANA_USER", "HANA_PASSWORD", "HANA_SCHEMA"],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Variable de verificación SSL → servicio al que aplica. Cada módulo solo
|
||||||
|
# la activa con el valor "true" (cualquier otro valor, o ausente, la desactiva).
|
||||||
|
VERIFICACION_SSL = {
|
||||||
|
"VERIFY_SSL": "Microsoft Graph",
|
||||||
|
"SAP_B1_VERIFY_SSL": "SAP B1 Service Layer",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validar_configuracion() -> tuple[list[str], list[str]]:
|
||||||
|
"""
|
||||||
|
Revisa las variables de entorno ya cargadas (tras ``load_dotenv``).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[list[str], list[str]]: ``(errores, advertencias)``. Los errores
|
||||||
|
impiden arrancar; las advertencias solo se registran.
|
||||||
|
"""
|
||||||
|
errores: list[str] = []
|
||||||
|
advertencias: list[str] = []
|
||||||
|
|
||||||
|
for servicio, variables in OBLIGATORIAS.items():
|
||||||
|
faltan = [v for v in variables if not os.getenv(v, "").strip()]
|
||||||
|
if faltan:
|
||||||
|
errores.append(f"{servicio}: faltan {', '.join(faltan)}")
|
||||||
|
|
||||||
|
ruta_red = os.getenv("RUTA_RED_FACTURACION", "").strip()
|
||||||
|
ruc_empresa = os.getenv("RUC_EMPRESA", "").strip()
|
||||||
|
if ruta_red and not ruc_empresa:
|
||||||
|
errores.append("Ruta de red: RUTA_RED_FACTURACION está definida pero falta RUC_EMPRESA")
|
||||||
|
elif not ruta_red:
|
||||||
|
advertencias.append(
|
||||||
|
"RUTA_RED_FACTURACION no definida: no se copiarán PDF/XML a la red "
|
||||||
|
"y U_VS_ANEXO_PDF/XML irán vacíos en el draft"
|
||||||
|
)
|
||||||
|
|
||||||
|
nivel = os.getenv("LOG_LEVEL", "").strip()
|
||||||
|
if nivel and not isinstance(getattr(logging, nivel.upper(), None), int):
|
||||||
|
advertencias.append(f"LOG_LEVEL='{nivel}' no es un nivel válido; se usará INFO")
|
||||||
|
|
||||||
|
for variable, servicio in VERIFICACION_SSL.items():
|
||||||
|
if os.getenv(variable, "").strip().lower() != "true":
|
||||||
|
advertencias.append(f"Verificación SSL desactivada para {servicio} ({variable} distinto de 'true')")
|
||||||
|
|
||||||
|
return errores, advertencias
|
||||||
|
|
||||||
|
|
||||||
|
def verificar_configuracion() -> None:
|
||||||
|
"""
|
||||||
|
Registra las advertencias y, si hay errores, los registra y termina el
|
||||||
|
proceso con código 1.
|
||||||
|
"""
|
||||||
|
errores, advertencias = validar_configuracion()
|
||||||
|
|
||||||
|
for a in advertencias:
|
||||||
|
logger.warning(f"⚠️ Configuración: {a}")
|
||||||
|
|
||||||
|
if errores:
|
||||||
|
logger.error("❌ Configuración incompleta en .env:")
|
||||||
|
for e in errores:
|
||||||
|
logger.error(f" - {e}")
|
||||||
|
sys.exit(1)
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
| `hana_manager.py` | 123 | Comunicación con SAP HANA directa (fallback de `sapb1_manager` y valor referencial) |
|
| `hana_manager.py` | 123 | Comunicación con SAP HANA directa (fallback de `sapb1_manager` y valor referencial) |
|
||||||
| `database_utilities.py` | 116 | SQLite local (cache de correos) |
|
| `database_utilities.py` | 116 | SQLite local (cache de correos) |
|
||||||
| `tools/consulta_rapida.py` | 121 | Script auxiliar para reprocesar correos por ID (sets en `tools/ids_prueba.json`) |
|
| `tools/consulta_rapida.py` | 121 | Script auxiliar para reprocesar correos por ID (sets en `tools/ids_prueba.json`) |
|
||||||
|
| `config_validator.py` | 83 | Valida el `.env` al arrancar (obligatorias, ruta de red, avisos de SSL) |
|
||||||
| `email_utilities.py` | 52 | Utilitarios de adjuntos (ZIP, filtrado) |
|
| `email_utilities.py` | 52 | Utilitarios de adjuntos (ZIP, filtrado) |
|
||||||
| `logger_manager.py` | 149 | Logging a archivo rotado (30 días) + consola + captura de `print()` |
|
| `logger_manager.py` | 149 | Logging a archivo rotado (30 días) + consola + captura de `print()` |
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -96,7 +96,12 @@ if __name__ == "__main__":
|
|||||||
1. logger_manager.setup_logging()
|
1. logger_manager.setup_logging()
|
||||||
└── Configura logging a archivo rotado (logs/descarga_factura_transporte.log,
|
└── Configura logging a archivo rotado (logs/descarga_factura_transporte.log,
|
||||||
rotación diaria, 30 días de historial) + consola, y redirige print()/stderr
|
rotación diaria, 30 días de historial) + consola, y redirige print()/stderr
|
||||||
al mismo log (no hace falta migrar cada print a logger.info).
|
al mismo log (los módulos del pipeline ya usan logger con nivel explícito;
|
||||||
2. print(...) banner de inicio, incluyendo logger_manager.LOG_FILE
|
los print() restantes se registran como INFO).
|
||||||
|
2. logger.info(...) banner de inicio, incluyendo logger_manager.LOG_FILE
|
||||||
3. main()
|
3. main()
|
||||||
|
└── Tras load_dotenv(), config_validator.verificar_configuracion():
|
||||||
|
termina con código 1 si falta alguna variable obligatoria del .env
|
||||||
|
(ver config_validator.OBLIGATORIAS) y advierte si la verificación SSL
|
||||||
|
está desactivada o no hay ruta de red.
|
||||||
```
|
```
|
||||||
@@ -6,6 +6,7 @@ from datetime import datetime, timedelta
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
import logger_manager
|
import logger_manager
|
||||||
|
from config_validator import verificar_configuracion
|
||||||
from pipeline import run_pipeline
|
from pipeline import run_pipeline
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -70,6 +71,7 @@ def main() -> None:
|
|||||||
|
|
||||||
# Ruta absoluta: no depende del directorio de trabajo desde el que se lance el proceso
|
# Ruta absoluta: no depende del directorio de trabajo desde el que se lance el proceso
|
||||||
load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), ".env"))
|
load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), ".env"))
|
||||||
|
verificar_configuracion()
|
||||||
|
|
||||||
EMAIL = os.getenv("EMAIL", "")
|
EMAIL = os.getenv("EMAIL", "")
|
||||||
DOWNLOAD_FOLDER = os.getenv("DOWNLOAD_FOLDER", "")
|
DOWNLOAD_FOLDER = os.getenv("DOWNLOAD_FOLDER", "")
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ dependencies = [
|
|||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
py-modules = [
|
py-modules = [
|
||||||
"adjuntos",
|
"adjuntos",
|
||||||
|
"config_validator",
|
||||||
"database_utilities",
|
"database_utilities",
|
||||||
"documentos",
|
"documentos",
|
||||||
"draft_sap",
|
"draft_sap",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ sys.path.insert(0, RAIZ)
|
|||||||
|
|
||||||
import database_utilities
|
import database_utilities
|
||||||
import logger_manager
|
import logger_manager
|
||||||
|
from config_validator import verificar_configuracion
|
||||||
from pipeline import _procesar_un_correo, step_authenticate
|
from pipeline import _procesar_un_correo, step_authenticate
|
||||||
|
|
||||||
|
|
||||||
@@ -54,13 +55,10 @@ def get_email_by_id(user_email: str, token: str, message_id: str) -> dict | None
|
|||||||
def run_pipeline_for_ids(ids: list[str]) -> None:
|
def run_pipeline_for_ids(ids: list[str]) -> None:
|
||||||
# Ruta absoluta: no depende del directorio de trabajo desde el que se lance el proceso
|
# Ruta absoluta: no depende del directorio de trabajo desde el que se lance el proceso
|
||||||
load_dotenv(dotenv_path=os.path.join(RAIZ, ".env"))
|
load_dotenv(dotenv_path=os.path.join(RAIZ, ".env"))
|
||||||
|
verificar_configuracion()
|
||||||
email_addr = os.getenv("EMAIL", "")
|
email_addr = os.getenv("EMAIL", "")
|
||||||
download_folder = os.getenv("DOWNLOAD_FOLDER", "adjuntos")
|
download_folder = os.getenv("DOWNLOAD_FOLDER", "")
|
||||||
output_folder = os.getenv("OUTPUT_FOLDER", "resultado")
|
output_folder = os.getenv("OUTPUT_FOLDER", "")
|
||||||
|
|
||||||
if not email_addr:
|
|
||||||
print("❌ EMAIL no configurado en .env")
|
|
||||||
return
|
|
||||||
|
|
||||||
database_utilities.init_db()
|
database_utilities.init_db()
|
||||||
token = step_authenticate()
|
token = step_authenticate()
|
||||||
|
|||||||
Reference in new issue
Block a user