# Monitorización externa y notas manuales por proceso Sexta entrega del proyecto. Cierra la pieza que falta para que el sistema cubra **todo el ciclo de vida** de un proceso selectivo, no solo la parte que se publica en boletines. --- ## 1. El problema que resuelve El art. 16 del RD 364/1995 permite que, una vez iniciado el proceso selectivo, "los sucesivos anuncios" se publiquen "por el órgano de selección en los lugares o medios indicados". En la práctica esto significa que **a partir de la fase 4** (lista provisional de admitidos) la información oficial deja en muchos casos de aparecer en BOE o boletines autonómicos y se publica directamente en: - **Tablón de anuncios electrónico** de la administración convocante. - **Sede electrónica** del organismo (AEAT, INGESA, ministerio, ayuntamiento, etc.). - **Web específica del proceso selectivo** (común en convocatorias grandes: hay URL única donde se cuelga todo). - **Web del tribunal calificador** o portal interno. Un buscador que solo escanea boletines pierde toda esa cola, que es precisamente la parte más activa del proceso para los opositores ya inscritos: subsanaciones, fechas de examen, plantillas correctoras, calificaciones, propuestas de nombramiento. Además, hay información valiosa que **no está en ningún sitio público** y que el equipo de la academia conoce: la persona de contacto en el tribunal, una observación sobre la dificultad del temario, un correo recibido de un alumno, una alerta de "ojo a esta convocatoria, riesgo de retraso", el archivo PDF que alguien descargó manualmente del tablón antes de que el sistema lo viera. --- ## 2. Las dos funcionalidades ### 2.1 Monitorización automática de URLs externas Por cada proceso, el sistema permite registrar una o varias URLs a monitorizar. Tipos típicos: | Tipo | Ejemplo | |------|---------| | Tablón electrónico | `https://sede.aeat.gob.es/Sede/oposiciones-y-empleo/.../tecnicos-hacienda-2026.html` | | Sede electrónica | `https://sede.ingesa.gob.es/empleo-publico/proceso-2026/` | | Web del proceso | `https://www.madrid.es/oposiciones/tag-2026/` | | Web del tribunal | `https://www.upm.es/pruebas-selectivas/tribunal-2026/` | | Otra web relevante | foros, portales gremiales, asociaciones | El motor `web_watcher` visita cada URL según una frecuencia configurada (por defecto adaptada a la fase del proceso) y: - Calcula un hash del contenido normalizado para detectar **cambios** en el HTML. - Extrae todos los enlaces a archivos (PDF, DOCX, DOC) y compara con la visita anterior. Si hay archivos **nuevos**, los descarga. - Si el archivo descargado parece unas bases o una resolución importante (por nombre y contenido), pasa por el clasificador de fases y por el parser de bases existente. El sistema lo trata exactamente igual que si hubiera aparecido en un boletín. - Si hay cambios pero no son archivos, dispara una notificación de "esta URL ha cambiado, revísala manualmente" con un diff resumido. ### 2.2 Información manual por proceso Cada proceso tiene un panel donde el equipo puede ir registrando: - **Notas libres**: texto, formato Markdown ligero, sin restricción. - **Pendientes**: tareas con checkbox, asignables a una persona, con fecha límite opcional. Aparecen en el dashboard hasta que se marcan como hechas. - **Contactos**: nombre, rol, teléfono, email, organismo (la persona del tribunal, la persona de RR.HH. del ayuntamiento, etc.). - **Etiquetas**: libres por usuario u organización. Útiles para filtrar luego ("riesgo alto", "curso abierto", "candidato comercial", etc.). - **Adjuntos manuales**: PDFs u otros documentos que alguien sube al proceso porque los consiguió por otra vía (un alumno los compartió, alguien los descargó del tablón antes de que el watcher pasara, etc.). Todo queda asociado al proceso y aparece en su pantalla de detalle junto al timeline de 10 fases. --- ## 3. Cómo se integra con el resto del sistema Esto **no rompe ni reemplaza** nada de lo entregado. Encaja como una fuente adicional de eventos: ``` ┌─────────────────────────────────────────┐ │ FUENTES DE EVENTOS │ │ │ │ ┌───────────────┐ ┌────────────────┐ │ │ │ Boletines │ │ URLs externas │ │ │ │ oficiales │ │ (tablones, │ │ │ │ (entregado) │ │ sedes, webs) │ │ │ └───────┬───────┘ └────────┬───────┘ │ │ │ │ │ │ └────────┬──────────┘ │ │ ↓ │ │ Motor común de clasificación │ │ (fases, modificadores, notificación) │ │ ↓ │ │ Eventos persistidos │ │ eventos_proceso, fechas_clave... │ └─────────────────────────────────────────┘ ↓ Dashboard, emails, calendario, reglas de notificación, mejora continua... (todo igual que antes) ``` Las URLs externas usan exactamente la misma pipeline: clasificador de fases (`01_fases.yaml` / `06_fases_extendido.yaml`), parser de bases (`04_parser_bases.py`), motor de reglas (`11_motor_reglas.py`). Cambia solo de dónde viene el documento — el resto es idéntico. Las notas manuales no disparan eventos automáticos; son información contextual que aparece en el dashboard y se puede buscar. --- ## 4. Decisiones de diseño ### 4.1 Frecuencia de chequeo adaptativa por fase del proceso | Estado del proceso | Frecuencia por defecto | |---|---| | OEP publicada (esperando bases) | 24 horas | | Bases publicadas (esperando apertura) | 12 horas | | Plazo abierto (presentación de solicitudes) | 4 horas | | Lista provisional → definitiva | 2 horas | | Esperando fecha de examen | 2 horas | | Desarrollo de ejercicios | 4 horas | | Resultados | 2 horas | | Esperando nombramiento | 24 horas | | Finalizado / archivado | no se chequea | La razón: los momentos más sensibles (subsanaciones, fecha de examen, calificaciones) son los que requieren más frecuencia. Cuando el proceso lleva meses sin moverse, no tiene sentido machacar el servidor. Esta frecuencia se puede sobrescribir manualmente por enlace si el usuario quiere algo distinto ("vigílame esto cada 30 minutos hoy porque sé que sacan resultados"). ### 4.2 Respeto a los servidores El watcher es **educado**: - User-Agent identificable: `Buscador-Multisearch/1.0 (+https://buscador.example/about-bot)` para que el administrador del servidor sepa quién es si revisa los logs. - Rate limit por dominio: máximo 1 petición cada 10 segundos al mismo dominio, aunque haya varias URLs registradas de ese dominio. - Respeto a `robots.txt`: antes de chequear una URL nueva por primera vez, se descarga el `robots.txt` del dominio y se respeta lo que diga. Si bloquea al bot, esa URL no se monitoriza y se avisa al usuario. - Cache HTTP: se mandan headers `If-Modified-Since` y `If-None-Match` cuando los hay, para ahorrar ancho de banda en ambos lados. - Snapshots: solo se guarda copia en BD cuando hay cambio real, no en cada chequeo. ### 4.3 Detección de cambios robusta El hash directo del HTML falla siempre que la página tiene timestamps, contadores de visita, anuncios rotatorios o tokens CSRF. El watcher hace **normalización** antes de hashear: - Elimina scripts, estilos y meta tags. - Normaliza espacios en blanco. - Quita patrones de fecha/hora del tipo "última actualización: X". - Quita IDs aleatorios y tokens en hidden inputs. Lo que sí siempre se compara directamente sin normalización es **la lista de enlaces a archivos** (PDFs, DOCX). Si aparece uno nuevo en la lista, es un cambio real y se descarga. ### 4.4 Almacenamiento de archivos descargados Los archivos descargados se guardan localmente (o en S3) bajo: ``` archivos_externos/{id_proceso}/{año}/{mes}/{hash_sha256}_{nombre_original} ``` El hash SHA-256 evita duplicados si dos URLs distintas exponen el mismo archivo. El nombre original se preserva para legibilidad humana. ### 4.5 Procesamiento automático de archivos descargados Cuando se detecta un archivo nuevo, una heurística decide si pasar el PDF por la pipeline: - Si el nombre contiene "bases", "convocatoria", "programa", "temario" → pasar por `parser_bases.py`. - Si contiene "admitidos", "excluidos", "tribunal", "calificaciones", "aprobados" → pasar por clasificador de fases para identificar sub-fase. - Si no encaja en ningún patrón → guardarlo, indexar texto y avisar al usuario para revisión manual ("nuevo archivo desconocido, ¿qué es?"). --- ## 5. Archivos de esta entrega | # | Archivo | Qué contiene | |---|---|---| | 14 | `14_README_monitorizacion_externa.md` | Este documento | | 15 | `15_db_enlaces_y_notas_schema.sql` | Tablas SQL: enlaces, snapshots, archivos detectados, notas, pendientes, contactos, adjuntos | | 16 | `16_web_watcher.py` | Motor de monitorización con detección de cambios y archivos | | 17 | `17_dashboard_enlaces_notas.md` | Especificación de las nuevas pantallas del dashboard | --- ## 6. Casos de uso reales ### Caso 1: Tablón del Ayuntamiento de Madrid El proceso de TAG Madrid 2026 tiene sus bases en BOCM (las captó el buscador) pero a partir de ahí todo va al portal de oposiciones de madrid.es. El coordinador de contenidos registra esa URL en el proceso. El watcher la visita cada 4 horas mientras el plazo está abierto. Cuando aparece la lista provisional de admitidos (sin pasar por BOCM), el watcher detecta el PDF nuevo, lo descarga, lo clasifica como fase 4, dispara notificación inmediata a los alumnos suscritos con el plazo de subsanación y crea la fecha clave correspondiente en calendario. ### Caso 2: AEAT — proceso de Técnicos de Hacienda La OEP sale en BOE y la apertura de plazo también, pero las calificaciones y la composición del tribunal van a sede.agenciatributaria.gob.es en una página específica de cada proceso. El equipo registra la URL en el dashboard. Cuando salen calificaciones del primer ejercicio, el watcher lo detecta, las descarga, las clasifica como fase 8 y avisa. ### Caso 3: Nota manual de un contacto El comercial de la academia consigue el teléfono del jefe de RR.HH. del ayuntamiento que va a convocar un proceso aún no anunciado. Lo registra como contacto en el proceso (que ya existe en estado "OEP publicada"). Tres meses después, cuando aparezcan las bases, el contacto está ahí, listo para usar. ### Caso 4: Pendiente al equipo El responsable de contenidos crea una tarea pendiente en el proceso de Auxiliar Administrativo AGE: "Actualizar materiales del tema 12, referencia legal cambió". Asignada a una persona, con fecha límite. El dashboard la muestra hasta que se marca como completada. --- ## 7. Roadmap de implementación **Sprint 1 (cuando tengas dashboard básico ya rodando)**: - Tablas SQL nuevas (15_db_enlaces_y_notas_schema.sql). - CRUD de notas, pendientes, contactos, etiquetas en el dashboard. - Sin watcher todavía: solo gestión manual. **Sprint 2**: - Watcher básico: chequea URLs registradas, detecta cambios por hash, notifica. - Sin descarga ni clasificación automática de archivos. **Sprint 3**: - Detección de archivos nuevos (PDF, DOCX). - Descarga al almacén local. - Integración con clasificador de fases existente. **Sprint 4**: - Procesamiento automático con `parser_bases.py` para los casos en que el archivo descargado son nuevas bases. - Métricas de eficacia del watcher (cuántos eventos se generaron por esta vía).