Servicio Windows 24/7 — Polling cada 5 min

Decoracio Virtual Manager

Plataforma completa de gestion automatizada de archivos de decoracion virtual en OneDrive Business: sincronizacion en tiempo real, archivado programado, monitorizacion de calidad, dashboard web con SSO y alertas por email.

👤 Autor: JM Fernandez + Claude (Anthropic)
📅 Fecha: Abril 2026
☁️ Target: [email protected]
🏠 Server: decomanager.sys.idealista
Python 3.13 Flask + Waitress Microsoft Graph API OneDrive Business Azure AD SSO Certificate Auth SQLite WAL Nginx HTTPS WinSW Service HTML Email Reports Delta Query AWS S3 / boto3 SQLite Logs DB
🚀 Evolucion: Este proyecto reemplaza y unifica los 3 scripts PowerShell originales (ArchiveOldProjectsModular.ps1, ArchiveOldProjectsCalidad.ps1, ArchiveOldProjectsRecopilacion.ps1) en una plataforma unica con dashboard web, deteccion en tiempo real, ejecucion continua 24/7 y funcionalidades que antes no existian (sync UPLOADS, auto-limpieza, alertas de errores, configuracion online). → Ver informe de los scripts originales
8
Tareas automatizadas
14
Servicios Python
8
Paginas web
6
Tipos de email
24/7
Ejecucion continua
~5min
Ciclo de polling

Que hace Decoracio Virtual Manager

Gestiona el ciclo de vida completo de los archivos de decoracion virtual: desde que un colaborador sube imagenes hasta que se archivan en el historico, con monitorizacion, limpieza y reportes automaticos.

🔄

Sync UPLOADS

Detecta nuevas carpetas PARA_ENTREGAR via Delta Query y las copia automaticamente a IDEALISTA. Retry con backoff, copias concurrentes (5 hilos), notificacion por email a cada copia, y auto-limpieza de origenes tras X dias.

📸

Calidad Monitor

Cada ciclo (~5min) escanea carpetas PRODUCCION_DD-MM-YYYY y mueve imagenes JPG/PNG a IDEALISTA/Calidad. Elimina carpetas vacias de mas de 3 dias. Informe diario combinado por email.

📄

Recopilacion Monitor

Mueve archivos (cualquier extension) de sincronitzacio/RECOPILACION a IDEALISTA/CALIDAD/RECOPILACION cada ciclo. Edad minima configurable para no mover archivos en proceso de copia.

🗃

Archivo Produccion

Semanal. Archiva carpetas de proyectos (_es/_it/_pt) de #PRODUCCIO con mas de 7 dias al OneDrive historico (decoraciovirtualhistoric). Copia recursiva cross-drive + eliminacion de origen.

🗃

Archivo Calidad

Semanal. Archiva carpetas PRODUCCION_* con mas de 210 dias de IDEALISTA/CALIDAD al historico. Excluye carpetas DEBUG y RECOPILACION. Cross-drive a cuenta historica.

📄

Archivo Recopilacion

Semanal. Archiva archivos de IDEALISTA/CALIDAD/RECOPILACION con mas de 30 dias al historico, organizados por fecha (YYYY-MM-DD). Batch de 1000 archivos, borra origen tras copia.

🗑

Limpieza UPLOADS

Diario. Elimina carpetas de proyectos en UPLOADS que ya fueron copiados y tienen mas de 30 dias. Recorre toda la estructura colaborador/pais/proyecto.

☁️

S3 Sync 360

Detecta tours 360 nuevos o modificados en /360/IDEALISTA via Delta Query. Descarga de OneDrive y sube al bucket S3 (tour360.yaencontre.com) con boto3. Thread separado para no bloquear el sync de uploads. Soporta re-subida selectiva (solo archivos modificados).

Stack tecnologico

Servicio Windows con webapp Flask, reverse proxy Nginx con HTTPS, autenticacion Azure AD SSO, y conexion a OneDrive via Microsoft Graph API.

🌐
Nginx
HTTPS / HTTP2
Reverse proxy con SSL, HSTS, security headers.
🎓
Flask + Waitress
Python WSGI
Web dashboard, settings, auth. 4 worker threads.
🕓
Scheduler
Daemon Thread
Orquesta todas las tareas. Polling cada ~300s.
☁️
Graph API
Microsoft v1.0
Delta query, copy, move, delete. Certificado X.509.
🗃
SQLite
WAL + Autocommit
Proyectos, ejecuciones, estado. Thread-safe.
Request: Browser Nginx :443 Waitress :8085 Flask
Sync: Scheduler Delta Query Copy API Email + DB
Archive: Scheduler (semanal) List + Filter Cross-drive Copy Delete + Report
Auth: Azure AD SSO MSAL Auth Code ID Token Flask Session
S3 Sync: Delta Query OneDrive Download Graph API Upload boto3 S3 Email + DB

