From 1d23e87b9b01bd792f71354ef60e5d89ec0340a3 Mon Sep 17 00:00:00 2001 From: George Date: Thu, 22 Oct 2026 15:36:48 -0500 Subject: [PATCH] =?UTF-8?q?versi=C3=B3n=201.2.2:=20horarios=20nocturnos=20?= =?UTF-8?q?unificados=20en=20dos=20tramos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CHANGELOG.md | 7 ++++++- README.md | 2 +- docs/despliegue_windows.md | 8 ++++---- docs/main.md | 10 ++++------ main.py | 6 ++---- pyproject.toml | 2 +- tests/test_horarios.py | 21 ++++++++++++++++----- 7 files changed, 34 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd419a2..f1140e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Historial de cambios -Las versiones siguen [SemVer](https://semver.org/lang/es/) y se publican como etiquetas de git (`v1.2.1`). La versión vigente está en `pyproject.toml` y se muestra en el log al arrancar. +Las versiones siguen [SemVer](https://semver.org/lang/es/) y se publican como etiquetas de git (`v1.2.2`). La versión vigente está en `pyproject.toml` y se muestra en el log al arrancar. + +## [1.2.2] — 2026-10-22 + +### Cambiado +- **Horarios del demonio:** la noche pasa a dos tramos, `00:00–06:30` cada 30 minutos y `18:30–24:00` cada 60 (antes: `00:00–05:00` cada 60 segundos, `05:00–06:30` cada 30 minutos, `18:30–21:30` y `21:30–24:00` cada 60). El día sigue igual. 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. Los 5 bloques siguen cubriendo las 24 horas sin huecos. ## [1.2.1] — 2026-10-22 diff --git a/README.md b/README.md index 73ae49a..3166c94 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ El demonio trabaja por bloques horarios (cada 1, 5, 30 o 60 minutos según la ho ## Despliegue -En producción corre como **servicio de Windows con NSSM**: arranca con el servidor y se reinicia solo. La carpeta [`deploy/`](deploy/) trae los scripts (instalar, actualizar a una versión etiquetada y volver atrás, desinstalar) y [`docs/despliegue_windows.md`](docs/despliegue_windows.md) la guía completa. Antes de instalar, `python tools/verificar_conexiones.py` comprueba el `.env`, Microsoft Graph, SAP, HANA y la ruta de red. Las versiones son etiquetas de git (`v1.2.1`); el historial está en [`CHANGELOG.md`](CHANGELOG.md). +En producción corre como **servicio de Windows con NSSM**: arranca con el servidor y se reinicia solo. La carpeta [`deploy/`](deploy/) trae los scripts (instalar, actualizar a una versión etiquetada y volver atrás, desinstalar) y [`docs/despliegue_windows.md`](docs/despliegue_windows.md) la guía completa. Antes de instalar, `python tools/verificar_conexiones.py` comprueba el `.env`, Microsoft Graph, SAP, HANA y la ruta de red. Las versiones son etiquetas de git (`v1.2.2`); el historial está en [`CHANGELOG.md`](CHANGELOG.md). ## Pruebas diff --git a/docs/despliegue_windows.md b/docs/despliegue_windows.md index 9786d26..187cdee 100644 --- a/docs/despliegue_windows.md +++ b/docs/despliegue_windows.md @@ -27,7 +27,7 @@ En PowerShell **como administrador**: cd C:\apps git clone https://unasgeorge.vaquita-hadar.ts.net/george/descarga_factura_transporte.git cd descarga_factura_transporte -git checkout v1.2.1 # siempre una versión etiquetada, no main +git checkout v1.2.2 # siempre una versión etiquetada, no main py -3 -m venv venv venv\Scripts\python -m pip install -r requirements.txt @@ -66,7 +66,7 @@ Get-Service descarga_factura_transporte Get-Content logs\descarga_factura_transporte.log -Wait -Tail 30 ``` -En el log, al arrancar, debe verse `🚀 Demonio iniciado … (versión 1.2.1)`, y `config_validator` anuncia el modo de notificación (`PRUEBA` o `REAL`). +En el log, al arrancar, debe verse `🚀 Demonio iniciado … (versión 1.2.2)`, y `config_validator` anuncia el modo de notificación (`PRUEBA` o `REAL`). ## Operación @@ -85,8 +85,8 @@ En el log, al arrancar, debe verse `🚀 Demonio iniciado … (versión 1.2.1)`, Siempre a una **versión etiquetada** (ver [CHANGELOG](../CHANGELOG.md)): ```powershell -.\deploy\actualizar.ps1 -Version v1.2.1 -.\deploy\actualizar.ps1 -Version v1.2.0 # volver a la anterior +.\deploy\actualizar.ps1 -Version v1.2.2 +.\deploy\actualizar.ps1 -Version v1.2.1 # volver a la anterior ``` El script detiene el servicio, hace `git checkout` de la etiqueta, instala las dependencias y arranca de nuevo (aunque algo falle, vuelve a levantar lo que había). No toca el `.env` ni `emails.db`. Se niega a actuar si hay cambios locales sin guardar. diff --git a/docs/main.md b/docs/main.md index 2ecd3ec..0d7b8a6 100644 --- a/docs/main.md +++ b/docs/main.md @@ -4,7 +4,7 @@ ``` main.py -├── Constantes: HORARIOS (7 bloques que cubren las 24 horas) +├── 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) @@ -81,15 +81,13 @@ main() | Desde | Hasta | Intervalo | Modo | |---------|---------|-----------|----------| -| 00:00 | 05:00 | 60s | rápido | -| 05:00 | 06:30 | 1800s | full | +| 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 | 21:30 | 3600s | full | -| 21:30 | 24:00 | 3600s | full | +| 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. +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 diff --git a/main.py b/main.py index b093e27..5b7deec 100644 --- a/main.py +++ b/main.py @@ -17,13 +17,11 @@ logger = logging.getLogger(__name__) # (hora inicio, min inicio, hora fin, min fin, intervalo en segundos, modo). El fin no se incluye. # Los bloques cubren las 24 horas sin huecos ni solapes (lo comprueba tests/test_horarios.py). HORARIOS = [ - (0, 0, 5, 0, 60, "rapido"), # 00:00 - 05:00 -> cada 60 segundos - (5, 0, 6, 30, 1800, "full"), # 05:00 - 06:30 -> cada 30 minutos + (0, 0, 6, 30, 1800, "full"), # 00:00 - 06:30 -> cada 30 minutos (6, 30, 13, 15, 300, "rapido"), # 06:30 - 13:15 -> cada 5 minutos (13, 15, 14, 0, 1800, "full"), # 13:15 - 14:00 -> cada 30 minutos (14, 0, 18, 30, 300, "rapido"), # 14:00 - 18:30 -> cada 5 minutos - (18, 30, 21, 30, 3600, "full"), # 18:30 - 21:30 -> cada 60 minutos - (21, 30, 24, 0, 3600, "full"), # 21:30 - 24:00 -> cada 60 minutos + (18, 30, 24, 0, 3600, "full"), # 18:30 - 24:00 -> cada 60 minutos ] # ============================================================================ def obtener_intervalo(dt: datetime) -> tuple[int, str] | None: diff --git a/pyproject.toml b/pyproject.toml index 4c9c868..dd31d25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "descarga_factura_transporte" -version = "1.2.1" +version = "1.2.2" description = "Descarga y procesamiento de facturas de transporte desde Office365" requires-python = ">=3.11" dependencies = [ diff --git a/tests/test_horarios.py b/tests/test_horarios.py index 144dc5b..b507c3d 100644 --- a/tests/test_horarios.py +++ b/tests/test_horarios.py @@ -23,11 +23,22 @@ def test_los_bloques_estan_ordenados_y_se_encadenan(): assert inicios[1:] == fines[:-1] # cada bloque empieza donde termina el anterior -def test_los_huecos_de_antes_ahora_tienen_horario(): - assert _intervalo(21, 29) == (3600, "full") and _intervalo(21, 30) == (3600, "full") - assert _intervalo(23, 59) == (3600, "full") - assert _intervalo(5, 0) == (1800, "full") and _intervalo(6, 29) == (1800, "full") - assert _intervalo(6, 30) == (300, "rapido") +def test_los_limites_de_cada_bloque(): + """Primer y último minuto de cada bloque, con su intervalo.""" + esperado = [ + ((0, 0), (1800, "full")), ((6, 29), (1800, "full")), # madrugada + ((6, 30), (300, "rapido")), ((13, 14), (300, "rapido")), # mañana + ((13, 15), (1800, "full")), ((13, 59), (1800, "full")), # mediodía + ((14, 0), (300, "rapido")), ((18, 29), (300, "rapido")), # tarde + ((18, 30), (3600, "full")), ((23, 59), (3600, "full")), # noche + ] + for (hora, minuto), valor in esperado: + assert _intervalo(hora, minuto) == valor, f"{hora:02d}:{minuto:02d}" + + +def test_nunca_se_devuelve_none_en_el_dia(): + """``obtener_intervalo`` devuelve None fuera de horario: con los bloques actuales no debe pasar nunca.""" + assert all(_intervalo(h, m) is not None for h in range(24) for m in range(60)) def test_la_version_del_proyecto_sale_de_pyproject():