Files
admin-edu-space/CHANGELOG_SESSION.md
T

53 KiB

Resumen de Cambios - Sesión de Desarrollo (Rama dev)

Fecha: 2026-09-02 / 2026-09-03 / 2026-09-04
Proyecto: Admin Edu-Space (Flask + PostgreSQL)
Rama: dev


1. Auditoría Git y Puesta en Producción

  • Informe Técnico Completo (DEPLOYMENT_AUDIT.md):
    • Análisis de ramas locales y remotas (main, dev, origin/planning, origin/commissions, origin/spanish).
    • Manual de despliegue en producción con arquitectura WSGI (Gunicorn), Nginx como proxy inverso, Systemd unit y contenedorización Docker / Docker Compose.
  • Archivos Base:
    • Creación de .env.example con todas las variables requeridas (PostgreSQL, Flask, Secret Key, Babel).
    • Creación y posterior actualización de init_db.py para bootstrap inicial seguro de base de datos, roles RBAC y usuario administrador por defecto (admin@edu-space.com / admin123).

2. Base de Datos y Modelos

  • Preferencia de Idioma Persistente:
    • Migración en PostgreSQL agregando la columna preferred_language VARCHAR(10) a la tabla users.
    • Actualización del modelo User en app/models/user.py con el campo preferred_language.
  • Preferencia de Tema Visual Persistente:
    • Migración agregando la columna theme_preference VARCHAR(10) DEFAULT 'auto' NOT NULL a la tabla users.
    • Soporte en el modelo User para valores 'auto', 'light' y 'dark'.
  • Control de Acceso Basado en Roles (RBAC):
    • Migración creando las tablas roles y permissions, y agregando la clave foránea role_id a la tabla users.
    • Modelos Role y Permission en app/models/role.py.
    • Métodos has_permission(module, min_level) y can_manage() en User.

3. Implementación de Vistas y Plantillas

  • Perfil de Usuario (app/templates/auth/profile.html):
    • Vista completa con datos del usuario, rol, estado, fecha de registro y reservas recientes.
    • Formulario integral para configurar Idioma de la interfaz (Detectar automáticamente del navegador, Español, English) y Tema visual (Automático, Modo Claro, Modo Oscuro).
  • Cambio de Contraseña (app/templates/auth/change_password.html):
    • Formulario seguro para actualización de credenciales.
  • Manejo de Errores (app/templates/auth/403.html):
    • Pantalla de error 403 Forbidden estilizada e integrada con el sistema de diseño.

4. Correcciones de UX, Linter y Bugs de Rutas

  • Navbar Limpio (app/templates/base.html):
    • Eliminación del bloque duplicado del menú de usuario System Administrator.
  • Corrección de BuildError en Comisiones:
    • En app/routes/classrooms.py, se agregó el alias de ruta @classrooms_bp.route('/', endpoint='index') para resolver enlaces a classrooms.index.
    • En app/templates/schedule/commissions.html, se reemplazó el filtro inválido de Django truncatewords(20) por el filtro estándar de Jinja2 truncate(100).
  • Corrección Visual en /genetic-optimizer (app/templates/genetic_optimizer.html):
    • Se agregó el espaciado pt-5 mt-4 evitando que la barra fija superior se monte sobre el contenido.
    • Rediseño de tarjeta hero con contraste óptimo y reemplazo de iconos faltantes por Bootstrap Icons (bi bi-...).
  • Corrección de Errores del Linter en schedule/add.html y schedule/view.html:
    • En add.html: Desacople de iteraciones Jinja a <script type="application/json" id="classroom-data"> y parseo con JSON.parse(), eliminando 19 errores sintácticos del editor.
    • En view.html: Formateo de la propiedad inline style en barras de progreso para cumplir con el validador CSS.