Deteccion y copia en tiempo real

La funcionalidad principal del sistema. Detecta subidas de colaboradores externos y las replica automaticamente en la estructura de produccion.

FLUJO SYNC UPLOADS

1. detect_new_uploads()
   Delta Query a /sincronitzacio/UPLOADS
   Busca cambios en carpetas PARA_ENTREGAR con contenido
   Parsea: UPLOADS/{colaborador}/PAISES/{pais}/{proyecto}/PARA_ENTREGAR
   → INSERT INTO pending_projects (attempts=0)

2. process_pending_projects()  [ThreadPoolExecutor, 5 hilos]
   Espera wait_minutes (10 min) desde deteccion
   Para cada proyecto pendiente:merge_copy_folder() PARA_ENTREGAR → IDEALISTA/{pais}/{proyecto}
      Crea carpeta destino si no existe
      Copia archivos con replace si origen es mas reciente
      Preserva archivos solo-destinoOK:   INSERT INTO processed_projects (COPIED/OVERWRITTEN)
          send_copy_notification() email con lista de archivos
   → FAIL: attempts++ → retry (max 3) → si agotado: FAILED

3. cleanup_copied_sources()
   Elimina origen de proyectos copiados con >30 dias
   Marca source_deleted=1 en processed_projects
🔍

Delta Query

Usa delta de Graph API para detectar solo cambios incrementales. Mantiene el deltaLink persistente en sync_state. Si expira (410 Gone), hace rescan con cutoff configurable.

Copias concurrentes

ThreadPoolExecutor con max_concurrent_copies configurable (default 5). Lock global para stats thread-safe. Timeout por copia de 1800s con polling adaptativo.

🔄

Merge-copy inteligente

No sobreescribe todo a ciegas. Compara timestamps: solo reemplaza si origen es mas reciente. Mantiene archivos que solo existen en destino. Crea carpetas destino si no existen.

Calidad + Recopilacion

Se ejecutan en cada ciclo del scheduler (~5 min). Mueven archivos de las carpetas de entrada a la estructura de produccion.

📸

Calidad Monitor

