# Daemon Planificador de Agenda (Scheduler) ⏰
`bgustscraper` cuenta con un motor de programación de tareas en segundo plano que permite ejecutar rutinas de raspado complejas de manera desatendida. Está diseñado para garantizar la **tolerancia a fallos**, **evitar el consumo innecesario de recursos** y **evitar el spam o bloqueos de IP** en sitios web protegidos.
---
## 📂 1. Configuración de Agenda: `config/schedule.json`
El planificador asíncrono carga su configuración desde `config/schedule.json`. Este archivo modela las tareas y sus respectivas ventanas horarias semanales de validez:
```json
{
"jobs": [
{
"name": "tasas_bcv",
"url": "https://www.bcv.org.ve/",
"days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
"windows": [
{ "name": "AM", "start": "09:00", "end": "12:59" },
{ "name": "PM", "start": "13:00", "end": "18:00" }
],
"extract_prompt": "Extrae las tasas oficiales de venta del Dólar (USD) y Euro (EUR)...",
"output_path": "downloads/bcv_rates.json"
}
]
}
```
### Atributos Clave:
* `days`: Listado de días de la semana de validez (`"Mon"`, `"Tue"`, etc.).
* `windows`: Segmentos específicos en los cuales la tarea debe ejecutarse.
* `output_path`: Ruta física en el disco donde se persistirá el resultado de la inferencia JSON final.
---
## 💾 2. Control de Estado en `redb` (Prevención de Spam)
Para cumplir con la regla de negocio de **ejecutar la tarea una sola vez dentro de cada ventana**, el scheduler interactúa directamente con la base de datos de caché local embebida **`redb`**:
### La Lógica de Control:
1. El daemon del scheduler corre en un bucle infinito asíncrono despertando cada **60 segundos** en Tokio.
2. Obtiene la hora, día de la semana y fecha local (ej: martes, 10:15 AM).
3. Busca tareas activas en esa ventana (ej: `tasas_bcv` en la ventana `AM`).
4. Antes de iniciar Playwright o cualquier llamada, comprueba si existe una marca en la base de datos de `redb` bajo la clave:
`schedule:<job_name>:<YYYY-MM-DD>:<window_name>` (ej. `schedule:tasas_bcv:2026-06-02:AM`).
5. **Si existe con valor `EJECUTADO`:** Omitir de forma inmediata. La tarea ya se corrió hoy en esta ventana.
6. **Si no existe:**
* Levanta el worker asíncrono.
* Raspa la web, comprime el texto mediante clustering K-Means y ejecuta la extracción estructurada con Qwen.
* Guarda el JSON final en el disco (`downloads/bcv_rates.json`).
* Inserta la clave `schedule:tasas_bcv:2026-06-02:AM` en `redb` con valor `EJECUTADO`.
* Cierra el worker de forma segura.
---
## 🛡️ 3. Resiliencia y Tolerancia a Fallos
Este diseño asíncrono-persistente asegura que:
* **Si la máquina se apaga o el servidor se reinicia** a las 11:00 AM, al reanudarse, el programador sabrá que ya se ejecutó a las 9:02 AM y no volverá a realizar peticiones innecesarias al sitio web, evitando posibles baneos de IP o consumos extra de CPU.
* **Si el daemon se inicia tarde** (ej: a las 10:00 AM en lugar de a las 9:00 AM), detectará que no ha corrido hoy en la ventana AM, la ejecutará inmediatamente y la marcará, garantizando la recolección del dato diario de forma segura.