5. Rediseño UI/UX, Assets Institucionales y Modo Claro/Oscuro

  • Exploración e Integración de Assets Gráficos:
    • Sincronización desde app/resources hacia app/static/img/:
      • logo_unicaba.png: Logo institucional principal para el login.
      • logo_unicaba_mini.png: Isotipo oficial para el navbar.
      • favicon.png: Favicon vinculado en las plantillas base.
      • icons/: Paquete de resoluciones WebApp/PWA.
  • Sistema de Diseño y Tokens de Color (app/static/css/theme.css):
    • Paleta institucional basada en Magenta/Uva (#B43E8E), acentos violetas (#682CAD / #AE7FFF) y secundarios magentas.
    • Tokens CSS para :root (Modo Claro) y [data-bs-theme="dark"] (Modo Oscuro).
    • Clases especializadas: .auth-card, .navbar-app, .user-avatar-small, .theme-toggle-btn, .badge-brand.
  • Modo Claro / Modo Oscuro Dinámico (app/static/js/theme-toggle.js):
    • Script anti-flickering en <head> para evitar parpadeos visuales al cargar la página.
    • Botón toggle interactivo con persistencia en localStorage.
    • En Modo Claro: Isotipo del navbar con filtro blanco puro (brightness(0) invert(1)) sobre el fondo magenta.
    • Selector de idioma (#languageDropdown) con fondo y bordes de alto contraste y tilde de verificación blanca (bi-check-lg) visible únicamente en el idioma activo sobre gradiente magenta.

6. Internacionalización (i18n), Localización y Formateo de Fechas

  • Detección Inteligente de Subtags Regionales (get_locale en app/__init__.py):
    • Extracción del subtag primario (es a partir de es-419, es-AR, es-ES), resolviendo el fallo donde Werkzeug saltaba erróneamente a inglés.
    • Configuración institucional: BABEL_DEFAULT_LOCALE = 'es' y LANGUAGES = ['es', 'en'].
  • Reglas de Idioma para la Pantalla de Login:
    • Resuelve según el navegador del visitante o recurre a español por defecto.
    • Inmunidad contra cookies o sesiones residuales de otros usuarios.
    • Flash messages traducidos (¡Bienvenido de nuevo!, Has cerrado sesión correctamente).
  • Formateador de Fechas Localizadas en Jinja2:
    • Registro de format_date y format_datetime de Flask-Babel.
    • Fechas en español natural en schedule/today.html (Jueves, 3 de septiembre de 2026), view.html y list.html.
  • Traducción Integral de schedule/add.html:
    • Formulario y textos de ayuda 100% traducidos.
    • Caja dinámica de resumen (JavaScript e i18n-strings) con cálculo de duración y validación de sobrecupo en español.

7. Módulo de Gestión de Accesos (RBAC) con Matriz FortiGate

  • Modelos y Persistencia:
    • Módulo app/models/role.py con Role y Permission.
    • Roles iniciales sembrados: Admin (total, protegido), Docente (reservas y horarios), Operador (aulas y reservas), Consulta (solo lectura).
    • Vinculación del usuario inicial admin@edu-space.com al rol Admin.
  • Matriz de Permisos Estilo FortiGate (/admin/roles/edit/<id>):
    • Tabla interactiva con columnas Access Control y Permissions.
    • Segmented control por módulo con tres estados: 🚫 None, 👁️ Read y ✏️ Read/Write.
    • Color activo verde esmeralda FortiGate (#2D9F6F) con texto e icono en blanco puro.
    • Menú desplegable masivo Set All ▾ (Set All to None, Set All to Read, Set All to Read/Write).
  • Módulo de Administración (app/routes/admin.py):
    • /admin/roles: Listado de roles, conteo de miembros, visualización de permisos en badges (RW, R, -) y modal de borrado seguro.
    • /admin/users: Listado de cuentas con filtros por nombre, rol y estado (Activo/Inactivo), paginación y switch rápido para alternar accesos.
    • /admin/users/add y /admin/users/edit/<id>: Formularios de alta y edición con asignación de rol y contraseña opcional.
  • Seguridad y Decoradores (app/utils/decorators.py):
    • @permission_required(module, min_level) y @admin_required.
    • Pestaña Management en el navbar visible exclusivamente para administradores.
    • Botones de acción sensibles en el navbar (Add Classroom, New Reservation) condicionados a permisos de escritura.

8. Sincronización y Actualización de init_db.py

  • Migraciones DDL automáticas (ALTER TABLE ... ADD COLUMN IF NOT EXISTS) para preferred_language, theme_preference, role_id, y para la tabla careers y columna career_id en subjects.
  • Creación y siembra de roles institucionales y permisos en PostgreSQL.
  • Configuración de salida con codificación UTF-8 segura en Windows para evitar excepciones de cp1252.

9. Sincronización e Importación de Datos Académicos (Google Sheets)

  • Modelo Career (app/models/career.py):
    • Entidad de carreras universitarias vinculada con Subject vía relación uno a muchos.
    • Migración en PostgreSQL de la tabla careers y campo career_id en subjects.
  • Servicio de Extracción (app/services/sheets_importer.py):
    • Integración con la cartelera oficial UniCABA 2026 a través de 6 hojas CSV (Lunes a Sábado).
    • Normalización inteligente de espacios áulicos físicos (Aula 402, Aula 302), espacios virtuales (VIRTUAL), auditorios y salas de reunión.
    • Extracción y parseo de franjas horarias (8:00 a 9:00hs, 17:30 a 21:30hs).
    • Rutina de upsert transaccional e idempotente que previene la duplicación de datos.
    • Generación de reservas/clases en el calendario para poblar instantáneamente las vistas de horario diario y semanal.
  • Interfaz de Gestión (/admin/import):
    • Vista interactiva con métricas en tiempo real (total de carreras, asignaturas, aulas, comisiones y clases).
    • Botón de ejecución manual de sincronización con indicador visual de progreso (spinner).
    • Reporte detallado de resultados y tabla con vista previa de asignaturas y comisiones sincronizadas.
  • Acceso Rápido en Navbar (app/templates/base.html):
    • Nueva opción "Import from Google Sheets" incorporada en el menú desplegable Management, restringida a roles con permisos administrativos.

10. Correcciones en la Vista de Calendario (FullCalendar) y Códigos de Asignatura

  • Carga de Eventos y Corrección de Error 500 (/schedule/calendar-data):
    • Se identificó y corrigió el error AttributeError: 'str' object has no attribute 'value' provocado por llamadas a .status.value sobre campos de estado textuales.
    • Se implementó el TypeDecorator StatusType con la clase StatusString en app/models/reservation.py garantizando compatibilidad total tanto para .status como para .status.value.
    • Sanitización de fechas ISO 8601 con zona horaria (Z, -03:00) enviadas por FullCalendar para comparación adecuada con campos TIMESTAMP WITHOUT TIME ZONE en PostgreSQL.
    • Anclaje de fecha inicial con initialDate: '{{ today_date }}' para posicionar el calendario en la semana activa de cursada 2026.
  • Corrección del Código de Asignatura (subject_code):
    • Se reemplazó la concatenación de código extendido de comisión (Commission.get_full_code() que arrojaba cadenas distorsionadas como INGA0003-C1-2026-22026) por el código de asignatura real del Sheet (Subject.code, ej. INGA0003, ASIG00165).
    • Propiedades de acceso directo añadidas en Reservation: subject_code, subject_name y classroom_name.
  • Jerarquía Visual y Renderizado de Tarjetas (calendar.html):
    • Implementación del hook eventContent en FullCalendar 5 para construir tarjetas personalizadas.
    • Nombre de la Asignatura destacado como elemento principal con tipografía grande y en negrita (font-weight: 700; font-size: 0.88rem).
    • Código de Asignatura y Aula mostrados como datos secundarios en badges de alto contraste (código con fondo oscuro translúcido y aula con isotipo y fondo blanco translúcido).
    • Modal interactivo instantáneo al hacer clic en cualquier evento con todos los detalles académicos de la clase y enlace a la ficha completa.

11. Filtros por Aula y Materia en el Calendario de Reservas

  • Barra de Herramientas de Filtros Interactiva (calendar.html):
    • Filtrar por Aula: Desplegable dinámico con todas las aulas activas ordenadas por edificio y código (físicas y virtuales). Al seleccionar un aula (ej. Aula 402), se eliminan todas las superposiciones visuales mostrando el cronograma limpio de ese espacio.
    • Filtrar por Materia: Desplegable ordenado alfabéticamente con formato [Código] Nombre de la Asignatura. Permite aislar todas las clases de una asignatura en particular.
    • Filtrar por Carrera: Filtro adicional por plan académico/carrera.
    • Botón Limpiar Filtros: Restablece instantáneamente todos los controles y refresca la vista.
    • Insignias de Estado: Indicador en tiempo real de los filtros aplicados y contador de clases visibles (X clases visibles).
  • Soporte en Backend (app/routes/schedule.py):
    • calendar_view: Suministra las listas de classrooms, subjects y careers a la plantilla.
    • calendar_data: Procesa classroom_id, subject_id, career_id y search aplicando cláusulas filter() con JOIN en SQLAlchemy.
    • refetchEvents() de FullCalendar conectado a los eventos change de cada selector para recarga inmediata vía AJAX sin parpadeo de pantalla.

12. Módulo de Gestión de Edificios y Corrección de Vistas de Aulas

  • Corrección de Plantillas de Aulas Faltantes:
    • Se crearon las plantillas app/templates/classrooms/edit.html y app/templates/classrooms/view.html, solucionando el error HTTP 500 (TemplateNotFound: classrooms/edit.html) y la redirección 302 que ocurría al hacer clic en cualquier aula.
    • La vista de ficha técnica del aula ahora muestra detalles de capacidad, piso, estado, edificio y la tabla interactiva de próximas reservas.
  • Modelo y Base de Datos de Edificios (Building):
    • Se creó el modelo app/models/building.py (id, name, code, address, floors, description, is_active, timestamps y relación con Classroom).
    • Migración DDL en PostgreSQL (CREATE TABLE buildings, ALTER TABLE classrooms ADD COLUMN building_id).
    • Actualización de init_db.py y siembra de edificios institucionales iniciales (Edificio Central y Campus Virtual) con vinculación de las 25 aulas existentes.
  • Rutas y Gestión Completa (app/routes/buildings.py):
    • /buildings: Listado con métricas de infraestructura (total de edificios, aulas activas, capacidad total y plantas/pisos), barra de búsqueda y filtros por estado.
    • /buildings/<id>: Vista de detalle con distribución de aulas organizadas visualmente por piso (Planta Baja, Piso 1, Piso 2, etc.), capacidad por sala y acciones directas.
    • /buildings/add y /buildings/<id>/edit: Formularios con validación WTForms para alta y actualización.
    • /buildings/<id>/toggle y /buildings/<id>/delete: Control de activación y eliminación protegida (impide borrar edificios que contengan aulas asignadas).
  • Navegación (app/templates/base.html):
    • Enlace a Edificios (/buildings) incorporado en el menú Classrooms y en el menú administrativo Management.

13. Mapeo de Campos del Google Sheet y Concepto de Turno (Mañana / Tarde)

  • Uso de los 5 Campos de la Cartelera Oficial:
    • Codigo (Subject.code y Commission.code): Almacena el código académico real (INGA0003, ASIG00165). Se expone como badge monoespaciado en eventos de calendario, comisiones y vistas administrativas.
    • Asignatura (Subject.name): Título principal de la materia destacado en texto grande y en negrita en FullCalendar y en la cartelera diaria.
    • Aula (Classroom.code, Classroom.building, Classroom.floor, Classroom.capacity): Normalización de espacios físicos y virtuales vinculados al nuevo modelo Building.
    • Carrera (Career.name y Subject.career_id): Entidad de carrera universitaria con relaciones relacionales normalizadas.
    • Horarios (Commission.schedule, Reservation.start_time, Reservation.end_time): Rango horario parseado hacia objetos datetime nativos para el motor de reservas y el calendario.
  • Modelado y Captura de Turnos (TURNO MAÑANA y TURNO VESPERTINO):
    • Se agregó la columna shift en las tablas commissions y reservations de PostgreSQL y en sus respectivos modelos de SQLAlchemy.
    • El parser de app/services/sheets_importer.py identifica las filas delimitadoras de bloque (TURNO MAÑANA vs TURNO VESPERTINO / TARDE) y corrobora la hora de inicio (< 13:00hs = Mañana, \ge 14:00hs = Tarde).
    • Se sincronizaron todas las comisiones y reservas de cursada: 15 comisiones y 48 reservas en Turno Mañana; 191 comisiones y 364 reservas en Turno Tarde / Vespertino.
    • En el Calendario de Reservas (/schedule/calendar) se añadió el selector Filtrar por Turno (Todos, Mañana, Tarde / Vespertino), permitiendo a los usuarios aislar las franjas horarias y resolver la alta concentración de clases en el horario vespertino (17:30 a 21:30hs).

14. Accesos Directos en Panel, Ocultamiento de Virtuales, Filtro por Piso, Dirección Oficial y Traducción

  • Tarjetas Estadísticas como Accesos Directos Interactivos (app/templates/dashboard.html):
    • Total de Aulas (25): Redirige a /classrooms/list con feedback visual de hover y elevación.
    • Total de Materias (206): Redirige a /schedule/commissions.
    • Reservas de Hoy (18): Redirige a /schedule/today.
    • Esta Semana (103): Redirige a /schedule/calendar.
    • Se añadieron microinteracciones CSS en app/static/css/style.css (.dashboard-stat-card).
  • Ocultar Aulas Virtuales y Filtro por Piso:
    • Calendario (app/templates/schedule/calendar.html): Switch interactivo Ocultar Aulas Virtuales (indica capacidad ilimitada) y selector Filtrar por Piso (Planta Baja, Piso 1, Piso 2, Piso 3, Piso 4). El backend /schedule/calendar-data excluye las aulas virtuales o filtra por número de piso.
    • Cartelera del Día (app/templates/schedule/today.html): Barra de filtros completa con soporte para ocultar virtuales, piso y turno.
    • Listado de Reservas (app/templates/schedule/list.html): Endpoint /schedule/list totalmente operativo con paginación de 20 registros, preservación de query params y filtros de piso, turno, virtuales, estado y rango de fechas.
  • Dirección Oficial de la Sede Central:
    • Se configuró la dirección oficial de la Universidad de la Ciudad de Buenos Aires (UniCABA): Tte. Gral. Juan Domingo Perón 802, CABA en la base de datos, en init_db.py y en el importador app/services/sheets_importer.py.
  • Traducción Integral al Español y Catálogo Babel:
    • Se agregaron traducciones en translations/es/LC_MESSAGES/messages.po y se recompiló el binario .mo con pybabel.
    • Textos traducidos: Buildings \rightarrow Edificios, Management \rightarrow Gestión, Timeline View \rightarrow Vista Cronológica, Grid View \rightarrow Vista en Tabla, Active Classrooms \rightarrow Aulas Ocupadas, All Reservations \rightarrow Todas las Reservas, AI Room Optimizer \rightarrow Optimizador de Aulas, etc.

15. Actualización Integral de Roles RBAC, Capacidad Infinita (∞) y Horarios de Próximas Clases

  • Actualización de Roles, Grupos de Acceso y Matriz de Permisos (app/models/role.py, init_db.py):
    • Se amplió SYSTEM_MODULES para cubrir los 8 módulos del sistema:
      1. classrooms: Aulas y Espacios (físicas, virtuales y capacidades).
      2. buildings: Edificios y Sedes (Sede Central, Campus Virtual, plantas).
      3. reservations: Reservas de Aulas (aprobación, confirmación, cancelación).
      4. schedule: Cartelera y Cronograma (calendario, cartelera del día, comisiones).
      5. academic: Gestión Académica (carreras, asignaturas, códigos).
      6. import: Sincronización Sheets (importador Google Sheets).
      7. optimizer: Optimizador IA (algoritmo genético áulico).
      8. users: Usuarios y Accesos RBAC (cuentas, roles y matriz).
    • Se actualizaron los perfiles estándar (Admin, Docente, Operador, Consulta) en PostgreSQL y en init_db.py.
    • Se tradujeron completamente las plantillas app/templates/admin/roles/list.html y app/templates/admin/roles/edit.html al español.
  • Soporte de Capacidad Infinita (∞) y Aclaración (app/forms/classroom.py, app/templates/classrooms/edit.html):
    • Se actualizó ClassroomForm.capacity con validador personalizado que admite el símbolo ∞, inf, 0 o cualquier capacidad numérica positiva.
    • Se corrigió la validación de floor con InputRequired y NumberRange(min=0) para aceptar Planta Baja (Piso 0).
    • En la interfaz de edición y alta de aulas se incluyó el botón de acción rápida Ilimitada (∞) que rellena automáticamente el campo con el símbolo ∞.
    • Se añadió una leyenda explicativa directa: "Para aulas virtuales o eventos sin aforo restringido, ingrese ∞ o 0 (Capacidad ilimitada)".
    • Se tradujo el botón de guardado a "Guardar Cambios" (reemplazando "Save Changes").
    • El aula virtual institucional (Campus Virtual - VIRTUAL) se actualizó a capacidad 0 (∞), renderizando el badge ∞ Ilimitada en listados y fichas.
  • Próximas Clases: Visualización Exclusiva del Horario (app/templates/dashboard.html, app/templates/classrooms/view.html):
    • En la tabla de Próximas Reservas del panel y en la ficha del aula se cambió la columna a "Horario", removiendo la fecha del calendario y destacando únicamente el rango horario de cursada (ej. 17:30 - 21:30 hs).

16. Desacoplamiento de Hitos Académicos: Catálogo Dinámico de Tipificaciones y Matriz RBAC

  • Modelos de Datos (app/models/milestone.py, app/models/__init__.py):
    • MilestoneType: Catálogo dinámico y administrable con id, name, code (slug único), color (hexadecimal), description, sort_order e is_active.
    • AcademicMilestone: Relacionado mediante clave foránea milestone_type_id a MilestoneType en lugar de strings estáticos.
    • Sembrado de 12 categorías iniciales predeterminadas (Parcial 1, Parcial 2, TP 1, TP 2, TP 3, Recu. P1, Recu. P2, Recu. TP 1, Recu. TP 2, Recu. TP 3, Final, Otro).
  • Matriz de Permisos RBAC (app/models/role.py, init_db.py):
    • Se agregó el módulo milestone_types a SYSTEM_MODULES cubriendo los 9 módulos de la plataforma.
    • Niveles asignados: Admin (RW), Docente (R), Operador (RW), Consulta (R).
  • Vistas y ABM en Management (app/routes/admin.py, app/templates/admin/milestone_types/list.html):
    • Se añadió el ítem de navegación "Tipificaciones" al menú desplegable Management.
    • Tabla completa con badges de color, slugs, switches interactivos de activación/desactivación y modales para dar de alta y editar tipificaciones.
  • API y Modal del Docente (app/routes/main.py, example/aulas-unicaba-mvp/...):
    • Se expuso el endpoint GET /api/milestone-types para consumo desacoplado.
    • Se actualizó el modal de eventos académicos (evento-modal.component.html y .ts) desacoplando las opciones estáticas y cargando dinámicamente el catálogo de tipificaciones activas con fallback automático para funcionamiento offline.

17. Rediseño de Calendario Estilo Google Calendar y Corrección Visual en Eliminación de Roles

  • Manejo de Eventos Simultáneos y Densidad Visual (app/templates/schedule/calendar.html):
    • Se configuró FullCalendar con eventMaxStack: 3, evitando que decenas de reservas concurrentes reduzcan las columnas a franjas milimétricas del 3%.
    • Se habilitó dayMaxEvents: 3, dayMaxEventRows: true y moreLinkClick: 'popover' nativo de Google Calendar con enlaces "+X más".
    • Se fijaron slotMinTime: "07:00:00", slotMaxTime: "23:00:00", slotDuration: "00:30:00", slotLabelInterval: "01:00:00" y nowIndicator: true con la línea roja indicadora de hora actual.
  • Diseño de Tarjetas de Evento Píldora (.gc-event-pill / .gc-month-pill):
    • Estilo Google Calendar con barra lateral de color (magenta UniCABA #B43E8E, ámbar #F59E0B para pendientes, índigo #6366F1 para aulas virtuales).
    • Encabezado compacto con hora de inicio y badge de aula (bi bi-door-open o bi bi-camera-video con fondo suave para VIRTUAL).
    • Tipografía en 0.78rem con elipsis para evitar desbordes y sombras sutiles.
    • Compatibilidad completa con modo oscuro ([data-bs-theme="dark"]).
  • Modal Flotante Interactivo al Clic (#eventPreviewModal):
    • Reemplazo de la navegación directa por un popover flotante estilizado como Google Calendar.
    • Banner artístico superior con accesos rápidos: editar (✏️), eliminar/cancelar (🗑️) y cerrar (✕).
    • Fila principal con indicador cuadrado de color, título completo, código de materia y badge de estado/turno.
    • Lista con íconos: 🕒 horario y fecha completa, 📍 aula/piso/sede (o enlace directo al Campus Virtual), 👤 carrera/comisión/docente, 📝 hito o notas.
    • Botones de acción inferior: "Ver Ficha Completa" y "Cerrar".
  • Corrección de Modal de Eliminación de Roles (app/templates/admin/roles/list.html):
    • Se trasladó el modal de confirmación de eliminación #deleteRoleModal fuera de la celda <td> y de la tabla <table>, situándolo a nivel raíz de la plantilla.
    • Se corrigió el error visual donde el cuadro emergente temblaba, saltaba o se desplazaba por el recálculo de padding/scrollbar de Bootstrap en la celda de la tabla.

18. ABM de Comisiones con Hitos y Aula Virtual, Eliminación de Usuarios y Renombrado de Roles (Bedelía y Alumno)

  • Renombrado de Roles Institucionales (init_db.py, base de datos PostgreSQL):
    • Operador pasó a denominarse Bedelía con foco en la gestión operativa de aulas, sedes, reservas, comisiones, importación y cartelera.
    • Consulta pasó a denominarse Alumno con acceso de solo lectura al cronograma general, asignaturas y aulas.
    • Se ejecutó la actualización en caliente de los registros existentes en PostgreSQL y se actualizaron los sembrados en init_db.py.
  • Corrección Visual de Tipificaciones de Hitos (app/templates/admin/milestone_types/list.html):
    • Se ajustó el contenedor a pt-5 mt-4 evitando que el encabezado quede solapado bajo la barra de navegación fija.
    • Se limpió la duplicación del nombre en la tabla (se eliminó la repetición del texto dentro de la celda).
    • Se agregó la paleta rápida de colores institucionales UniCABA al modal de edición.
    • Se agregó el botón y modal global de confirmación de eliminación #deleteMilestoneTypeModal sin saltos visuales.
  • Eliminación de Usuarios (app/routes/admin.py, app/templates/admin/users/list.html):
    • Se implementó el endpoint POST /admin/users/delete/<id> protegido contra la eliminación del usuario en sesión activa y del administrador único.
    • Desvinculación automática de comisiones como docente para prevenir violaciones de integridad referencial.
    • Botón rojo de eliminación con ícono de papelera y modal global #deleteUserModal fuera de la tabla para evitar desplazamientos.
  • ABM Integral de Comisiones con Aulas Virtuales e Hitos (app/models/subject.py, app/models/milestone.py, app/routes/schedule.py, app/templates/schedule/commissions.html, app/templates/base.html):
    • Columna y Enlace de Aula Virtual: Se añadió el campo virtual_link (VARCHAR 500) a commissions. Permite registrar enlaces directos a Zoom, Microsoft Teams, Google Meet o Campus Virtual.
    • Hitos de Evaluación vinculados a Comisiones: Se añadió commission_id a academic_milestones. Permite asociar parciales, TPs y recuperatorios directamente a la comisión de cursada.
    • Permisos para Profesores, Bedelía y Admin: Los roles Docente, Bedelía y Admin pueden configurar el aula virtual y registrar hitos académicos en sus comisiones.
    • Navegación: Acceso directo a Comisiones y Cursadas disponible en la barra superior (menús Schedule y Management).
    • Modales interactivos: Creación de comisión (#modalAddCommission), edición (#modalEditCommission), configuración de aula virtual (#modalVirtualLink), programación de hitos (#modalAddMilestone) y eliminación segura (#deleteCommissionModal).

19. Corrección de Redundancia en Nombre de Aula en Formulario y Resumen de Reserva

  • Desplegable de Selección de Aula (app/routes/schedule.py):
    • Se corrigió la tupla de opciones en form.classroom_id.choices. Anteriormente concatenaba c.code_display y c.name (ambas propiedades computaban "Edificio Central-Aula 204"), provocando el texto repetido Edificio Central-Aula 204 - Edificio Central-Aula 204 (Cap: 35).
    • Se simplificó a f'{c.building} - {c.code} (Cap: {c.capacity_display})', mostrando limpiamente Edificio Central - Aula 204 (Cap: 35).
  • Cuadro de Resumen en Tiempo Real (app/templates/schedule/add.html):
    • Se corrigió la función JavaScript updateSummary() que concatenaba ${code_display} - ${name}.
    • Ahora renderiza de forma limpia y directa ${classroomData[classroomId].code_display} (${capLabel}: ${classroomData[classroomId].capacity}).
  • Formato de Códigos de Comisión (app/models/subject.py):
    • Se optimizó Commission.get_full_code() para no repetir el año cuando ya viene incluido en el string del semestre (ej: 2026-2).

20. Refactorización Integral de Espacios Físicos: Desacoplamiento de Edificio, Piso y Aula

  • Modelo de Datos y Base de Datos (app/models/classroom.py, app/models/building.py):
    • Desacoplamiento de Edificio: Se reforzó la clave foránea Classroom.building_id vinculada a buildings.id. El nombre del edificio se obtiene directamente a través de building_entity manteniendo retrocompatibilidad.
    • Pisos Flexibles (Alfanuméricos): Se migró la columna classrooms.floor a VARCHAR(50), permitiendo registrar tanto valores numéricos como designaciones textuales ("PB", "Planta Baja", "1", "2°P", "Subsuelo").
    • Propiedades de Formato: Se incorporaron floor_display ("Planta Baja", "Piso X"), is_virtual y location_display (Edificio · Piso · Aula).
    • Rutina de Migración: Se verificó la integridad referencial garantizando que todas las aulas existentes queden asociadas a su building_id correspondiente en PostgreSQL.
  • Formulario y Flujo Dinámico de Creación/Edición de Aulas (app/forms/classroom.py, app/routes/classrooms.py, app/templates/classrooms/add.html, app/templates/classrooms/edit.html):
    • Selector de Edificio con Alta Dinámica: Desplegable con las sedes activas más la opción destacada + Crear nuevo edificio. Al seleccionarla, despliega de forma inmediata un campo de texto para escribir el nuevo edificio sin abandonar la pantalla.
    • Sugerencia de Pisos: Campo con datalist interactivo que sugiere los pisos ya cargados en la sede seleccionada o permite tipear uno nuevo libremente.
    • Validación de Unicidad: Se valida tanto en cliente como en servidor que no existan aulas duplicadas dentro del mismo edificio y piso.
  • Selectores en Cascada para Reservas (app/templates/schedule/add.html, app/routes/schedule.py):
    • Flujo Dependiente:
      1. Edificio / Sede: Selección de sede física o modalidad Campus Virtual.
      2. Piso / Nivel: Se puebla dinámicamente con los pisos disponibles de esa sede (se deshabilita automáticamente si es Virtual).
      3. Aula / Espacio: Filtra y muestra únicamente las aulas del edificio y piso seleccionados con su capacidad.
    • Resumen Desglosado con Badges:
      • 🏢 Edificio / Sede: Muestra el nombre limpio de la sede.
      • 🪜 Piso / Nivel: Muestra la planta o "Remoto (Virtual)".
      • 🚪 Aula Seleccionada: Muestra el número de aula y su capacidad.
  • Filtros en Listado de Aulas y Calendario (app/templates/classrooms/list.html, app/templates/schedule/calendar.html, app/templates/schedule/view.html):
    • Listado de Aulas: Incorporación de dropdowns independientes para filtrar por Edificio y Piso en la barra superior. Tarjetas rediseñadas con badges distintivos de sede y piso.
    • Calendario Semanal: Agregado el filtro Filtrar por Edificio en la barra superior junto al filtro de Piso.
    • Modal Estilo Google Calendar: La sección de espacio físico desglosa limpiamente en badges: 🏢 Edificio, 🪜 Piso y 🚪 Aula.

21. Corrección de Linter y Renderizado de Barras en Métricas de Ocupación (2026-09-04)

  • Plantilla de Métricas de Ocupación (app/templates/admin/metrics/occupancy.html):
    • Eliminación de Falsos Positivos de CSS en el IDE: Se resolvieron los 12 errores de sintaxis (property value expected, at-rule or selector expected) reportados por el analizador HTML/CSS del IDE en las tarjetas de KPI (Tasa de Ocupación Hoy, Semanal, Mensual) y en la distribución por Piso / Nivel.
    • Atributos Semánticos HTML5: Se sustituyó la inyección directa de Jinja2 en atributos style="width: {{ ... }}%;" por el atributo de datos estándar data-width="{{ ... }}%".
    • Inicialización y Animación Dinámica en JS:
      • Se configuró en CSS (.metric-bar-fill { width: 0; }) un ancho base inicial.
      • Se implementó un script en el bloque extra_js que asigna el ancho dinámico al cargar el DOM (DOMContentLoaded).
      • En conjunto con transition: width 0.6s ease;, las barras se expanden de forma fluida y visualmente atractiva al cargar la pantalla.

22. Actualización de Instalador, Backup de Base de Datos y Despliegue en Proxmox LXC (2026-09-04)

  • Backup de Base de Datos (classrooms_db.sql):
    • Extracción del Snapshot Completo: Volcado íntegro de la base de datos PostgreSQL activa (classrooms_db) que alimenta el sistema en producción/local.
    • Contenido Respaldado: 25 aulas físicas y virtuales, 206 comisiones, 206 asignaturas, 412 reservas, 24 carreras, 12 tipos de hitos, matrices de permisos RBAC y usuarios administradores.
    • Compatibilidad Multi-Versión: Se sanitizó la directiva \restrict específica de PostgreSQL 18 para garantizar compatibilidad nativa con PostgreSQL 14, 15, 16, 17 y 18.
  • Actualización del Instalador del Sistema para Proxmox LXC (install.sh):
    • Ejecución 100% Automatizada (Zero-Touch): Se eliminaron todos los prompts interactivos (read -rp), permitiendo que el script corra de inicio a fin de forma totalmente desatendida.
    • Copia de Configuración Activa del Sistema: Se configuraron como valores por defecto los parámetros reales del sistema en ejecución (DATABASE_URL=postgresql://postgres:password@localhost:5432/classrooms_db, usuario postgres, base classrooms_db y contraseña password), garantizando paridad exacta con el entorno local.
    • Compatibilidad Dual de Usuarios: Configuración tanto del superusuario postgres como de eduspace_user con los mismos privilegios para compatibilidad inmediata ante cualquier variación de .env.
    • Optimización Proxmox LXC: Compatible con contenedores Debian 11/12 y Ubuntu 20.04/22.04/24.04 en Proxmox VE.
    • Migración Automática de Directorio Robusta: Si el repositorio se clona en /root, se traslada automáticamente a /opt/admin-edu-space mediante sincronización directa con cp -a, limpiando posibles directorios anidados residuales y deteniendo el servicio previamente para evitar errores de colisión (Directory not empty).
    • Despliegue del Motor PostgreSQL Nativo: Instalación desatendida del motor en el LXC, inicialización dinámica del servicio y bucle de healthcheck de conexión.
    • Restauración Automatizada y Permisos: Detección automática de classrooms_db.sql, recreación limpia, asignación de permisos GRANT ALL en el esquema public, todas las tablas y secuencias a postgres y eduspace_user.
    • Entorno Virtual Nativo (venv): Configuración en /opt/admin-edu-space/venv con dependencias del sistema y gunicorn.
    • Servicio Systemd de Producción: Configuración y activación de admin-edu-space.service en el puerto 5000.
    • Ejecución Idempotente: Integración de init_db.py tras la restauración para validar la coherencia del esquema y roles.
  • Utilidad de Backup Continuo (backup_db.sh):
    • Script automatizado para generar copias de seguridad fechadas en backups/ y actualizar simultáneamente classrooms_db.sql para facilitar la migración entre contenedores LXC.
  • Documentación y Tutorial en README (README.md):
    • Guía paso a paso para la creación del contenedor LXC en Proxmox VE (especificaciones de CPU, RAM, disco y habilitación de nesting=1).
    • Instrucciones completas de instalación desatendida con install.sh.
    • Corrección de comandos de inicio apuntando al punto de entrada oficial wsgi.py.
    • Guía paso a paso para la generación y restauración de backups.
  • Dependencias (requirements.txt):
    • Se incorporó formalmente gunicorn>=21.2.0 al listado de dependencias del proyecto.

23. Resolución de Error 500 (Internal Server Error) y Robustecimiento del Instalador Proxmox LXC (2026-09-04)

  • Causa Raíz del Error HTTP 500 en Despliegues Existentes:
    • Al desplegar sobre contenedores LXC con archivos .env preexistentes (por ejemplo, con usuario de base de datos personalizado eduadmin extraído de DATABASE_URL), el instalador anterior no otorgaba permisos de tablas ni secuencias al usuario dinámico (GRANT solo cubría postgres y eduspace_user), bloqueando las consultas de Flask a PostgreSQL con excepciones de privilegios insuficientes (permission denied for relation ...).
    • Los registros de excepción de Gunicorn se canalizaban exclusivamente a un archivo sin --capture-output, impidiendo su visualización inmediata en journalctl -u admin-edu-space -f.
  • Solución y Mejoras en el Instalador (install.sh):
    • Extracción Dinámica de Credenciales: Parseo bidireccional de DATABASE_URL y variables individuales (DB_USERNAME, DB_PASSWORD, DB_NAME), garantizando compatibilidad absoluta con cualquier configuración de conexión.
    • Aprovisionamiento Universal de Roles PostgreSQL: Creación y asignación de contraseña y privilegios SUPERUSER CREATEDB a todos los usuarios detectados (DB_USER, DB_USERNAME, postgres, eduspace_user).
    • Garantía Integral de Permisos DDL y DML: Tras restaurar classrooms_db.sql, se ejecutan GRANT ALL en DATABASE, SCHEMA public, ALL TABLES, ALL SEQUENCES, ALL FUNCTIONS y ALTER DEFAULT PRIVILEGES para todos los usuarios.
    • Configuración de Autenticación Local (pg_hba.conf): Ajuste automático en Debian/Ubuntu para que conexiones locales (127.0.0.1, ::1 y sockets UNIX) no fallen por discrepancias de método de cifrado (scram-sha-256/md5).
    • Logs en Tiempo Real en Systemd: Reconfiguración de Gunicorn en el servicio con --error-logfile -, --capture-output y --enable-stdio-inheritance, permitiendo que cualquier excepción de Python/Flask se visualice en tiempo real vía journalctl -u admin-edu-space -f.
    • Healthcheck HTTP Automatizado: Verificación al final del despliegue con curl http://127.0.0.1:5000/, validando que el servidor web responda exitosamente (HTTP 200/302) antes de concluir la instalación.
  • Carga Segura de Configuración (config/config.py):
    • Integración explícita de load_dotenv para garantizar que scripts y workers carguen .env de forma confiable.
    • Normalización automática de URIs con esquema heredado postgres:// hacia el estándar postgresql://.

24. Auto-recuperación y Creación de Cluster PostgreSQL 15 en Debian 12 Proxmox LXC (2026-09-04)

  • Diagnóstico de Arranque de PostgreSQL en Debian 12 (Bookworm):
    • En contenedores Proxmox VE con Debian 12 limpio, apt-get install postgresql puede no inicializar automáticamente el cluster main si los locales no estaban completamente registrados al compilar el paquete, o el directorio de sockets /run/postgresql puede no tener los permisos adecuados en el filesystem tmpfs.
    • El comando genérico systemctl start postgresql no inicia el motor si la instancia nativa postgresql@15-main no está creada ni generada.
  • Auto-detección y Resiliencia en install.sh:
    • Permisos del Socket UNIX: Creación y asignación explícita de permisos chmod 2775 con propietario postgres:postgres sobre /run/postgresql y /var/run/postgresql.
    • Creación Automática del Cluster: Chequeo con pg_lsclusters; si el cluster main no existe, se ejecuta automáticamente pg_createcluster ${PG_MAJOR} main --locale en_US.UTF-8 --start.
    • Control Systemd Específico: Arranque y habilitación de la unidad del cluster nativo postgresql@${PG_MAJOR}-main y pg_ctlcluster.
    • Diagnóstico de Salida: En caso de contingencias en el host Proxmox, el script imprime directamente el estado del cluster con pg_lsclusters y las últimas líneas de registro de PostgreSQL.
  • Verificación e Instalación Automática de Git (install.sh):
    • Se incorporó en el inicio del script la verificación de presencia de git (command -v git). Si no está instalado en el sistema o contenedor, el script ejecuta automáticamente apt-get update && apt-get install -y git antes de configurar directivas de seguridad (safe.directory) o manipular el repositorio.

25. Roadmap Académico, Propuesta de Mejora (MVC, JWT, Sprint Security), Valor Agregado y Desafíos Técnicos (2026-09-05)

  • Propuesta de Mejora Arquitectónica y Gobernanza (README.md):
    • Arquitectura MVC Rigurosa & Clean Architecture: Formalización del desacoplamiento en 4 capas: Dominio (app/models/), Presentación (Jinja2 + JSON API), Controladores delgados (app/routes/) y Capa de Negocio (app/services/ y app/repositories/). Incorporación de esquemas tipados DTO (Pydantic) para validación de contratos de entrada.
    • Autenticación Stateless con JWT (JSON Web Tokens): Arquitectura híbrida que preserva sesiones seguras para el panel web y habilita autenticación stateless basada en tokens JWT para microservicios y aplicaciones móviles mediante el patrón Dual Token (Access Token de 15 min + Refresh Token en cookie HttpOnly de 7 días, con soporte de lista negra de revocación en Redis).
    • Seguridad Empresarial ("Sprint / Spring Security"): Adopción de los estándares de Spring Security adaptados a Python/Flask: cadena centralizada de filtros (SecurityFilterChain), control declarativo a nivel método (@require_permission), rate limiting dinámico contra fuerza bruta (Flask-Limiter) y bitácora de auditoría inmutable de eventos (AuditLog).
  • Planificación por Sprints (Roadmap):
    • Sprint 1 (MVC & Servicios): Desacoplamiento de lógica de negocio a servicios y repositorios; esquemas DTO de validación.
    • Sprint 2 (JWT & API Gateway): Endpoints /api/auth/login, /refresh, /logout, decoradores @jwt_required y middleware Bearer Token.
    • Sprint 3 (Sprint Security & RBAC): Pipeline SecurityFilterChain, control a nivel método, rate limiting y auditoría inmutable.
    • Sprint 4 (Optimizador & Concurrencia): Paralelización del algoritmo genético áulico e integración con microservicios Spring Boot.
  • Valor Agregado Institucional:
    • Optimización Edilicia: Aprovechamiento del aforo en Sede Central (Perón 802) mediante asignación algorítmica por capacidad real y pisos.
    • Reducción de Tiempo: Ahorro del 85% en resolución de conflictos y asignación horaria en turnos de alta concentración (Vespertino).
    • Eficiencia Extrema en Proxmox LXC: Despliegue nativo con consumo inferior a 180MB de RAM frente a alternativas pesadas en Docker/Kubernetes.
  • Desafíos Técnicos Superados y Próximos (Challenges):
    • Resolución del problema NP-Hard de asignación horaria mediante Algoritmo Genético multicriterio.
    • Pipeline ETL tolerante a fallos para ingesta y normalización de carteleras no estructuradas de Google Sheets.
    • Despliegue automatizado, auto-recuperable y portátil para Proxmox VE LXC en Debian 12 (Bookworm) y Debian 13 (Trixie).

26. Creación de Rama rc y Publicación del Primer Release Oficial v1.0.1 (2026-09-05)

  • Rama rc (Release Candidate):
    • Creación y sincronización de la rama rc con el estado íntegro de dev (commit 566908f), estableciendo el seguimiento remoto gitea/rc.
  • Publicación de Tag y Release v1.0.1 en Gitea:
    • Generación de la etiqueta anotada v1.0.1 a partir de rc con notas de versión detalladas.
    • Publicación remota en Gitea (git push gitea v1.0.1), disponibilizando la primera versión candidata/estable oficial para producción en Proxmox VE LXC con empaquetado de artefactos descargables (.zip y .tar.gz).

27. Implementación Integral del Roadmap (Sprints 1, 2 y 3): MVC, Stateless JWT y Spring Security (2026-09-05)

  • Sprint 1: Refactorización MVC & Capa de Servicios (app/repositories/, app/services/, app/schemas/):
    • Capa de Repositorios: Creación de BaseRepository[T] genérico, ClassroomRepository (con filtros de aulas físicas y virtuales), ReservationRepository (con detección de solapamiento horario y carga ansiosa joinedload), y UserRepository.
    • Contratos DTOs (Pydantic v2): Creación de esquemas tipados de validación: ClassroomCreateDTO, ClassroomUpdateDTO, ReservationCreateDTO, ReservationUpdateDTO y LoginDTO.
    • Capa de Servicios Desacoplada: ClassroomService (gestión de unicidad por edificio/piso y aforo virtual), ReservationService (resolución de conflictos y validación de capacidad), y UserService (autenticación y extracción de permisos RBAC).
    • Controladores Delgados: Refactorización de rutas web delegando validaciones y persistencia a los servicios (routes/classrooms.py).
  • Sprint 2: Autenticación Stateless JWT & API Gateway (app/routes/api/, app/utils/jwt_decorators.py):
    • Dependencias: Incorporación de PyJWT>=2.8.0 y pydantic>=2.0.0 en requirements.txt.
    • Servicio Criptográfico (JWTService): Implementación del patrón Dual Token (Access Token de 15 min con claims RBAC completos + Refresh Token de 7 días para renovación silenciosa en cookie HttpOnly). Soporte de lista negra de revocación en memoria.
    • Decoradores de Seguridad REST: @jwt_required y @jwt_role_required para inspección de cabeceras Authorization: Bearer <token>.
    • Endpoints API RESTful (v1):
      • POST /api/v1/auth/login: Entrega de par de tokens y perfil de usuario.
      • POST /api/v1/auth/refresh: Renovación transparente de tokens sin reingreso de credenciales.
      • POST /api/v1/auth/logout: Revocación del token activo e invalidación de cookies.
      • GET /api/v1/auth/me: Perfil y matriz de permisos del usuario autenticado.
      • GET /api/v1/classrooms y POST /api/v1/classrooms: Consulta y creación tipada de aulas.
      • GET /api/v1/reservations y POST /api/v1/reservations: Consulta y gestión de reservas con resolución de conflictos.
  • Sprint 3: Seguridad Empresarial "Sprint / Spring Security" (app/security/, AuditLog):
    • SecurityFilterChain: Pipeline centralizado que inyecta cabeceras HTTP de blindaje OWASP (X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, X-XSS-Protection, Strict-Transport-Security) y Rate Limiting dinámico contra fuerza bruta (15 peticiones/minuto en /login y /api/v1/auth/login).
    • Seguridad Declarativa a Nivel Método (@require_permission): Decorador que valida granularmente el nivel de acceso ('read', 'read_write', 'admin') tanto en sesiones web como en API JWT.
    • Bitácora Inmutable de Auditoría: Modelo de persistencia AuditLog y servicio AuditService.log() con inferencia automática de usuario actuante (JWT/Web) e IP de origen (X-Forwarded-For).
  • Batería de Pruebas Automatizadas:
    • Creación de tests unitarios y de integración en tests/test_services_and_dto.py, tests/test_jwt_and_api.py y tests/test_security_chain.py (18/18 pruebas aprobadas).

28. Selector de Vista Universal: Tabla / Grilla vs. Tarjetas / Mosaico (2026-09-05)

  • Arquitectura Extensible de Visualización (app/static/js/view_switcher.js y app/templates/partials/view_switcher.html):
    • Desarrollo del módulo ViewSwitcherManager que desacopla la lógica de presentación de los registros. Permite añadir futuras vistas (ej. kanban, timeline, compacta) mediante atributos de datos data-view y data-view-container sin tocar el backend.
    • Conmutación Inmediata: Cambio instantáneo de vista en el cliente sin recarga de página.
    • Persistencia de Preferencias: Almacenamiento local mediante localStorage (view_preference_{context}) para conservar la elección del usuario entre sesiones.
    • Preservación de Estado y Filtros: Inyección reactiva del parámetro display en formularios de búsqueda/filtro y actualización silenciosa de la URL (window.history.replaceState), garantizando que búsquedas, ordenamientos y paginaciones conserven la vista activa.
  • Integración en Módulo de Aulas y Espacios (app/templates/classrooms/list.html):
    • Vista de Tabla / Grilla: Columnas detalladas (Código, Edificio/Sede, Piso, Capacidad, Equipamiento, Ocupación D/S/M, Estado y Acciones).
    • Vista de Tarjetas / Mosaico: Cuadrícula responsive (1 a 4 columnas) con badges semánticos, indicadores de piso y barras de porcentaje de uso.
  • Integración en Módulo de Reservas y Cronograma (app/templates/schedule/list.html):
    • Vista de Tabla: Análisis denso de reservas con estado, comisión, horario y aula.
    • Vista de Tarjetas: Fichas visuales con badges destacados de fecha/hora, materia, turno, docente a cargo y enlaces a aulas virtuales.
  • Pruebas y Verificación:
    • Incorporación de tests en tests/test_view_switcher.py, validando renderizado de selectores y contenedores en ambas rutas (20/20 pruebas aprobadas).