Files
descarga_factura_transporte/docs/main.md
T
GeorgeandClaude Sonnet 5.5 1d23e87b9b versión 1.2.2: horarios nocturnos unificados en dos tramos
HORARIOS pasa a 5 bloques que siguen cubriendo las 24 horas: 00:00-06:30 cada
30 minutos (antes 00:00-05:00 cada 60 s y 05:00-06:30 cada 30 min) y
18:30-24:00 cada 60 minutos (antes dos bloques iguales). El día no cambia.
De madrugada un correo puede esperar hasta 30 minutos y los reintentos por
SAP fuera de línea, uno por ciclo, van igual de espaciados.

Se ordena el formato de la lista, test_horarios fija el primer y el último
minuto de cada bloque (y que nunca se devuelva None en el día), y se
actualizan docs/main.md, la guía de despliegue y el CHANGELOG.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 15:36:48 -05:00

108 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# main.py — Mapa del flujo
## Estructura general
```
main.py
├── Constantes: HORARIOS (5 bloques que cubren las 24 horas)
├── obtener_intervalo(dt) → (intervalo, modo) | None
├── segundos_hasta_proximo_inicio(dt) → int
├── non_negative_int(value) → int (validador argparse)
└── main() → None (bucle infinito envuelto en try/except KeyboardInterrupt)
```
## Proceso de `main()`
```mermaid
flowchart TD
A[Inicio main] --> B[Parsear args --dias]
B --> C[Cargar .env → EMAIL, DOWNLOAD_FOLDER, OUTPUT_FOLDER]
C --> Z[try:]
Z --> D{Bucle infinito}
D --> E[inicio_ciclo = datetime.now]
E --> F[obtener_intervalo inicio_ciclo]
F -- Sí, está en horario --> G[Extraer intervalo, modo]
G --> H[run_pipeline]:::highlight
H --> I[Calcular duración real]
I --> J{Duración > intervalo?}
J -- Sí --> K[⚠️ Warning]
J -- No --> L[dormir = intervalo - duración]
K --> L
L --> M[💤 Próximo ciclo en dormir seg]
M --> N[time.sleep dormir]
N --> E
F -- No, fuera de horario --> O[dormir = segundos_hasta_proximo_inicio]
O --> P[Log próxima ejecución]
P --> N
D -. KeyboardInterrupt .-> Q[🛑 Demonio detenido por el usuario]
classDef highlight fill:#fff3e0,stroke:#e65100,stroke-width:3px;
```
### Paso a paso
```
main()
│
├── 1. Parsear args (--dias, default=2)
│
├── 2. Cargar .env → EMAIL, DOWNLOAD_FOLDER, OUTPUT_FOLDER
│
├── 3. try:
│ └── LOOP INFINITO (while True):
│ │
│ ├── 3.1. inicio_ciclo = datetime.now()
│ │
│ ├── 3.2. resultado = obtener_intervalo(inicio_ciclo)
│ │ │
│ ├── ¿Sí está en horario?
│ │ │
│ │ ├── Extraer (intervalo, modo)
│ │ ├── run_pipeline(dias, EMAIL, DOWNLOAD_FOLDER, OUTPUT_FOLDER)
│ │ ├── Calcular duración real del ciclo
│ │ ├── ⚠️ Si duración > intervalo → warning
│ │ └── dormir = max(0, intervalo - duración)
│ │
│ └── ¿No está en horario?
│ │
│ ├── dormir = segundos_hasta_proximo_inicio() (06:30 del día siguiente; rama inalcanzable mientras los bloques cubran el día)
│ └── Log "Fuera del horario, próxima ejecución en..."
│ │
│ └── 3.3. time.sleep(dormir) → vuelve a 3.1
│
└── except KeyboardInterrupt: 🛑 "Demonio detenido por el usuario" (sin traceback)
```
## Bloques horarios definidos
| Desde | Hasta | Intervalo | Modo |
|---------|---------|-----------|----------|
| 00:00 | 06:30 | 1800s | full |
| 06:30 | 13:15 | 300s | rápido |
| 13:15 | 14:00 | 1800s | full |
| 14:00 | 18:30 | 300s | rápido |
| 18:30 | 24:00 | 3600s | full |
Los bloques cubren las 24 horas sin huecos ni solapes (lo comprueba `tests/test_horarios.py`). El modo (`rápido`/`full`) solo se registra en el log: `run_pipeline` se ejecuta igual en ambos. Antes de la versión 1.2.0 los tramos 21:30–00:00 y 05:00–06:30 no tenían bloque y el demonio dormía hasta las 06:30; desde la 1.2.2 la noche son solo dos tramos (00:00–06:30 cada 30 minutos y 18:30–24:00 cada 60). De madrugada un correo puede esperar hasta 30 minutos, y los reintentos por SAP fuera de línea (uno por ciclo) van igual de espaciados.
## Entry point
```
if __name__ == "__main__":
1. logger_manager.setup_logging()
└── Configura logging a archivo rotado (logs/descarga_factura_transporte.log,
rotación diaria, 30 días de historial) + consola, y redirige print()/stderr
al mismo log (los módulos del pipeline ya usan logger con nivel explícito;
los print() restantes se registran como INFO).
2. logger.info(...) banner de inicio: fecha, versión del proyecto (pyproject.toml) y logger_manager.LOG_FILE
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.
```