Origen: /sincronitzacio/CALIDAD/PRODUCCION_*/IA/*.{jpg,jpeg,png}
Destino: /IDEALISTA/Calidad/{PRODUCCION_*}/

Usa move_item (PATCH) para mover en la misma unidad (mas eficiente que copy+delete). Sobreescribe si ya existe en destino (conflictBehavior: replace). Elimina carpetas PRODUCCION_* con mas de max_folder_age_days dias. Edad minima de archivo configurable para evitar mover archivos en proceso de escritura.

📄

Recopilacion Monitor

Origen: /sincronitzacio/RECOPILACION/* (cualquier extension)
Destino: /IDEALISTA/CALIDAD/RECOPILACION/

Mueve todos los archivos con edad superior a min_file_age_minutes (default 1 min). Sin filtro de extension. Usa move_item (PATCH) con replace. Registra ejecucion con estadisticas en task_executions solo si hay actividad.

📧

Informe diario combinado: A la hora configurada en report_time + 5min, se envia un email con el resumen del dia de ambos monitors: archivos movidos, errores y ciclos ejecutados. Los destinatarios se configuran de forma independiente.

Archivado diario + Limpieza

Se ejecutan automaticamente en el dia y hora configurados. Tambien se pueden lanzar on-demand desde el dashboard (solo admins).

Tarea Origen Destino Umbral Cuenta destino
Archivo Produccion #PRODUCCIO/{colab}/*_{es,it,pt} DecoracioVirtualHistoric/#PRODUCCIO/{colab} 7 dias — diario 03:00 decoraciovirtualhistoric@
Archivo Calidad IDEALISTA/CALIDAD/PRODUCCION_* DecoracioVirtualHistoric/IDEALISTA/CALIDAD 210 dias — diario 01:00 decoraciovirtualhistoric@
Archivo Recopilacion IDEALISTA/CALIDAD/RECOPILACION/* DecoracioVirtualHistoric/.../RECOPILACION/YYYY-MM-DD 30 dias — diario 02:00 decoraciovirtualhistoric@
Limpieza UPLOADS UPLOADS/{colab}/PAISES/{pais}/{proy} Se eliminan 30 dias — semanal

Las tareas de archivo usan cross-drive copy: copian de la cuenta de trabajo (decoraciovirtual@) a la cuenta historica (decoraciovirtualhistoric@) via Graph API, resolviendo el drive ID de destino dinamicamente. Tras la copia exitosa, eliminan el origen. Cada ejecucion envia un email de reporte inmediato con estadisticas. El Archivo Produccion desciende a subcarpetas de colaborador (#PRODUCCIO/{colaborador}/{proyecto}) replicando la estructura en destino.

Interfaz de gestion

Accesible via HTTPS con Azure AD SSO. Roles User y Admin. Dark theme responsive.

📊

Dashboard

Vista general con 7 cards: estado de cada tarea, ultimo ciclo, resumen de hoy (copiados/errores/pendientes). Indicador de scheduler activo/detenido.

🔄

Sync UPLOADS

Estadisticas del dia, cola de pendientes con intentos y errores, busqueda AJAX con filtros (estado, fechas, texto) y paginacion de 20 registros. Boton reintentar en proyectos fallidos.

📸

Monitors

Cards con estadisticas de Calidad y Recopilacion: movidos/errores hoy, ultimo ciclo. Auto-refresh cada 60s. Gear icon para config inline.

🗃

Archivo

3 cards (Produccion, Calidad, Recopilacion) con historial de las 20 ultimas ejecuciones. Boton "Ejecutar" on-demand (solo admin). Gear icon para schedule/umbral.

🗑

Limpieza

Card con historial de ejecuciones de cleanup. Boton on-demand para admin. Eliminados/omitidos/errores por ejecucion.

☁️

S3 Sync 360

Estadisticas del dia (subidos/errores), tours pendientes, busqueda AJAX con filtros y paginacion. Botones reintentar (FAILED) y re-subir selectivo (UPLOADED: solo archivos modificados).

📜

Logs del Sistema

Solo admin. Visor de logs SQLite con filtros por nivel (INFO/WARNING/ERROR), modulo, rango de fechas y texto libre. Paginacion de 200 registros. Purga manual configurable.

Configuracion

Solo admin. Cards para: Notificaciones, Reporte diario (SMTP, destinatarios), S3 Sync, Alertas (umbrales), Mantenimiento DB, Sesion. Guardado instantaneo con validacion server-side.

Cada card de tarea tiene un icono ⚙ que abre un modal de settings inline donde el admin puede cambiar configuracion especifica (intervalos, umbrales, dias, destinatarios email) sin necesidad de acceder al servidor. Los cambios se guardan en app-config.json y el scheduler los recoge en el siguiente ciclo.

6 tipos de email

Todos los emails son HTML con estadisticas visuales. Destinatarios configurables por tipo. SMTP interno sin autenticacion.

Tipo Cuando Contenido Destinatarios
Notificacion por copia Inmediato tras cada sync exitoso Proyecto copiado: colaborador, pais, archivos PARA_ENTREGAR con tamanos notification.email_to
Reporte diario sync A report_time (20:00) Resumen del dia: detectados, copiados, errores, pendientes + detalle por proyecto sync.email_to o reporting.email_to
Reporte diario monitors A report_time + 5min Calidad + Recopilacion: movidos, errores, ciclos del dia calidad.email_to o reporting.email_to
Reporte de tarea Inmediato tras archive/cleanup Archivados/eliminados/errores con stat cards {tarea}.email_to o reporting.email_to
Alerta de errores Cuando se supera umbral (max 1/dia/tipo) Tipo de error, conteo, detalle de proyectos fallidos alerts.email_to o reporting.email_to
Notificacion S3 Inmediato tras cada tour subido a S3 Tour subido: nombre, archivos, tamano total, duracion s3_sync.email_to

Monitorizacion proactiva

Comprueba umbrales cada ciclo del scheduler. Solo alerta por errores definitivos (post-retry). Maximo una alerta por tipo y dia.

🔄

Sync UPLOADS

Cuenta proyectos FAILED hoy (agotaron reintentos). Tambien detecta pendientes con 2+ intentos fallidos. Umbral: 5/dia.

🕓

Scheduler

Cuenta ciclos consecutivos con excepcion no controlada. Si el scheduler falla 3 veces seguidas, algo va muy mal. Umbral: 3.

🗃

Tareas programadas

Cuenta ejecuciones con estado ERROR hoy por tarea (archive, cleanup, recopilacion). Umbral: 2/tarea/dia.

📸

Monitors

Cuenta archivos fallidos acumulados en el dia para calidad y recopilacion_monitor. Umbral: 10 archivos/dia.

Azure AD SSO + Roles

Single Sign-On con Microsoft Entra ID. Dos niveles de acceso basados en roles del App Registration.

FLUJO DE LOGIN

1. Usuario accede a https://decomanager.sys.idealista
2. @login_required redirige a /auth/login
3. MSAL genera authorization URL → redirige a Azure AD
4. Usuario se autentica con credenciales corporativas
5. Azure AD callback a /auth/callback con auth code
6. MSAL adquiere token → extrae id_token_claims
     → name, email, roles[], oid
7. Sesion Flask con expiracion configurable (12h default)

ROLES
  User   → Dashboard, Sync, Monitors, Archivo, Limpieza (solo lectura)
  Admin  → Todo lo anterior + Settings, Ejecutar on-demand, Gear icons

SQLite con WAL + Autocommit

Base de datos ligera, sin servidor. WAL mode para lecturas concurrentes, autocommit para evitar locks entre threads.

Tabla Proposito Registros tipicos
sync_state Key-value para estado persistente: delta_link, alertas enviadas, errores consecutivos ~10 claves
pending_projects Cola de proyectos detectados esperando ser copiados. Retry counter + last_error 0-20 (transitorio)
processed_projects Log de proyectos procesados: COPIED/FAILED/OVERWRITTEN/SIMULATED Cientos/dia en pico
task_executions Historial de todas las ejecuciones de tareas con stats y estado Varios por dia
task_items Items individuales de sync (para detalle de notificacion) Purgado cada 30 dias
daily_reports Registro de reportes enviados por dia y tipo (sync, monitors, s3_sync) 3/dia
s3_pending_tours Cola de tours detectados esperando subida a S3. Incluye since_dt para re-subida selectiva 0-10 (transitorio)
s3_processed_tours Historial de tours subidos a S3: UPLOADED/FAILED/SIMULATED. files_count, total_bytes Decenas/semana
📜

Base de datos de logs: Ademas de la DB principal, existe logs/logs.db (SQLite separada) que recibe todos los logs del servicio via SQLiteLogHandler. Escritura en buffer (20 registros o 5s) para no impactar rendimiento. Accesible desde el panel web (solo admin) con filtros por modulo, nivel y fecha.

Mantenimiento automatico: El scheduler purga registros antiguos periodicamente. Procesados se retienen 30 dias, fallidos 180 dias. Intervalo de purga configurable (default 24h). Migraciones: Auto-migracion al arrancar. Detecta esquema actual y aplica cambios incrementales (v2: status SIMULATED, v3: source_deleted, v4: report_type).

Operaciones OneDrive

Todas las operaciones sobre OneDrive Business se realizan via Microsoft Graph REST API v1.0 con autenticacion por certificado X.509 desde el almacen de Windows.

Operacion Metodo Graph Uso
get_user_drive_id(email) GET /users/{email}/drives Resolver drive ID de cualquier cuenta OneDrive
invoke_delta() GET /drives/{id}/root/delta Detectar cambios incrementales en UPLOADS
list_children() GET /drives/{id}/items/{id}/children Listar contenido de carpetas (paginado)
copy_item() POST /drives/{id}/items/{id}/copy Copia asincrona con polling (cross-drive)
move_item() PATCH /drives/{id}/items/{id} Mover dentro del mismo drive (monitors)
delete_item() DELETE /drives/{id}/items/{id} Eliminar archivos/carpetas (ignora 404)
create_folder() POST /drives/{id}/items/{id}/children Crear carpetas con conflict handling
merge_copy_folder() Combinacion de operaciones Copia inteligente: solo reemplaza si mas reciente
copy_folder_recursive() Combinacion de operaciones Copia recursiva del arbol completo (archive)
🔒

Autenticacion: MSAL con certificado X.509 almacenado en el Windows Certificate Store. El thumbprint se configura en graph.certificate_thumbprint. Tambien soporta certificados PEM/PFX (modo certificate_file) y client secret para desarrollo. Token cacheado en memoria con renovacion automatica.

Servicio Windows con Nginx

Dos servicios Windows (WinSW): uno para la aplicacion Python y otro para Nginx como reverse proxy HTTPS.

💻

DecoracioVirtualManager

Servicio WinSW que ejecuta run_webapp.py con Waitress (4 threads) en puerto 8085. Auto-restart on failure. Logs rolling 10MB.

🌐

Nginx Reverse Proxy

Servicio WinSW para Nginx. SSL/HTTP2 en :443, redirect :80→:443. HSTS, X-Frame-Options, X-Content-Type-Options. Static cache 30 dias.

🚀

Deploy automatizado

deploy.ps1: git pull master, pip install, restart servicio, verificacion. Un solo comando para actualizar produccion.

ESTRUCTURA DE ARCHIVOS

decoracio-virtual-manager/
├── config/
│   ├── app-config.json          Configuracion runtime
│   └── app-config.example.json  Template
├── db/
│   ├── database.py              Conexion + migraciones
│   └── schema.sql               Esquema DDL
├── services/
│   ├── graph_auth.py            MSAL autenticacion
│   ├── graph_onedrive.py        Graph API operations
│   ├── scheduler.py             Orquestador daemon
│   ├── sync_processor.py        Core sync logic
│   ├── calidad_monitor.py       Monitor imagenes
│   ├── recopilacion_monitor.py  Monitor archivos
│   ├── archive_projects.py      Archivado diario (nested collaborators)
│   ├── archive_recopilacion.py  Archivado recopilacion
│   ├── cleanup_uploads.py       Limpieza UPLOADS
│   ├── s3_sync_processor.py     Deteccion + subida tours 360
│   ├── s3_client.py             Wrapper boto3 con filtro since_dt
│   ├── email_reporter.py        6 tipos de email
│   └── error_alerts.py          Alertas por umbral
├── db/
│   ├── database.py              Conexion + migraciones
│   ├── schema.sql               Esquema DDL
│   └── log_db.py                SQLiteLogHandler + query_logs()
├── logs/
│   ├── dvm.log                  Log rotativo aplicacion
│   └── logs.db                  SQLite logs (separada de la DB principal)
├── webapp/
│   ├── app.py                   Flask factory
│   ├── routes/                  9 blueprints (incl. s3_sync, logs)
│   ├── templates/               12 templates Jinja2
│   └── utils/auth.py            Decorators + helpers (admin_required)
├── run_webapp.py                Entry point
├── requirements.txt             7 dependencias
├── deploy.ps1                   Script de despliegue
├── nginx.conf                   Reverse proxy config
└── *-service.xml                Definiciones WinSW

Todo configurable sin tocar codigo

Un unico archivo app-config.json gobierna todo el comportamiento. Editable desde la web (admins) o directamente en disco. El scheduler recarga cada ciclo.

🔄

Sync

Polling interval, wait minutes, max attempts, concurrent copies, copy timeout, auto-delete days, email_to

📸

Calidad

Enabled, max folder age, min file age, extensions, email_to para informe diario

🗃

Archive (x3)

Enabled, schedule day/time, days threshold, destination email/paths, exclusions, email_to

🗑

Cleanup

Enabled, schedule day/time, days threshold, email_to

Alertas

Enabled, sync/task/monitor/scheduler thresholds, email_to

📧

Reporting

Report time, SMTP config, email_from, email_to default + overrides por tipo

☁️

S3 Sync

Bucket name, region, wait minutes, cutoff hours, max attempts, concurrent uploads, notificacion email. Credenciales S3 solo en JSON (no en UI)

🔒

Auth

Redirect URI, session hours. Graph: tenant, client, cert thumbprint

🔧

Mantenimiento

Retention days (procesados/fallidos), purge interval, log_retention_days

De PowerShell scripts a plataforma

Lo que empezo como 3 scripts independientes se ha convertido en un servicio completo con interfaz web.

Scripts PowerShell (antes) Decoracio Virtual Manager (ahora)
Ejecucion Task Scheduler, semanal Servicio 24/7, polling cada 5 min
Deteccion No existia (solo archivado) Delta Query en tiempo real
Sync UPLOADS No existia Deteccion + copia + notificacion + auto-limpieza
Visibilidad Logs en disco + email diario Dashboard web en tiempo real + 5 tipos de email
Configuracion Archivo .psd1 en servidor UI web con validacion + hot-reload
Autenticacion Certificado para Graph (solo) Certificado + Azure AD SSO para web
Alertas No existian Umbrales configurables, 1 alerta/tipo/dia
On-demand Ejecucion manual del script Boton en web (admin)
Base de datos No existia (estado en logs) SQLite con historial, retry queue, stats
Monitor de archivos Calidad semanal + Recopilacion semanal Cada 5 min (alta frecuencia) + archivado diario
Tours 360 a S3 No existia S3 Sync 360: deteccion Delta Query + upload boto3 + re-subida selectiva
Logs Solo ficheros en disco SQLite logs.db con visor web filtrable (modulo, nivel, fecha)