157 lines
17 KiB
Markdown
157 lines
17 KiB
Markdown
# CHANGELOG MVP — Admin Edu-Space (UniCABA)
|
|
|
|
> **Nota de Unificación:** Este archivo documenta la progresión de los hitos y entregables de las Fases del [ROADMAP_MVP.md](./ROADMAP_MVP.md). Para el historial técnico exhaustivo conforme a *Keep a Changelog* y *Semantic Versioning*, consultar el archivo oficial unificado: **[CHANGELOG.md](./CHANGELOG.md)**.
|
|
|
|
---
|
|
|
|
## [Fase 5: Integración Auth, Email & Moodle Backend] — v3.0.0 (2026-09-23)
|
|
**Estado:** ✅ 100% Completada
|
|
|
|
### Épica 1: Panel de Configuración Global (Perfil ADMIN)
|
|
* **Cifrado Simétrico Fernet:** Creación de `CryptoService` (`backend/app/services/crypto_service.py`) derivando de forma determinística una clave Fernet de 32 bytes a partir de `SECRET_KEY`.
|
|
* **Modelo `SystemSetting` Extendido:** Soporte para columnas `category` y flag `is_encrypted`. Almacenamiento seguro de secretos con métodos polimórficos `get_decrypted_value()` y enmascaramiento con `get_masked_value()`.
|
|
* **Endpoints Administrativos:**
|
|
- `GET /api/v1/admin/settings/all`: Retorna la configuración completa organizada por categorías (`smtp`, `auth_providers`, `google_oauth`, `moodle`) con secretos enmascarados.
|
|
- `POST /api/v1/admin/settings/smtp`: Configuración dinámica de Host, Puerto, Usuario, Contraseña cifrada, Protocolo de Seguridad (TLS/SSL/NONE), y Remitente.
|
|
- `POST /api/v1/admin/settings/smtp/test`: Handshake SMTP en vivo y envío opcional de correo de prueba con plantilla HTML oficial.
|
|
- `POST /api/v1/admin/settings/auth-providers`: Activación y desactivación de proveedores de acceso (`local`, `google`, `moodle`).
|
|
- `POST /api/v1/admin/settings/google-oauth`: Guardado de Client ID, Client Secret cifrado, Dominios autorizados y Callback URL.
|
|
- `POST /api/v1/admin/settings/moodle`: URL base del servidor, Token Web Services cifrado, timeout y frecuencia de sincronización.
|
|
* **Interfaz de Usuario Web:** Nueva pantalla responsiva `frontend/views/admin/settings/global_config.html` con pestañas navegables, validación en vivo, spinners y monitores de estado (100% adaptada para resoluciones desde 720p hasta 1080p).
|
|
* **Navegación:** Integración de enlace directo *"Config. Global"* en el menú lateral bajo Administración en `frontend/views/base.html`.
|
|
|
|
### Épica 2: Infraestructura Asíncrona y Caché
|
|
* **Capa de Caché Híbrida:** Creación de `CacheService` (`backend/app/services/cache_service.py`) con soporte nativo de Redis y fallback transparente en memoria con TTL para sesiones de usuario y perfiles de Moodle.
|
|
* **Cola Persistente de Sincronización:** Modelo `MoodleSyncTask` (`backend/app/models/sync_task.py`) y servicio `MoodleQueueService` (`backend/app/services/moodle_queue_service.py`) para registrar operaciones de sincronización (`CREATE_USER`, `UPDATE_USER`, `ENROL_USER`, `UNENROL_USER`, `ASSIGN_ROLE`).
|
|
* **Tolerancia a Fallos y DLQ:** Política de Exponential Backoff ($2^{\text{intentos}} \times 30$s) ante indisponibilidad de Moodle. Si se superan los intentos máximos, la tarea se traslada automáticamente a la **Dead Letter Queue (DLQ)** (`status = 'FAILED'`).
|
|
* **Endpoints de Cola & DLQ:**
|
|
- `GET /api/v1/admin/moodle/queue/stats`: Métricas en tiempo real (Pendientes, En Proceso, Reintentando, Completadas, Fallidas DLQ).
|
|
- `GET /api/v1/admin/moodle/queue/tasks`: Lista paginada con filtrado por estado.
|
|
- `POST /api/v1/admin/moodle/queue/process-now`: Disparo manual inmediato de sincronización.
|
|
- `POST /api/v1/admin/moodle/queue/tasks/<id>/retry`: Reintento de tarea individual de DLQ.
|
|
- `POST /api/v1/admin/moodle/queue/retry-all`: Reintento masivo de todas las tareas en DLQ.
|
|
|
|
### Épica 3: Sistema de Autenticación Híbrida (SSO & Local)
|
|
* **Login Local Refactorizado:** Endpoint `/api/v1/auth/login` respeta la configuración global. Si `auth_local_enabled` está desactivado, sólo admite acceso de contingencia a usuarios con rol `ADMIN`.
|
|
* **Google OAuth 2.0 (Arquitectura AlumnosLS):** Endpoint `POST /api/v1/auth/google` con validación estricta de dominios institucionales (`google_allowed_domains`), aprovisionamiento automático de perfil y emisión de tokens JWT.
|
|
* **Moodle Delegated Login:** Endpoint `POST /api/v1/auth/moodle` validando contra `/login/token.php` de Moodle 4.1 (`moodle_mobile_app`), obtención de datos de usuario con Web Services, caché en Redis y emisión de JWT.
|
|
* **Mapeo y Desacople de Roles:** Si el usuario es nuevo y posee rol de manager/admin en Moodle, se le asigna rol local `ADMIN`; en caso contrario se asigna `Docente` o `Alumno`. La administración y cambio de roles posterior permanece 100% local e independiente de Moodle.
|
|
* **Frontend Login:** Vista `frontend/views/auth/login.html` adaptada para consultar `/api/v1/auth/providers` y renderizar dinámicamente el botón de Google Workspace, el botón desplegable de Moodle y el formulario tradicional.
|
|
|
|
### Épica 4: Integración Bidireccional Moodle 4.1 (Backend)
|
|
* **Cliente REST Moodle 4.1:** `MoodleClient` (`backend/app/services/moodle_client.py`) con métodos para:
|
|
- `core_user_create_users`, `core_user_update_users`, `core_user_get_users`.
|
|
- `enrol_manual_enrol_users`, `enrol_manual_unenrol_users`, `core_enrol_get_enrolled_users`.
|
|
- `core_role_assign_roles`, `core_role_unassign_roles`.
|
|
- `test_connection()` contra `core_webservice_get_site_info`.
|
|
* **Desacople en CRUD Local:** Creación y edición de usuarios en `backend/app/routes/api/admin.py` encola automáticamente la sincronización sin bloquear las peticiones locales.
|
|
|
|
### Épica 5: Motor de Notificaciones (Email Server)
|
|
* **Cliente SMTP Dinámico:** Creación de `EmailService` (`backend/app/services/email_service.py`) que obtiene credenciales en tiempo de ejecución de la base de datos (con desencriptación de contraseña al vuelo).
|
|
* **Plantillas Transaccionales Profesionales:**
|
|
- `notify_teacher_assignment`: Notificación a profesores sobre asignación a comisiones y horarios.
|
|
- `notify_exam_schedule`: Convocatoria y citación a mesas de examen final.
|
|
- `test_smtp_connection`: Verificación de handshake y autenticación SMTP con feedback claro.
|
|
|
|
### Cobertura de Pruebas
|
|
* Suite completa en `backend/tests/test_phase6_integration.py` con 7 pruebas unitarias e integrales que validan cifrado Fernet, SystemSetting, CacheService, MoodleQueueService (Backoff & DLQ), EmailService dinámico, y autenticación híbrida (Google & Moodle). **30/30 pruebas pasando en verde** en la suite global de regresión.
|
|
|
|
---
|
|
|
|
## [Fase 4: UI/UX Avanzada para Bedelía & Modo Impersonación] — v2.8.0 (2026-09-23)
|
|
**Estado:** ✅ 100% Completada
|
|
|
|
### Estadio 4.1 — Grilla Semanal Interactiva (Drag-and-Drop) con Detección de Colisiones
|
|
* **FullCalendar Interactivo:** Habilitación de `editable: true` y `droppable: true` en el calendario general de Bedelía (`/schedule/calendar`).
|
|
* **Sidebar de Comisiones Pendientes:** Panel lateral dinámico con catálogo de comisiones que aún no tienen aula asignada (`unassigned_commissions`), permitiendo arrastrarlas directamente hacia los bloques horarios del calendario.
|
|
* **Validación de Choques en Tiempo Real:** Integración con `POST /api/v1/reservations/drag-update`. Al mover o soltar una clase, el backend somete la nueva posición a la matriz integral de conflictos (`ReservationService.check_full_conflicts_matrix`):
|
|
- Conflicto físico de aula (doble asignación simultánea).
|
|
- Conflicto docente (profesor ocupado en otra aula).
|
|
- Conflicto de aforo (capacidad de aula insuficiente).
|
|
* **Manejo de Respuestas:** Si hay colisión, devuelve HTTP 409 (`status: conflict`), FullCalendar revierte automáticamente el movimiento (`info.revert()`) y se dispara un Toast de alerta detallando el motivo del bloqueo. Si está libre, persiste la reserva y confirma con Toast de éxito.
|
|
|
|
### Estadio 4.2 — Modo Impersonación del Superadmin con Trazabilidad
|
|
* **Cabecera `X-Impersonate-User`:** Inyección y soporte en `backend/app/utils/jwt_decorators.py` dentro de `jwt_required` para usuarios administradores. Asigna `g.jwt_user` al usuario objetivo preservando `g.real_admin_user` y marcando `g.is_impersonating = True`.
|
|
* **Endpoints de Control:** `POST /api/v1/admin/impersonate` y `POST /api/v1/admin/stop-impersonating` en Backend Flask con auditoría inmutable en `AuditLog` (`IMPERSONATE_START` e `IMPERSONATE_END`).
|
|
* **Integración en BFF Node.js:** Middleware `authMiddleware.js` y proxy `/api` en `app.js` configurados para propagar cookies seguras `impersonate_user_id` e inyectar `X-Impersonate-User` en todas las peticiones a la API Flask.
|
|
* **Banner Visual Institucional:** Banner de advertencia permanente en `base.html` con fondo de alerta ámbar, spinner de estado, identificación del usuario y rol adoptado, y botón de salida inmediata.
|
|
* **Acceso de un Clic:** Botón directo de impersonación (`bi-incognito`) en cada fila de usuario activo en `/admin/users_list`.
|
|
|
|
### Estadio 4.3 — Acople del Optimizador Genético a Datos y Turnos Reales
|
|
* **Turnos Académicos Reales:** `ReservationOptimizer._calculate_preferred_time` sincronizado con las franjas horarias de UniCABA (`Mañana`: 08:00 a 12:00, `Tarde`: 14:00 a 18:00, `Vespertino`/`Noche`: 18:30 a 22:30).
|
|
* **Persistencia Segura:** Endpoint `POST /api/genetic/apply-optimization` adaptado con parseo riguroso de strings ISO y prevención de colisiones físicas en guardado masivo.
|
|
|
|
### Cobertura de Pruebas
|
|
* Suite completa en `backend/tests/test_phase4_interactive_impersonation.py` validando los 6 criterios de aceptación (impersonación de docente, impersonación de alumno, trazabilidad en auditoría, rechazo a no-admins, bloqueo 409 por colisión en drag-drop y actualización exitosa 200). 18/18 tests aprobados en suite global de Fases 2, 3 y 4.
|
|
|
|
---
|
|
|
|
## [Fase 3: Gestión de Hitos Evaluativos y Libro de Calificaciones] — v2.7.0 (2026-09-23)
|
|
**Estado:** ✅ 100% Completada
|
|
|
|
### Estadio 3.1 — Calendario de Evaluación por Comisión y Notificaciones Preventivas
|
|
* **Modelo `MilestoneGrade`:** Tabla relacional `milestone_grades` para registrar calificaciones por estudiante y reserva evaluativa, con validación de unicidad (`student_id`, `reservation_id`), notas en escala 1.0 a 10.0, estado de inasistencia (`is_absent`) y devoluciones pedagógicas (`feedback`).
|
|
* **Consulta de Hitos del Alumno:** Endpoint `GET /api/v1/students/my-upcoming-milestones` que detecta dinámicamente exámenes parciales, recuperatorios y entregas de TP de las comisiones del estudiante con cálculo automático de días restantes (`days_remaining`).
|
|
* **Dashboard del Alumno:** Widget interactivo de próximos exámenes en `/dashboard?role=alumno` con badges visuales de urgencia (*¡Hoy!*, *¡Mañana!*, *Faltan X días*) y acceso directo desde el Launchpad a *"Mis Calificaciones"*.
|
|
|
|
### Estadio 3.2 — Libro de Calificaciones (*Gradebook*) y Cierre Formal de Actas
|
|
* **Extensiones en `Commission`:** Incorporación de columnas `grades_closed` (boolean), `grades_closed_at` (datetime), `grades_closed_by` (foreign key) y `acta_number` (identificador oficial generado).
|
|
* **Servicio `GradebookService`:** Motor centralizado para matriz de notas por comisión, guardado asíncrono individual y por lote (`bulk_save_grades`), cálculo dinámico de promedios ponderados y condiciones académicas en tiempo real (*Promocionado* ≥ 7, *Regular* ≥ 4, *Libre* < 4).
|
|
* **Cierre Formal de Actas:** Sellado institucional con código único (`ACTA-YYYY-Sem-ID`), auditoría inmutable en `audit_logs` con acción `CLOSE_ACTA` y bloqueo total contra modificaciones posteriores.
|
|
* **Reapertura Excepcional:** Endpoint para Bedelía/Administración (`POST /api/v1/commissions/<id>/gradebook/reopen`) con justificación administrativa obligatoria asentada en auditoría.
|
|
* **Interfaz Docente & Bedelía (`/schedule/gradebook/:id`):** Matriz interactiva de carga rápida con autoguardado asíncrono debounced (500ms), indicador visual flotante de sincronización, recálculo instantáneo de promedios en el cliente y modales de sellado y reapertura de actas.
|
|
* **Portal del Alumno (`/schedule/my_grades` / `/mis-materias/mis-notas`):** Vista consolidada de todas las materias del estudiante, notas de parciales, condición final y número de acta emitida, con soporte de impresión de certificado de regularidad.
|
|
* **Acceso Directo en Comisión:** Botón destacado *"Libro de Calificaciones (Actas)"* y badge de acta sellada incorporados en `/admin/commission_detail`.
|
|
|
|
### Cobertura de Pruebas
|
|
* Suite completa en `backend/tests/test_phase3_gradebook.py` validando los 6 criterios de aceptación: estructura de matriz bidimensional, persistencia y autoguardado de notas, validación estricta de notas inválidas (fuera del rango 1-10), cierre formal de acta e inmutabilidad estricta ante intentos de edición, consulta integral del alumno y cuenta regresiva de próximos hitos. 6/6 tests aprobados (12/12 en conjunto con Fase 2).
|
|
|
|
---
|
|
|
|
## [Fase 2: Co-docencia, Matriz de Conflictos y Regla Diaria] — v2.6.0 (2026-09-19)
|
|
**Estado:** ✅ 100% Completada
|
|
|
|
### Estadio 2.1 — Modelo y Gestión Integral de Co-docencia
|
|
* **Modelo `CommissionTeacher`:** Soporte para múltiples docentes por comisión en base de datos (`commission_teachers`), admitiendo roles académicos diferenciados (`Titular`, `Adjunto`, `JTP`, `Ayudante`) y flag `is_primary`.
|
|
* **API REST de Cátedras:** Endpoints `POST /api/v1/commissions/<id>/teachers` y `DELETE /api/v1/commissions/<id>/teachers/<user_id>` con serialización completa de cátedra en comisiones.
|
|
* **Interfaz de Gestión:** Tarjeta interactiva *"Equipo Docente / Cátedra"* en `/admin/commission_detail` para incorporar y desvincular profesores con rol dinámico, y visualización de badges en `/admin/commissions_list`.
|
|
|
|
### Estadio 2.2 — Matriz de Conflictos y Validación Integral
|
|
* **Conflicto Físico:** Bloqueo estricto e infranqueable de superposiciones de reservas en la misma aula física (`ReservationRepository.find_overlapping`).
|
|
* **Conflicto Docente:** Detección de doble asignación simultánea para un mismo docente en diferentes aulas o formatos.
|
|
* **Excepción Justificada por Co-docencia:** Si un docente asignado presenta solapamiento pero la comisión cuenta con co-docentes registrados en `CommissionTeacher`, la matriz de validación autoriza la reserva validando la cobertura del equipo de cátedra.
|
|
* **Conflicto de Aforo:** Bloqueo estricto cuando `expected_attendees > classroom.capacity` en espacios físicos.
|
|
* **Endpoint de Pre-validación:** `POST /api/v1/reservations/check-conflicts` para validación asíncrona de conflictos físicos, docentes y de aforo antes del commit.
|
|
|
|
### Estadio 2.3 — Regla de Restricción Diaria del Alumno y Motor de Excepciones
|
|
* **Servicio `EnrollmentService`:** Lógica de matriculación académica con análisis de días de cursada por comisión y verificación de cupos.
|
|
* **Regla Restrictiva:** Un alumno no puede cursar dos asignaturas regulares el mismo día de la semana.
|
|
* **Excepción Automática por Curso Corto:** Atributo `is_short_course` en modelo `Subject`; si una de las materias es un taller o curso corto, el sistema autoriza la cursada simultánea en el mismo día.
|
|
* **Excepción Expresa de Bedelía:** Campos `allow_same_day_exception` y `exception_reason` en `StudentEnrollment`. La oficina de Bedelía puede autorizar la matriculación simultánea mediante switch justificado en la interfaz.
|
|
* **Endpoints de Matrícula:** `GET/POST /api/v1/commissions/<id>/enrollments`, `DELETE/PUT /api/v1/commissions/<id>/enrollments/<student_id>`.
|
|
* **UI en Detalle de Comisión:** Formulario de matriculación con toggle de autorización de Bedelía, motivo de excepción y tabla de inscriptos con badges de estado.
|
|
|
|
### Cobertura de Pruebas
|
|
* Suite completa en `backend/tests/test_phase2_rules.py` validando los 6 criterios de aceptación (conflicto físico, aforo, co-docencia, restricción diaria, bypass de curso corto y bypass de Bedelía). 6/6 tests aprobados con 100% de éxito.
|
|
|
|
### Complementos y Seguridad Transversal
|
|
* **Dashboard de Bedelía:** Integración de paneles en tiempo real para análisis de **Comisiones Sin Reserva** y grilla de **Aulas Disponibles** (Filtros Semana/Mes) conectando frontend UI con el servicio analítico backend.
|
|
* **Auditoría Automatizada (Fase 1):** Implantación de un pipeline de seguridad local/Gitea que aísla dependencias de desarrollo (`requirements-dev.txt`) y ejecuta controles SAST, DAST y SCA (Bandit, pip-audit, njsscan y Schemathesis) sin vulnerabilidades detectadas.
|
|
* **Optimizador Genético de Reservas:** Reparación de enrutamiento 404, migración a JWT (`@jwt_required`), corrección de UI de métricas dinámicas de algoritmo, y refactorización de layout para superposición de herramientas.
|
|
* **UI/UX General:** Ajustes de CSS en modo claro (soporte dinámico de contraste de isologotipo blanco a rosa institucional mediante filtros) y traducciones de menús de usuario en `base.html`.
|
|
---
|
|
|
|
## [Fase 1: Paridad Legacy y Estabilización] — v2.5.0 / v2.4.0 (2026-09-19)
|
|
**Estado:** ✅ 100% Completada
|
|
* Seguridad automatizada SAST/DAST/SCA (Bandit, pip-audit, schemathesis, njsscan, npm audit).
|
|
* Tipificaciones de documentos argentinos y extranjeros, perfiles extendidos de usuarios con email personal e institucional.
|
|
* Gestión completa de aulas físicas vs virtuales con percentiles de asignación y métricas de ocupación real.
|
|
* Reingeniería del modal de edición y eliminación segura de asignaturas.
|
|
|
|
---
|
|
|
|
## [Fase 0: Desacople y Arquitectura Base] — v2.0.0 (2026-09-18)
|
|
**Estado:** ✅ 100% Completada
|
|
* Desacople arquitectónico completo: Backend Flask RESTful (`/api/v1/`) + Frontend BFF Node.js/Express con Nunjucks.
|
|
* Autenticación basada en tokens JWT con sincronización de estado de sesión.
|