feat: actualizar install.sh para Proxmox LXC con venv, snapshot classrooms_db.sql, backup_db.sh y fix linter en occupancy
This commit is contained in:
+33
-8
@@ -1,6 +1,6 @@
|
||||
# Resumen de Cambios - Sesión de Desarrollo (Rama `dev`)
|
||||
|
||||
**Fecha:** 2026-09-02 / 2026-09-03
|
||||
**Fecha:** 2026-09-02 / 2026-09-03 / 2026-09-04
|
||||
**Proyecto:** Admin Edu-Space (Flask + PostgreSQL)
|
||||
**Rama:** `dev`
|
||||
|
||||
@@ -345,14 +345,39 @@
|
||||
* **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](file:///c:/Workspace/admin-edu-space/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](file:///c:/Workspace/admin-edu-space/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](file:///c:/Workspace/admin-edu-space/install.sh)):**
|
||||
* **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:** Si el repositorio se clona en `/root`, se traslada automáticamente a `/opt/admin-edu-space` para garantizar permisos de ejecución del usuario del servicio (`eduspace`).
|
||||
* **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 al usuario `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](file:///c:/Workspace/admin-edu-space/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](file:///c:/Workspace/admin-edu-space/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](file:///c:/Workspace/admin-edu-space/wsgi.py).
|
||||
* Guía paso a paso para la generación y restauración de backups.
|
||||
* **Dependencias ([requirements.txt](file:///c:/Workspace/admin-edu-space/requirements.txt)):**
|
||||
* Se incorporó formalmente `gunicorn>=21.2.0` al listado de dependencias del proyecto.
|
||||
|
||||
|
||||
@@ -1,315 +1,306 @@
|
||||
# Panel de Administración Edu-Space (UniCABA)
|
||||
|
||||
Plataforma integral de gestión áulica, asignación de espacios, control de horarios y reservas académicas para la **Universidad de la Ciudad de Buenos Aires (UniCABA)**, desarrollada con **Python / Flask**, **SQLAlchemy**, **PostgreSQL** y **Bootstrap 5**.
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ Sede Institucional y Ubicaciones
|
||||
* **Sede Central UniCABA:** Tte. Gral. Juan Domingo Perón 802, Ciudad Autónoma de Buenos Aires (CABA) — Aulas de grado, auditorio principal y laboratorios distribuidos desde Planta Baja (Piso 0) hasta el 4.° Piso.
|
||||
* **Campus Virtual:** Espacio áulico digital integrado (Microsoft Teams / Moodle) configurado con **capacidad ilimitada ($\infty$)**.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Características Principales
|
||||
|
||||
### 🛡️ 1. Control de Accesos RBAC Granular (Matriz estilo FortiGate)
|
||||
* **Gestión por Módulos:** Permisos independientes por cada área del sistema:
|
||||
1. **Aulas y Espacios:** Creación, edición, capacidad y estado de aulas físicas y virtuales.
|
||||
2. **Edificios y Sedes:** Administración de sedes, pisos y arquitectura edilicia.
|
||||
3. **Reservas de Aulas:** Solicitud, aprobación, confirmación y cancelación.
|
||||
4. **Cartelera y Cronograma:** Calendario interactivo, visualización diaria y comisiones.
|
||||
5. **Gestión Académica:** Carreras, asignaturas, códigos y comisiones.
|
||||
6. **Sincronización Sheets:** Importador y sincronizador de Google Sheets.
|
||||
7. **Optimizador IA:** Algoritmo genético de distribución y resolución de conflictos.
|
||||
8. **Usuarios y Accesos RBAC:** Cuentas, roles y matriz de permisos.
|
||||
* **Niveles de Permiso Granulares:** `Ninguno`, `Solo Lectura` o `Lectura y Escritura`.
|
||||
* **Controles Rápidos:** Botón *"Establecer Todos"* para configuración masiva inmediata.
|
||||
* **Roles Predefinidos:** `Admin` (control total), `Docente`, `Operador` y `Consulta`.
|
||||
|
||||
### 📊 2. Sincronización e Importación desde Google Sheets
|
||||
* Integración directa con la cartelera académica de UniCABA en Google Sheets.
|
||||
* Extracción e importación normalizada de los 5 campos clave:
|
||||
* **Código Académico:** Códigos reales de asignatura (ej. `INGA0003`, `ASIG00165`).
|
||||
* **Asignatura:** Nombre oficial de la materia.
|
||||
* **Aula:** Mapeo a aulas físicas (PB a 4.° piso) o aula `VIRTUAL` ($\infty$).
|
||||
* **Carrera:** Normalización y vinculación relacional con entidades de carrera.
|
||||
* **Horarios y Turnos:** Detección automática de franjas horarias y delimitadores de bloque (`Turno Mañana` y `Turno Tarde / Vespertino`).
|
||||
|
||||
### 📅 3. Calendario Interactivo de Reservas (FullCalendar)
|
||||
* **Visualización Semanal, Mensual y Diaria:** Grilla ágil de alta legibilidad con jerarquía visual:
|
||||
* Asignatura destacada en tipografía grande y negrita.
|
||||
* Badges con código de materia, aula asignada, turno y franja horaria.
|
||||
* **Filtros Dinámicos Combinables:**
|
||||
* Filtrar por **Aula**.
|
||||
* Filtrar por **Materia / Asignatura**.
|
||||
* Filtrar por **Carrera**.
|
||||
* Filtrar por **Turno** (`Turno Mañana` / `Turno Tarde`).
|
||||
* Filtrar por **Piso / Planta** (`Planta Baja`, `Piso 1`, `Piso 2`, `Piso 3`, `Piso 4`).
|
||||
* **Switch "Ocultar Aulas Virtuales":** Aísla la ocupación física real en el edificio central excluyendo los eventos virtuales de capacidad infinita.
|
||||
* **Ficha Modal Instantánea:** Vista previa detallada al hacer clic en cualquier evento.
|
||||
|
||||
### 📋 4. Cartelera del Día y Listado de Reservas
|
||||
* **Cartelera del Día (`/schedule/today`):**
|
||||
* **Vista Cronológica (Timeline):** Bloques horarios de 07:00 a 22:00 hs con indicador de ocupación.
|
||||
* **Vista en Tabla:** Lista ordenada con acciones rápidas y detalles de comisión.
|
||||
* Filtros directos por piso, turno y switch para ocultar virtuales.
|
||||
* **Listado Paginado de Reservas (`/schedule/list`):**
|
||||
* Paginación de 20 registros por página preservando filtros aplicados.
|
||||
* Búsqueda por texto, estado (`Confirmada`, `Pendiente`, `Cancelada`), rango de fechas y piso.
|
||||
|
||||
### 🏫 5. Gestión de Aulas con Capacidad Infinita ($\infty$)
|
||||
* Soporte nativo para el símbolo **`∞`** o `0` para designar aulas virtuales o espacios sin aforo restringido.
|
||||
* Botón de un solo clic `Ilimitada (∞)` en formularios de alta y edición con leyendas explicativas.
|
||||
* Badges distintivos `∞ Ilimitada` en listados, tarjetas y fichas técnicas.
|
||||
|
||||
### 🤖 6. Optimizador Inteligente de Aulas (Algoritmo Genético)
|
||||
* Motor de optimización heurística para asignación automática de aulas a comisiones.
|
||||
* Pondera capacidad requerida vs. capacidad de aula, equipamiento tecnológico y minimización de desplazamientos entre pisos.
|
||||
|
||||
### 🎨 7. Experiencia de Usuario, Temas y Localización
|
||||
* **Tema Oscuro / Claro:** Alternador con botón flotante y persistencia en `localStorage` y perfil de usuario.
|
||||
* **Soporte Multilingüe:** Internacionalización integral (Español e Inglés) compilada con GNU gettext (`pybabel`).
|
||||
* **Tarjetas Estadísticas Interactivas:** Accesos directos rápidos desde el panel general hacia las distintas secciones operativas.
|
||||
|
||||
### 🏛️ 8. Sistema Integral de Gestión Institucional (8 Módulos)
|
||||
El menú desplegable **Gestión** en la barra de navegación organiza las áreas operativas de la universidad:
|
||||
* **Espacios e Infraestructura:**
|
||||
* **Edificios y Sedes (`/buildings/list`):** Administración de edificios físicos, arquitectura y estado edilicio.
|
||||
* **Aulas Presenciales (`/classrooms/list?view_mode=physical`):** Catálogo de aulas físicas por piso, capacidades y zócalos compactos con métricas de ocupación en tiempo real (día, semana, mes).
|
||||
* **Aulas Virtuales con Reunión (`/classrooms/list?view_mode=virtual`):** Campus Virtual con salas a demanda agrupadas por comisión, capacidad ilimitada ($\infty$) y enlaces directos de acceso a videollamadas (Zoom, Google Meet, Microsoft Teams).
|
||||
* **Gestión de Ocupación y Métricas (`/admin/occupancy-metrics`):** Dashboard analítico de tasas de ocupación horaria (día, semana, mes), horas pico, distribución por turno y comparativa física vs. virtual.
|
||||
* **Gestión Académica:**
|
||||
* **Carreras (`/admin/careers`):** ABM de carreras universitarias y planes de estudio con contador de asignaturas y comisiones activas.
|
||||
* **Asignaturas (`/admin/subjects`):** Catálogo institucional de materias con créditos, departamentos y filtros por carrera y estado.
|
||||
* **Comisiones y Cursadas (`/schedule/commissions`):** Gestión de cursadas, turnos, docentes, enlaces virtuales e hitos de evaluación.
|
||||
* **Ciclo Lectivo (`/admin/academic-terms`):** Modelo y panel de administración de períodos académicos, cuatrimestres y activación del ciclo lectivo en curso.
|
||||
* **Administración & Seguridad:**
|
||||
* **Usuarios y Roles RBAC:** Cuentas, asignación de permisos matriciales y auditoría.
|
||||
* **Tipificaciones de Hitos:** Catálogo de tipos de hitos de evaluación académica.
|
||||
* **Sincronización Sheets:** Integración con Google Sheets para actualización en lote.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Stack Tecnológico
|
||||
|
||||
| Componente | Tecnología |
|
||||
| :--- | :--- |
|
||||
| **Backend** | Python 3.12+ / Flask |
|
||||
| **Base de Datos** | PostgreSQL 16+ / SQLAlchemy ORM |
|
||||
| **Seguridad & Sesiones** | Flask-Login / Werkzeug / CSRF Protect |
|
||||
| **Formularios** | Flask-WTF / WTForms |
|
||||
| **Internacionalización** | Flask-Babel / GNU gettext |
|
||||
| **Frontend** | Bootstrap 5.3, Bootstrap Icons, Vanilla JS, CSS Variables |
|
||||
| **Calendario** | FullCalendar 5.11 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Instalación y Puesta en Marcha
|
||||
|
||||
### ⚡ Opción A — Deploy Automático en Debian 12 LXC (Recomendado)
|
||||
|
||||
El repositorio incluye un instalador interactivo (`install.sh`) que configura todo el entorno de producción automáticamente: dependencias del sistema, PostgreSQL, entorno virtual Python, inicialización de base de datos y servicio systemd con Gunicorn.
|
||||
|
||||
#### Prerrequisitos
|
||||
* Contenedor LXC o servidor con **Debian 12 (Bookworm)**
|
||||
* Acceso **root** (o `sudo`)
|
||||
* Conexión a Internet
|
||||
|
||||
#### Pasos
|
||||
|
||||
```bash
|
||||
# 1. Clonar el repositorio
|
||||
git clone https://gitea.oemspot.com.ar/carlostellocba/admin-edu-space.git
|
||||
cd admin-edu-space
|
||||
|
||||
# 2. Dar permisos de ejecución al instalador
|
||||
chmod +x install.sh
|
||||
|
||||
# 3. Ejecutar el instalador como root
|
||||
sudo bash install.sh
|
||||
```
|
||||
|
||||
Durante la ejecución el instalador solicitará únicamente la **contraseña para el usuario de base de datos** (si se deja vacío usa `eduspace2024` por defecto).
|
||||
|
||||
#### Qué instala y configura automáticamente
|
||||
|
||||
| Paso | Acción |
|
||||
| :--- | :--- |
|
||||
| 1 | Configura locale `en_US.UTF-8` en el sistema (previene errores de encoding) |
|
||||
| 2 | Instala Python 3, venv, pip, build-tools, libpq-dev y PostgreSQL |
|
||||
| 3 | Inicia y habilita el servicio PostgreSQL para el arranque automático |
|
||||
| 4 | Genera `SECRET_KEY` aleatoria y crea el archivo `.env` de producción |
|
||||
| 5 | Crea el usuario `eduspace_user` y la base de datos `classrooms_db` en PostgreSQL |
|
||||
| 6 | Crea el virtualenv e instala todas las dependencias + Gunicorn |
|
||||
| 7 | Ejecuta `init_db.py` (tablas, roles RBAC, edificios y usuario admin inicial) |
|
||||
| 8 | Crea el usuario del sistema `eduspace` (sin shell, por seguridad) |
|
||||
| 9 | Registra y arranca el servicio systemd `admin-edu-space` en el puerto **5000** |
|
||||
| 10 | Muestra resumen con URL, credenciales y comandos de gestión |
|
||||
|
||||
#### Comandos de gestión post-instalación
|
||||
|
||||
```bash
|
||||
# Ver estado del servicio
|
||||
systemctl status admin-edu-space
|
||||
|
||||
# Ver logs en tiempo real
|
||||
journalctl -u admin-edu-space -f
|
||||
|
||||
# Reiniciar la aplicación
|
||||
systemctl restart admin-edu-space
|
||||
|
||||
# Detener la aplicación
|
||||
systemctl stop admin-edu-space
|
||||
```
|
||||
|
||||
> **Nota:** Si el archivo `.env` ya existe en el directorio (por ejemplo, copiado manualmente antes de correr el instalador), el script lo conserva sin sobrescribirlo.
|
||||
|
||||
---
|
||||
|
||||
### 🖥️ Opción B — Instalación Manual (Desarrollo Local)
|
||||
|
||||
#### Prerrequisitos
|
||||
1. **Python 3.10 o superior**
|
||||
2. **PostgreSQL** en ejecución con una base de datos creada (ej. `classrooms_db`).
|
||||
3. **Git**
|
||||
|
||||
#### Pasos de Configuración
|
||||
|
||||
1. **Clonar el repositorio:**
|
||||
```bash
|
||||
git clone https://gitea.oemspot.com.ar/carlostellocba/admin-edu-space.git
|
||||
cd admin-edu-space
|
||||
```
|
||||
|
||||
2. **Crear y activar el entorno virtual:**
|
||||
* En Windows (PowerShell):
|
||||
```powershell
|
||||
python -m venv venv
|
||||
.\venv\Scripts\Activate.ps1
|
||||
```
|
||||
* En Linux / macOS:
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
3. **Instalar dependencias:**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
4. **Configurar variables de entorno (`.env`):**
|
||||
Copiar la plantilla y editar con los valores de tu entorno:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
```env
|
||||
SECRET_KEY=clave-secreta-de-produccion-cambiar-en-despliegue
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/classrooms_db
|
||||
FLASK_ENV=development
|
||||
FLASK_DEBUG=1
|
||||
```
|
||||
|
||||
5. **Inicializar la Base de Datos:**
|
||||
Ejecutar el script de inicialización para crear tablas, roles institucionales, permisos RBAC, sedes y el usuario administrador:
|
||||
```bash
|
||||
python init_db.py
|
||||
```
|
||||
|
||||
6. **Compilar catálogos de traducción (opcional si se modifican textos):**
|
||||
```bash
|
||||
pybabel compile -d translations
|
||||
```
|
||||
|
||||
7. **Iniciar el servidor de desarrollo:**
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
La aplicación estará disponible en: **`http://localhost:5000`**
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Credenciales Predeterminadas
|
||||
|
||||
| Usuario | Correo Electrónico | Contraseña | Rol |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Administrador del Sistema** | `admin@edu-space.com` | `admin123` | `Admin` (Acceso Total) |
|
||||
|
||||
> ⚠️ **Importante:** Cambiá la contraseña del administrador inmediatamente después del primer acceso.
|
||||
|
||||
---
|
||||
|
||||
## 📁 Estructura del Proyecto
|
||||
|
||||
```text
|
||||
admin-edu-space/
|
||||
├── app/
|
||||
│ ├── __init__.py # Factoría de la aplicación Flask y extensiones
|
||||
│ ├── forms/ # Formularios Flask-WTF con validación CSRF
|
||||
│ │ ├── auth.py # Login, perfil y cambio de clave
|
||||
│ │ ├── building.py # Sedes y edificios
|
||||
│ │ ├── classroom.py # Aulas y capacidades (con soporte ∞)
|
||||
│ │ └── reservation.py # Reservas de aulas y horarios
|
||||
│ ├── models/ # Modelos relacionales SQLAlchemy
|
||||
│ │ ├── academic_term.py # Ciclos lectivos y períodos académicos (activo/histórico)
|
||||
│ │ ├── building.py # Sedes y edificios institucionales
|
||||
│ │ ├── career.py # Carreras y titulaciones universitarias
|
||||
│ │ ├── classroom.py # Aulas físicas y virtuales con capacidades
|
||||
│ │ ├── genetic_algorithm.py # Estructuras de optimización heurística
|
||||
│ │ ├── milestone.py # Tipificaciones de hitos académicos y exámenes
|
||||
│ │ ├── reservation.py # Reservas horarias, estados y turnos
|
||||
│ │ ├── role.py # Roles y matriz de permisos granulares RBAC
|
||||
│ │ ├── subject.py # Asignaturas, materias y comisiones de cursada
|
||||
│ │ └── user.py # Usuarios, credenciales y asignación de roles
|
||||
│ ├── routes/ # Controladores y rutas modulares (Blueprints)
|
||||
│ │ ├── admin.py # Carreras, Asignaturas, Ciclo Lectivo, Tipificaciones, Métricas, Usuarios y Roles
|
||||
│ │ ├── auth.py # Autenticación, sesiones y perfil
|
||||
│ │ ├── buildings.py # Gestión de sedes y edificios
|
||||
│ │ ├── classrooms.py # Aulas presenciales vs. Campus Virtual a demanda y métricas de uso
|
||||
│ │ ├── genetic_algorithm.py # Optimizador heurístico de asignación de espacios
|
||||
│ │ ├── main.py # Dashboard principal y métricas rápidas
|
||||
│ │ └── schedule.py # Calendario interactivo, cartelera del día, comisiones y reservas
|
||||
│ ├── services/ # Servicios y lógica de negocio
|
||||
│ │ └── sheets_importer.py # Parser y sincronizador bidireccional de Google Sheets
|
||||
│ ├── static/ # Recursos estáticos web
|
||||
│ │ ├── css/
|
||||
│ │ │ ├── style.css # Estilos base y utilidades
|
||||
│ │ │ └── theme.css # Variables CSS para modo claro y modo oscuro
|
||||
│ │ └── js/
|
||||
│ │ ├── genetic-algorithm.js # Lógica de ejecución del algoritmo genético
|
||||
│ │ ├── main.js # Interacciones globales y filtros dinámicos
|
||||
│ │ └── theme-toggle.js # Conmutador reactivo de tema con persistencia
|
||||
│ ├── templates/ # Vistas Jinja2 renderizadas con Bootstrap 5
|
||||
│ │ ├── base.html # Plantilla maestra con navbar categorizada y menú Gestión
|
||||
│ │ ├── dashboard.html # Panel principal de control y métricas
|
||||
│ │ ├── genetic_optimizer.html # Interfaz del optimizador de espacios
|
||||
│ │ ├── admin/
|
||||
│ │ │ ├── academic_terms/ # Gestión de Ciclos Lectivos y cuatrimestres
|
||||
│ │ │ ├── careers/ # Gestión de Carreras y titulaciones
|
||||
│ │ │ ├── metrics/ # Dashboard de Ocupación y Métricas de Espacios
|
||||
│ │ │ ├── milestone_types/ # Gestión de Tipificaciones de Hitos
|
||||
│ │ │ ├── roles/ # Gestión de Roles y Matriz de Permisos RBAC
|
||||
│ │ │ ├── subjects/ # Gestión de Asignaturas y Materias
|
||||
│ │ │ ├── users/ # Gestión de Usuarios y cuentas
|
||||
│ │ │ └── import.html # Sincronización con Google Sheets
|
||||
│ │ ├── auth/ # Vistas de autenticación y perfil de usuario
|
||||
│ │ ├── buildings/ # Listado, alta y edición de sedes y edificios
|
||||
│ │ ├── classrooms/ # Listado físico vs virtual, alta, edición y ficha
|
||||
│ │ └── schedule/ # Calendario (FullCalendar), cartelera del día, comisiones y reservas
|
||||
│ └── utils/ # Utilidades transversales y decoradores
|
||||
│ └── decorators.py # Decoradores RBAC (@permission_required, can_manage, etc.)
|
||||
├── config/ # Configuraciones de entorno (desarrollo, testing, prod)
|
||||
├── translations/ # Catálogos GNU gettext compilados (es / en)
|
||||
├── install.sh # Instalador automático para Debian 12 LXC
|
||||
├── init_db.py # Inicializador DDL y semillero de datos
|
||||
├── CHANGELOG_SESSION.md # Historial técnico cronológico
|
||||
├── requirements.txt # Dependencias Python
|
||||
├── .env.example # Plantilla de variables de entorno
|
||||
└── app.py # Punto de entrada de la aplicación Flask
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licencia
|
||||
|
||||
Este proyecto forma parte del sistema de gestión institucional y educativa de **UniCABA**. Todos los derechos reservados.
|
||||
# Panel de Administración Edu-Space (UniCABA)
|
||||
|
||||
Plataforma integral de gestión áulica, asignación de espacios, control de horarios y reservas académicas para la **Universidad de la Ciudad de Buenos Aires (UniCABA)**, desarrollada con **Python / Flask**, **SQLAlchemy**, **PostgreSQL** y **Bootstrap 5**.
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ Sede Institucional y Ubicaciones
|
||||
* **Sede Central UniCABA:** Tte. Gral. Juan Domingo Perón 802, Ciudad Autónoma de Buenos Aires (CABA) — Aulas de grado, auditorio principal y laboratorios distribuidos desde Planta Baja (Piso 0) hasta el 4.° Piso.
|
||||
* **Campus Virtual:** Espacio áulico digital integrado (Microsoft Teams / Moodle) configurado con **capacidad ilimitada ($\infty$)**.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Características Principales
|
||||
|
||||
### 🛡️ 1. Control de Accesos RBAC Granular (Matriz estilo FortiGate)
|
||||
* **Gestión por Módulos:** Permisos independientes por cada área del sistema:
|
||||
1. **Aulas y Espacios:** Creación, edición, capacidad y estado de aulas físicas y virtuales.
|
||||
2. **Edificios y Sedes:** Administración de sedes, pisos y arquitectura edilicia.
|
||||
3. **Reservas de Aulas:** Solicitud, aprobación, confirmación y cancelación.
|
||||
4. **Cartelera y Cronograma:** Calendario interactivo, visualización diaria y comisiones.
|
||||
5. **Gestión Académica:** Carreras, asignaturas, códigos y comisiones.
|
||||
6. **Sincronización Sheets:** Importador y sincronizador de Google Sheets.
|
||||
7. **Optimizador IA:** Algoritmo genético de distribución y resolución de conflictos.
|
||||
8. **Usuarios y Accesos RBAC:** Cuentas, roles y matriz de permisos.
|
||||
* **Niveles de Permiso Granulares:** `Ninguno`, `Solo Lectura` o `Lectura y Escritura`.
|
||||
* **Controles Rápidos:** Botón *"Establecer Todos"* para configuración masiva inmediata.
|
||||
* **Roles Predefinidos:** `Admin` (control total), `Docente`, `Operador` y `Consulta`.
|
||||
|
||||
### 📊 2. Sincronización e Importación desde Google Sheets
|
||||
* Integración directa con la cartelera académica de UniCABA en Google Sheets.
|
||||
* Extracción e importación normalizada de los 5 campos clave:
|
||||
* **Código Académico:** Códigos reales de asignatura (ej. `INGA0003`, `ASIG00165`).
|
||||
* **Asignatura:** Nombre oficial de la materia.
|
||||
* **Aula:** Mapeo a aulas físicas (PB a 4.° piso) o aula `VIRTUAL` ($\infty$).
|
||||
* **Carrera:** Normalización y vinculación relacional con entidades de carrera.
|
||||
* **Horarios y Turnos:** Detección automática de franjas horarias y delimitadores de bloque (`Turno Mañana` y `Turno Tarde / Vespertino`).
|
||||
|
||||
### 📅 3. Calendario Interactivo de Reservas (FullCalendar)
|
||||
* **Visualización Semanal, Mensual y Diaria:** Grilla ágil de alta legibilidad con jerarquía visual:
|
||||
* Asignatura destacada en tipografía grande y negrita.
|
||||
* Badges con código de materia, aula asignada, turno y franja horaria.
|
||||
* **Filtros Dinámicos Combinables:**
|
||||
* Filtrar por **Aula**.
|
||||
* Filtrar por **Materia / Asignatura**.
|
||||
* Filtrar por **Carrera**.
|
||||
* Filtrar por **Turno** (`Turno Mañana` / `Turno Tarde`).
|
||||
* Filtrar por **Piso / Planta** (`Planta Baja`, `Piso 1`, `Piso 2`, `Piso 3`, `Piso 4`).
|
||||
* **Switch "Ocultar Aulas Virtuales":** Aísla la ocupación física real en el edificio central excluyendo los eventos virtuales de capacidad infinita.
|
||||
* **Ficha Modal Instantánea:** Vista previa detallada al hacer clic en cualquier evento.
|
||||
|
||||
### 📋 4. Cartelera del Día y Listado de Reservas
|
||||
* **Cartelera del Día (`/schedule/today`):**
|
||||
* **Vista Cronológica (Timeline):** Bloques horarios de 07:00 a 22:00 hs con indicador de ocupación.
|
||||
* **Vista en Tabla:** Lista ordenada con acciones rápidas y detalles de comisión.
|
||||
* Filtros directos por piso, turno y switch para ocultar virtuales.
|
||||
* **Listado Paginado de Reservas (`/schedule/list`):**
|
||||
* Paginación de 20 registros por página preservando filtros aplicados.
|
||||
* Búsqueda por texto, estado (`Confirmada`, `Pendiente`, `Cancelada`), rango de fechas y piso.
|
||||
|
||||
### 🏫 5. Gestión de Aulas con Capacidad Infinita ($\infty$)
|
||||
* Soporte nativo para el símbolo **`∞`** o `0` para designar aulas virtuales o espacios sin aforo restringido.
|
||||
* Botón de un solo clic `Ilimitada (∞)` en formularios de alta y edición con leyendas explicativas.
|
||||
* Badges distintivos `∞ Ilimitada` en listados, tarjetas y fichas técnicas.
|
||||
|
||||
### 🤖 6. Optimizador Inteligente de Aulas (Algoritmo Genético)
|
||||
* Motor de optimización heurística para asignación automática de aulas a comisiones.
|
||||
* Pondera capacidad requerida vs. capacidad de aula, equipamiento tecnológico y minimización de desplazamientos entre pisos.
|
||||
|
||||
### 🎨 7. Experiencia de Usuario, Temas y Localización
|
||||
* **Tema Oscuro / Claro:** Alternador con botón flotante y persistencia en `localStorage` y perfil de usuario.
|
||||
* **Soporte Multilingüe:** Internacionalización integral (Español e Inglés) compilada con GNU gettext (`pybabel`).
|
||||
* **Tarjetas Estadísticas Interactivas:** Accesos directos rápidos desde el panel general hacia las distintas secciones operativas.
|
||||
|
||||
### 🏛️ 8. Sistema Integral de Gestión Institucional (8 Módulos)
|
||||
El menú desplegable **Gestión** en la barra de navegación organiza las áreas operativas de la universidad:
|
||||
* **Espacios e Infraestructura:**
|
||||
* **Edificios y Sedes (`/buildings/list`):** Administración de edificios físicos, arquitectura y estado edilicio.
|
||||
* **Aulas Presenciales (`/classrooms/list?view_mode=physical`):** Catálogo de aulas físicas por piso, capacidades y zócalos compactos con métricas de ocupación en tiempo real (día, semana, mes).
|
||||
* **Aulas Virtuales con Reunión (`/classrooms/list?view_mode=virtual`):** Campus Virtual con salas a demanda agrupadas por comisión, capacidad ilimitada ($\infty$) y enlaces directos de acceso a videollamadas (Zoom, Google Meet, Microsoft Teams).
|
||||
* **Gestión de Ocupación y Métricas (`/admin/occupancy-metrics`):** Dashboard analítico de tasas de ocupación horaria (día, semana, mes), horas pico, distribución por turno y comparativa física vs. virtual.
|
||||
* **Gestión Académica:**
|
||||
* **Carreras (`/admin/careers`):** ABM de carreras universitarias y planes de estudio con contador de asignaturas y comisiones activas.
|
||||
* **Asignaturas (`/admin/subjects`):** Catálogo institucional de materias con créditos, departamentos y filtros por carrera y estado.
|
||||
* **Comisiones y Cursadas (`/schedule/commissions`):** Gestión de cursadas, turnos, docentes, enlaces virtuales e hitos de evaluación.
|
||||
* **Ciclo Lectivo (`/admin/academic-terms`):** Modelo y panel de administración de períodos académicos, cuatrimestres y activación del ciclo lectivo en curso.
|
||||
* **Administración & Seguridad:**
|
||||
* **Usuarios y Roles RBAC:** Cuentas, asignación de permisos matriciales y auditoría.
|
||||
* **Tipificaciones de Hitos:** Catálogo de tipos de hitos de evaluación académica.
|
||||
* **Sincronización Sheets:** Integración con Google Sheets para actualización en lote.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Stack Tecnológico
|
||||
|
||||
| Componente | Tecnología |
|
||||
| :--- | :--- |
|
||||
| **Backend** | Python 3.12+ / Flask |
|
||||
| **Base de Datos** | PostgreSQL 16+ / SQLAlchemy ORM |
|
||||
| **Seguridad & Sesiones** | Flask-Login / Werkzeug / CSRF Protect |
|
||||
| **Formularios** | Flask-WTF / WTForms |
|
||||
| **Internacionalización** | Flask-Babel / GNU gettext |
|
||||
| **Frontend** | Bootstrap 5.3, Bootstrap Icons, Vanilla JS, CSS Variables |
|
||||
| **Calendario** | FullCalendar 5.11 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Despliegue en Proxmox VE (LXC) con Entorno Virtual `venv`
|
||||
|
||||
El sistema está especialmente optimizado para ejecutarse en contenedores **LXC (Debian 12 Bookworm o Ubuntu 22.04/24.04 LTS)** dentro de **Proxmox VE**, utilizando un entorno virtual nativo de Python (`venv`), el motor **PostgreSQL** instalado en el propio contenedor y el servidor WSGI **Gunicorn** gestionado como servicio por **systemd**.
|
||||
|
||||
---
|
||||
|
||||
### 📋 Paso 1: Creación del Contenedor LXC en Proxmox VE
|
||||
|
||||
Podés crear el contenedor desde la interfaz Web de Proxmox (*Create CT*) o por terminal Proxmox con `pct create`:
|
||||
|
||||
* **Plantilla (Template):** `debian-12-standard` (*Recomendado*) o `ubuntu-22.04-standard` / `ubuntu-24.04-standard`.
|
||||
* **CPU:** 1 a 2 vCPU.
|
||||
* **Memoria RAM:** 1024 MB a 2048 MB (Swap: 512 MB).
|
||||
* **Disco:** 8 GB a 15 GB (almacenamiento `local-lvm` o `zfs`).
|
||||
* **Red (Network):** Modo Bridge `vmbr0` con IP estática en tu red local o DHCP (ej: `192.168.1.150/24`, Gateway: `192.168.1.1`).
|
||||
* **Opciones del Contenedor:**
|
||||
* *Unprivileged container:* Sí (marcado por defecto, compatible).
|
||||
* *Features (Características):* Habilitar **Nesting (`nesting=1`)** en `CT > Opciones > Características` (requerido para systemd).
|
||||
|
||||
---
|
||||
|
||||
### ⚡ Paso 2: Instalación Automatizada con `install.sh`
|
||||
|
||||
1. **Ingresar a la consola del contenedor LXC:**
|
||||
Desde la Web UI de Proxmox (consola noVNC) o desde el shell de tu nodo Proxmox:
|
||||
```bash
|
||||
pct enter <ID_DEL_CONTENEDOR>
|
||||
```
|
||||
|
||||
2. **Clonar el repositorio y lanzar el instalador:**
|
||||
```bash
|
||||
apt update && apt install -y git
|
||||
git clone https://gitea.oemspot.com.ar/carlostellocba/admin-edu-space.git
|
||||
cd admin-edu-space
|
||||
bash install.sh
|
||||
```
|
||||
|
||||
3. **Flujo de Ejecución Automatizado en el LXC:**
|
||||
* **Migración segura fuera de `/root`:** Si el repositorio se clonó en `/root`, el instalador lo traslada automáticamente a `/opt/admin-edu-space` para garantizar que el usuario del servicio (`eduspace`) tenga acceso de lectura y ejecución.
|
||||
* **Locales UTF-8:** Configura `en_US.UTF-8` y `es_AR.UTF-8` para asegurar la internacionalización con `Flask-Babel` y prevenir errores de encoding.
|
||||
* **Motor PostgreSQL:** Instala el motor PostgreSQL, inicia el servicio y verifica conectividad activa con bucle de espera.
|
||||
* **Restauración del Snapshot Oficial ([classrooms_db.sql](file:///c:/Workspace/admin-edu-space/classrooms_db.sql)):**
|
||||
* Detecta el volcado completo de datos.
|
||||
* Crea la base de datos `classrooms_db` y el usuario `eduspace_user`.
|
||||
* Restaura **25 aulas** (físicas y Campus Virtual $\infty$), **206 comisiones**, **206 materias**, **412 reservas históricas**, **24 carreras**, matrices RBAC y cuentas de acceso.
|
||||
* Otorga privilegios totales (`GRANT ALL`) en el esquema `public`, todas las tablas y **todas las secuencias** al usuario `eduspace_user`.
|
||||
* Ejecuta `init_db.py` de forma idempotente para validar roles y coherencia.
|
||||
* **Entorno Virtual Python (`venv`):** Crea el entorno aislado en `/opt/admin-edu-space/venv` e instala todas las dependencias oficiales (`Flask`, `SQLAlchemy`, `Gunicorn`, etc.).
|
||||
* **Servicio Systemd de Producción:** Configura el servicio `admin-edu-space.service` gestionado por **Gunicorn** (3 workers, puerto 5000), con reinicio automático ante fallos y habilitado al inicio del contenedor.
|
||||
|
||||
---
|
||||
|
||||
### 🌐 Paso 3: Acceso Inmediato a la Aplicación
|
||||
|
||||
Al finalizar la instalación, el script mostrará la dirección IP del contenedor:
|
||||
* **URL:** **`http://<IP-DEL-LXC>:5000`** (ejemplo: `http://192.168.1.150:5000`)
|
||||
* **Email Administrador:** `admin@edu-space.com`
|
||||
* **Contraseña Administrador:** `admin123`
|
||||
|
||||
---
|
||||
|
||||
### 🛠️ Comandos de Gestión del Servicio en el LXC
|
||||
|
||||
```bash
|
||||
# Consultar estado en tiempo real
|
||||
systemctl status admin-edu-space
|
||||
|
||||
# Seguir logs de la aplicación
|
||||
journalctl -u admin-edu-space -f
|
||||
|
||||
# Reiniciar la aplicación
|
||||
systemctl restart admin-edu-space
|
||||
|
||||
# Detener el servicio
|
||||
systemctl stop admin-edu-space
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 💾 Generación de Backups y Migración a Otros Contenedores LXC
|
||||
|
||||
Para respaldar la base de datos en ejecución o clonarla a otro contenedor LXC:
|
||||
|
||||
```bash
|
||||
# Dentro del contenedor LXC:
|
||||
cd /opt/admin-edu-space
|
||||
bash backup_db.sh
|
||||
```
|
||||
|
||||
* El script genera un archivo fechado en `backups/classrooms_db_YYYYMMDD_HHMMSS.sql`.
|
||||
* Actualiza automáticamente el archivo principal [classrooms_db.sql](file:///c:/Workspace/admin-edu-space/classrooms_db.sql).
|
||||
* **Para migrar a otro contenedor:** Podés transferir `classrooms_db.sql` mediante SCP o Git y ejecutar `bash install.sh` en el nuevo LXC.
|
||||
|
||||
---
|
||||
|
||||
### 💻 Instalación Manual para Desarrollo Local (con `venv`)
|
||||
|
||||
Si deseás correr el proyecto en una máquina de desarrollo (Windows / Linux / macOS) sin usar el instalador de Proxmox:
|
||||
|
||||
1. **Crear y activar el entorno virtual:**
|
||||
* En Windows (PowerShell):
|
||||
```powershell
|
||||
python -m venv venv
|
||||
.\venv\Scripts\Activate.ps1
|
||||
```
|
||||
* En Linux / macOS:
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
2. **Instalar dependencias:**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. **Configurar base de datos local y restaurar snapshot:**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
|
||||
# Crear base de datos en PostgreSQL local y cargar datos
|
||||
sudo -u postgres psql -c "CREATE DATABASE classrooms_db;"
|
||||
sudo -u postgres psql -d classrooms_db < classrooms_db.sql
|
||||
```
|
||||
|
||||
4. **Compilar catálogos de traducción:**
|
||||
```bash
|
||||
pybabel compile -d translations
|
||||
```
|
||||
|
||||
5. **Iniciar el servidor:**
|
||||
```bash
|
||||
python wsgi.py
|
||||
```
|
||||
Disponible en: **`http://localhost:5000`**
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Credenciales Predeterminadas
|
||||
|
||||
| Usuario | Correo Electrónico | Contraseña | Rol |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Administrador del Sistema** | `admin@edu-space.com` | `admin123` | `Admin` (Acceso Total) |
|
||||
|
||||
> ⚠️ **Seguridad:** Cambiá la contraseña del administrador desde la interfaz web inmediatamente después del primer inicio de sesión.
|
||||
|
||||
---
|
||||
|
||||
## 📁 Estructura del Proyecto
|
||||
|
||||
```text
|
||||
admin-edu-space/
|
||||
├── app/
|
||||
│ ├── __init__.py # Factoría de la aplicación Flask y extensiones
|
||||
│ ├── forms/ # Formularios Flask-WTF con validación CSRF
|
||||
│ │ ├── auth.py # Login, perfil y cambio de clave
|
||||
│ │ ├── building.py # Sedes y edificios
|
||||
│ │ ├── classroom.py # Aulas y capacidades (con soporte ∞)
|
||||
│ │ └── reservation.py # Reservas de aulas y horarios
|
||||
│ ├── models/ # Modelos relacionales SQLAlchemy
|
||||
│ │ ├── academic_term.py # Ciclos lectivos y períodos académicos (activo/histórico)
|
||||
│ │ ├── building.py # Sedes y edificios institucionales
|
||||
│ │ ├── career.py # Carreras y titulaciones universitarias
|
||||
│ │ ├── classroom.py # Aulas físicas y virtuales con capacidades
|
||||
│ │ ├── genetic_algorithm.py # Estructuras de optimización heurística
|
||||
│ │ ├── milestone.py # Tipificaciones de hitos académicos y exámenes
|
||||
│ │ ├── reservation.py # Reservas horarias, estados y turnos
|
||||
│ │ ├── role.py # Roles y matriz de permisos granulares RBAC
|
||||
│ │ ├── subject.py # Asignaturas, materias y comisiones de cursada
|
||||
│ │ └── user.py # Usuarios, credenciales y asignación de roles
|
||||
│ ├── routes/ # Controladores y rutas modulares (Blueprints)
|
||||
│ │ ├── admin.py # Carreras, Asignaturas, Ciclo Lectivo, Tipificaciones, Métricas, Usuarios y Roles
|
||||
│ │ ├── auth.py # Autenticación, sesiones y perfil
|
||||
│ │ ├── buildings.py # Gestión de sedes y edificios
|
||||
│ │ ├── classrooms.py # Aulas presenciales vs. Campus Virtual a demanda y métricas de uso
|
||||
│ │ ├── genetic_algorithm.py # Optimizador heurístico de asignación de espacios
|
||||
│ │ ├── main.py # Dashboard principal y métricas rápidas
|
||||
│ │ └── schedule.py # Calendario interactivo, cartelera del día, comisiones y reservas
|
||||
│ ├── services/ # Servicios y lógica de negocio
|
||||
│ │ └── sheets_importer.py # Parser y sincronizador bidireccional de Google Sheets
|
||||
│ ├── static/ # Recursos estáticos web (CSS, JS, imágenes institucionales)
|
||||
│ └── templates/ # Vistas Jinja2 renderizadas con Bootstrap 5
|
||||
├── config/ # Configuraciones de entorno (desarrollo, testing, prod)
|
||||
├── translations/ # Catálogos GNU gettext compilados (es / en)
|
||||
├── classrooms_db.sql # Snapshot completo de base de datos (PostgreSQL 14+)
|
||||
├── install.sh # Instalador automatizado para Proxmox VE LXC (venv + PostgreSQL)
|
||||
├── backup_db.sh # Script para generar nuevos backups y actualizar snapshot
|
||||
├── init_db.py # Inicializador DDL y sincronizador de esquema/roles
|
||||
├── CHANGELOG_SESSION.md # Historial técnico cronológico
|
||||
├── requirements.txt # Dependencias Python (incluye Gunicorn)
|
||||
├── .env.example # Plantilla de variables de entorno
|
||||
└── wsgi.py # Punto de entrada de la aplicación Flask
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licencia
|
||||
|
||||
Este proyecto forma parte del sistema de gestión institucional y educativa de **UniCABA**. Todos los derechos reservados.
|
||||
␍
|
||||
@@ -29,6 +29,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
.metric-bar-fill {
|
||||
width: 0;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
transition: width 0.6s ease;
|
||||
@@ -76,7 +77,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-bar-container mt-3">
|
||||
<div class="metric-bar-fill bg-primary" style="width: {{ avg_day_pct }}%;"></div>
|
||||
<div class="metric-bar-fill bg-primary" data-width="{{ avg_day_pct }}%"></div>
|
||||
</div>
|
||||
<small class="text-muted mt-2 d-block" style="font-size: 0.72rem;">
|
||||
{% trans %}Promedio sobre 15 hs operativas ({{ physical_rooms_count }} aulas presenciales){% endtrans %}
|
||||
@@ -97,7 +98,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-bar-container mt-3">
|
||||
<div class="metric-bar-fill bg-info" style="width: {{ avg_week_pct }}%;"></div>
|
||||
<div class="metric-bar-fill bg-info" data-width="{{ avg_week_pct }}%"></div>
|
||||
</div>
|
||||
<small class="text-muted mt-2 d-block" style="font-size: 0.72rem;">
|
||||
{% trans %}Semana en curso (Lunes a Sábado){% endtrans %}
|
||||
@@ -118,7 +119,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-bar-container mt-3">
|
||||
<div class="metric-bar-fill bg-success" style="width: {{ avg_month_pct }}%;"></div>
|
||||
<div class="metric-bar-fill bg-success" data-width="{{ avg_month_pct }}%"></div>
|
||||
</div>
|
||||
<small class="text-muted mt-2 d-block" style="font-size: 0.72rem;">
|
||||
{% trans %}Total mes calendario activo{% endtrans %}
|
||||
@@ -174,7 +175,7 @@
|
||||
</div>
|
||||
<div class="metric-bar-container">
|
||||
<div class="metric-bar-fill {% if fs.avg_pct > 50 %}bg-danger{% elif fs.avg_pct > 25 %}bg-warning{% else %}bg-success{% endif %}"
|
||||
style="width: {{ fs.avg_pct }}%;"></div>
|
||||
data-width="{{ fs.avg_pct }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -330,3 +331,13 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.metric-bar-fill[data-width]').forEach(function(bar) {
|
||||
bar.style.width = bar.getAttribute('data-width');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# backup_db.sh — Admin Edu-Space (UniCABA)
|
||||
# Generador de backups de PostgreSQL y actualización de classrooms_db.sql
|
||||
# Uso: bash backup_db.sh
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
|
||||
ok() { echo -e "${GREEN} ✔ $*${RESET}"; }
|
||||
info() { echo -e "${CYAN} ▸ $*${RESET}"; }
|
||||
warn() { echo -e "${YELLOW} ⚠ $*${RESET}"; }
|
||||
err() { echo -e "${RED} ✘ $*${RESET}" >&2; exit 1; }
|
||||
hdr() { echo -e "\n${BOLD}${CYAN}══ $* ══${RESET}\n"; }
|
||||
|
||||
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BACKUP_DIR="${APP_DIR}/backups"
|
||||
TIMESTAMP="$(date +'%Y%m%d_%H%M%S')"
|
||||
BACKUP_FILE="${BACKUP_DIR}/classrooms_db_${TIMESTAMP}.sql"
|
||||
ROOT_BACKUP="${APP_DIR}/classrooms_db.sql"
|
||||
APP_ENV="${APP_DIR}/.env"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
hdr "Iniciando Backup de la Base de Datos"
|
||||
|
||||
# 1. Obtener parámetros de conexión
|
||||
DB_NAME="classrooms_db"
|
||||
DB_USER="eduspace_user"
|
||||
DB_HOST="localhost"
|
||||
DB_PORT="5432"
|
||||
DB_PASSWORD=""
|
||||
|
||||
if [[ -f "$APP_ENV" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source <(grep -v '^#' "$APP_ENV" | grep '=')
|
||||
set +a
|
||||
if [[ -n "${DATABASE_URL:-}" ]]; then
|
||||
# Extraer credenciales desde DATABASE_URL si existe
|
||||
# Formato: postgresql://usuario:pass@host:port/dbname
|
||||
DB_USER=$(echo "$DATABASE_URL" | sed -E 's|.*://([^:]+):?([^@]*)@.*|\1|')
|
||||
DB_PASSWORD=$(echo "$DATABASE_URL" | sed -E 's|.*://[^:]+:([^@]*)@.*|\1|')
|
||||
DB_HOST=$(echo "$DATABASE_URL" | sed -E 's|.*@([^:/]+):?.*|\1|')
|
||||
DB_PORT=$(echo "$DATABASE_URL" | sed -E 's|.*:([0-9]+)/.*|\1|')
|
||||
DB_NAME=$(echo "$DATABASE_URL" | sed -E 's|.*/([^?]+).*|\1|')
|
||||
fi
|
||||
fi
|
||||
|
||||
DB_USER="${DB_USER:-postgres}"
|
||||
DB_NAME="${DB_NAME:-classrooms_db}"
|
||||
DB_HOST="${DB_HOST:-localhost}"
|
||||
DB_PORT="${DB_PORT:-5432}"
|
||||
|
||||
info "Base de datos: ${DB_NAME}"
|
||||
info "Usuario: ${DB_USER}"
|
||||
info "Host: ${DB_HOST}:${DB_PORT}"
|
||||
|
||||
# 2. Ejecutar pg_dump
|
||||
info "Generando volcado SQL..."
|
||||
|
||||
export PGPASSWORD="${DB_PASSWORD:-}"
|
||||
|
||||
if command -v pg_dump >/dev/null 2>&1; then
|
||||
# Intentar como usuario configurado
|
||||
if pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \
|
||||
--no-owner --no-privileges --clean --if-exists > "$BACKUP_FILE" 2>/dev/null; then
|
||||
ok "Volcado generado con éxito usando pg_dump"
|
||||
elif [[ $EUID -eq 0 ]] || sudo -n true 2>/dev/null; then
|
||||
info "Reintentando volcado como superusuario postgres..."
|
||||
sudo -u postgres pg_dump -d "$DB_NAME" --no-owner --no-privileges > "$BACKUP_FILE"
|
||||
ok "Volcado generado como usuario postgres"
|
||||
else
|
||||
err "No se pudo realizar el backup. Verifica tus permisos y credenciales."
|
||||
fi
|
||||
else
|
||||
err "No se encontró la herramienta 'pg_dump'. Instala postgresql-client."
|
||||
fi
|
||||
|
||||
# 3. Limpiar comandos de versiones específicas (ej: \restrict de PG 18) para máxima compatibilidad
|
||||
sed -i '/^\\restrict/d' "$BACKUP_FILE" 2>/dev/null || true
|
||||
|
||||
# 4. Actualizar copia en raíz para despliegues automáticos
|
||||
cp "$BACKUP_FILE" "$ROOT_BACKUP"
|
||||
|
||||
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | awk '{print $1}')
|
||||
|
||||
hdr "Resumen del Backup"
|
||||
ok "Archivo histórico: ${BACKUP_FILE} (${BACKUP_SIZE})"
|
||||
ok "Copia para deploy: ${ROOT_BACKUP} (${BACKUP_SIZE})"
|
||||
info "Para restaurar en otro servidor:"
|
||||
echo -e " ${CYAN}sudo -u postgres psql -d ${DB_NAME} < ${ROOT_BACKUP}${RESET}"
|
||||
echo ""
|
||||
+2044
File diff suppressed because it is too large
Load Diff
+144
-114
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# install.sh — Admin Edu-Space (UniCABA)
|
||||
# Instalador automatizado para Debian 12 (LXC / Bare Metal)
|
||||
# Instalador automatizado para Proxmox VE (LXC) / Debian 12 / Ubuntu (entorno venv)
|
||||
# Uso: sudo bash install.sh
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ─── Colores ───────────────────────────────────────────────────────────────────
|
||||
# ─── Colores y Helpers ─────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
|
||||
@@ -17,8 +17,7 @@ warn() { echo -e "${YELLOW} ⚠ $*${RESET}"; }
|
||||
err() { echo -e "${RED} ✘ $*${RESET}" >&2; exit 1; }
|
||||
hdr() { echo -e "\n${BOLD}${CYAN}══ $* ══${RESET}\n"; }
|
||||
|
||||
# ─── Variables ─────────────────────────────────────────────────────────────────
|
||||
# APP_DIR resuelto desde la ubicación del script (no del CWD)
|
||||
# ─── Variables Principales ─────────────────────────────────────────────────────
|
||||
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VENV_DIR="${APP_DIR}/venv"
|
||||
APP_ENV="${APP_DIR}/.env"
|
||||
@@ -27,9 +26,10 @@ APP_USER="eduspace"
|
||||
PYTHON_REQUIRED_MAJOR=3
|
||||
PYTHON_REQUIRED_MINOR=10
|
||||
LOG_DIR="/var/log/${SERVICE_NAME}"
|
||||
SQL_BACKUP_FILE="${APP_DIR}/classrooms_db.sql"
|
||||
|
||||
# ─── Root check ────────────────────────────────────────────────────────────────
|
||||
[[ $EUID -ne 0 ]] && err "Ejecutá este script con sudo: sudo bash install.sh"
|
||||
# ─── Verificación de Permisos Root ─────────────────────────────────────────────
|
||||
[[ $EUID -ne 0 ]] && err "Ejecutá este script con permisos de root: sudo bash install.sh"
|
||||
|
||||
# Si el directorio está dentro de /root, lo movemos a /opt para evitar problemas de permisos
|
||||
if [[ "${APP_DIR}" == /root/* ]]; then
|
||||
@@ -41,38 +41,45 @@ if [[ "${APP_DIR}" == /root/* ]]; then
|
||||
exec bash install.sh
|
||||
fi
|
||||
|
||||
[[ ! -f "${APP_DIR}/wsgi.py" ]] && err "No se encontró wsgi.py en ${APP_DIR}. ¿Estás en el directorio correcto?"
|
||||
[[ ! -f "${APP_DIR}/wsgi.py" ]] && err "No se encontró wsgi.py en ${APP_DIR}. ¿Estás en el directorio raíz del proyecto?"
|
||||
|
||||
echo -e "\n${BOLD}${CYAN}"
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Admin Edu-Space (UniCABA) — Instalador Debian 12 ║"
|
||||
echo "║ Admin Edu-Space (UniCABA) — Proxmox LXC Installer ║"
|
||||
echo "║ Despliegue Nativo con venv + Motor PostgreSQL ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
echo -e "${RESET}"
|
||||
|
||||
OS_NAME=$(grep -oP '(?<=^PRETTY_NAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || uname -s)
|
||||
info "Sistema Operativo: ${OS_NAME}"
|
||||
info "Directorio del proyecto: ${APP_DIR}"
|
||||
info "Servicio systemd: ${SERVICE_NAME}"
|
||||
info "Usuario de servicio: ${APP_USER}"
|
||||
|
||||
# ─── 1. Actualizar sistema e instalar base ──────────────────────────────────────
|
||||
hdr "1. Actualizando sistema"
|
||||
# ─── 1. Actualizar sistema e instalar dependencias base ────────────────────────
|
||||
hdr "1. Actualizando repositorios y paquetes base"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq \
|
||||
curl wget git ca-certificates gnupg lsb-release \
|
||||
build-essential libpq-dev \
|
||||
locales
|
||||
locales \
|
||||
> /dev/null 2>&1
|
||||
ok "Paquetes base instalados"
|
||||
|
||||
# ─── 2. Configurar locale UTF-8 ────────────────────────────────────────────────
|
||||
hdr "2. Configurando locale"
|
||||
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
|
||||
sed -i 's/# es_AR.UTF-8 UTF-8/es_AR.UTF-8 UTF-8/' /etc/locale.gen
|
||||
locale-gen en_US.UTF-8 es_AR.UTF-8 > /dev/null 2>&1
|
||||
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 > /dev/null 2>&1
|
||||
# ─── 2. Configurar Locale UTF-8 ────────────────────────────────────────────────
|
||||
hdr "2. Configurando soporte de codificación (Locales UTF-8)"
|
||||
if [ -f /etc/locale.gen ]; then
|
||||
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
|
||||
sed -i 's/# es_AR.UTF-8 UTF-8/es_AR.UTF-8 UTF-8/' /etc/locale.gen
|
||||
locale-gen en_US.UTF-8 es_AR.UTF-8 > /dev/null 2>&1 || true
|
||||
fi
|
||||
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 > /dev/null 2>&1 || true
|
||||
export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 PYTHONUTF8=1 PYTHONIOENCODING=utf-8
|
||||
ok "Locale configurado: en_US.UTF-8 / es_AR.UTF-8"
|
||||
ok "Locales configurados: en_US.UTF-8 / es_AR.UTF-8"
|
||||
|
||||
# ─── 3. Instalar Python 3.10+ ───────────────────────────────────────────────────
|
||||
hdr "3. Instalando Python"
|
||||
# ─── 3. Instalar Python 3.10+ y entorno de desarrollo ──────────────────────────
|
||||
hdr "3. Verificando entorno Python"
|
||||
apt-get install -y -qq python3 python3-venv python3-pip python3-dev > /dev/null 2>&1
|
||||
|
||||
PY_VERSION=$(python3 --version 2>/dev/null | awk '{print $2}' || echo "0.0")
|
||||
@@ -89,43 +96,64 @@ else
|
||||
ok "Python $PY_VERSION instalado"
|
||||
fi
|
||||
|
||||
# ─── 4. Instalar PostgreSQL ────────────────────────────────────────────────────
|
||||
hdr "4. Instalando PostgreSQL"
|
||||
apt-get install -y -qq postgresql postgresql-client > /dev/null 2>&1
|
||||
ok "PostgreSQL instalado: $(psql --version | awk '{print $3}')"
|
||||
# ─── 4. Instalar y Levantar Motor PostgreSQL ───────────────────────────────────
|
||||
hdr "4. Instalando y configurando motor de base de datos PostgreSQL"
|
||||
apt-get install -y -qq postgresql postgresql-contrib postgresql-client > /dev/null 2>&1
|
||||
PG_VER=$(psql --version 2>/dev/null | awk '{print $3}' || echo "instalado")
|
||||
ok "Motor PostgreSQL instalado: ${PG_VER}"
|
||||
|
||||
# Iniciar PostgreSQL (LXC puede no tener systemd activo aún)
|
||||
pg_ctlcluster 15 main start 2>/dev/null \
|
||||
# Levantar servicio de PostgreSQL asegurando compatibilidad con contenedores LXC y VMs
|
||||
info "Iniciando servicio de PostgreSQL..."
|
||||
systemctl start postgresql 2>/dev/null \
|
||||
|| service postgresql start 2>/dev/null \
|
||||
|| systemctl start postgresql 2>/dev/null \
|
||||
|| su - postgres -c "pg_ctlcluster $(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -n1) main start" 2>/dev/null \
|
||||
|| true
|
||||
sleep 2
|
||||
systemctl enable postgresql > /dev/null 2>&1 || true
|
||||
ok "PostgreSQL activo y habilitado al inicio"
|
||||
|
||||
# ─── 5. Configurar .env ────────────────────────────────────────────────────────
|
||||
hdr "5. Configurando variables de entorno"
|
||||
# Esperar a que el socket de PostgreSQL esté completamente activo
|
||||
PG_READY=false
|
||||
for i in {1..15}; do
|
||||
if sudo -u postgres psql -c '\q' >/dev/null 2>&1; then
|
||||
PG_READY=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$PG_READY" = true ]; then
|
||||
systemctl enable postgresql > /dev/null 2>&1 || true
|
||||
ok "PostgreSQL activo y aceptando conexiones"
|
||||
else
|
||||
err "No se pudo conectar al motor PostgreSQL. Verificá los registros del sistema."
|
||||
fi
|
||||
|
||||
# ─── 5. Configurar Variables de Entorno (.env) ─────────────────────────────────
|
||||
hdr "5. Configurando variables de entorno (.env)"
|
||||
|
||||
DB_NAME="classrooms_db"
|
||||
DB_USER="eduspace_user"
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}Configuración de la base de datos PostgreSQL${RESET}"
|
||||
read -rp " Contraseña para el usuario de BD [default: eduspace2024]: " DB_PASS_INPUT
|
||||
DB_PASSWORD="${DB_PASS_INPUT:-eduspace2024}"
|
||||
DEFAULT_DB_PASS="eduspace2024"
|
||||
|
||||
if [[ -f "$APP_ENV" ]]; then
|
||||
ok ".env ya existe — se conserva sin modificar"
|
||||
# Leer variables del .env existente para usarlas en el script
|
||||
ok ".env ya existe — se conserva la configuración existente"
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source <(grep -v '^#' "$APP_ENV" | grep '=')
|
||||
set +a
|
||||
DB_USER="${DB_USERNAME:-$DB_USER}"
|
||||
DB_PASSWORD="${DB_PASSWORD:-eduspace2024}"
|
||||
DB_NAME=$(echo "${DATABASE_URL:-}" | sed 's|.*\/||')
|
||||
DB_PASSWORD="${DB_PASSWORD:-$DEFAULT_DB_PASS}"
|
||||
DB_NAME=$(echo "${DATABASE_URL:-}" | sed -E 's|.*/([^?]+).*|\1|')
|
||||
DB_NAME="${DB_NAME:-classrooms_db}"
|
||||
else
|
||||
echo ""
|
||||
echo -e " ${BOLD}Credenciales para la base de datos PostgreSQL:${RESET}"
|
||||
if [ -n "${NON_INTERACTIVE:-}" ] || [ ! -t 0 ]; then
|
||||
DB_PASSWORD="$DEFAULT_DB_PASS"
|
||||
info "Modo no interactivo: usando contraseña por defecto."
|
||||
else
|
||||
read -rp " Contraseña para el usuario '${DB_USER}' [default: ${DEFAULT_DB_PASS}]: " DB_PASS_INPUT
|
||||
DB_PASSWORD="${DB_PASS_INPUT:-$DEFAULT_DB_PASS}"
|
||||
fi
|
||||
|
||||
SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex(32))')
|
||||
cat > "$APP_ENV" <<ENVEOF
|
||||
# Admin Edu-Space — Configuración de producción
|
||||
@@ -146,89 +174,89 @@ BABEL_DEFAULT_LOCALE=es
|
||||
BABEL_DEFAULT_TIMEZONE=America/Argentina/Buenos_Aires
|
||||
ENVEOF
|
||||
chmod 640 "$APP_ENV"
|
||||
ok ".env creado con SECRET_KEY aleatoria"
|
||||
warn "Revisá ${APP_ENV} antes de exponer la app a producción"
|
||||
ok ".env generado exitosamente con SECRET_KEY criptográfica"
|
||||
fi
|
||||
|
||||
# ─── 6. Crear usuario y base de datos PostgreSQL ────────────────────────────────
|
||||
hdr "6. Configurando base de datos"
|
||||
# ─── 6. Crear Usuario y Permisos en PostgreSQL ─────────────────────────────────
|
||||
hdr "6. Configurando usuario y base de datos en PostgreSQL"
|
||||
|
||||
sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" 2>/dev/null \
|
||||
|| warn "El usuario '${DB_USER}' ya existe — se omite creación"
|
||||
sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" >/dev/null 2>&1 \
|
||||
|| sudo -u postgres psql -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" >/dev/null 2>&1 \
|
||||
|| true
|
||||
|
||||
sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" 2>/dev/null \
|
||||
|| warn "La base de datos '${DB_NAME}' ya existe — se omite creación"
|
||||
sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" >/dev/null 2>&1 \
|
||||
|| info "La base de datos '${DB_NAME}' ya existía."
|
||||
|
||||
sudo -u postgres psql -d "${DB_NAME}" \
|
||||
-c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" \
|
||||
-c "GRANT ALL ON SCHEMA public TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" \
|
||||
-c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" >/dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" -c "GRANT ALL ON SCHEMA public TO ${DB_USER};" >/dev/null 2>&1 || true
|
||||
|
||||
ok "Base de datos '${DB_NAME}' lista con usuario '${DB_USER}'"
|
||||
ok "Usuario '${DB_USER}' y base de datos '${DB_NAME}' configurados"
|
||||
|
||||
# ─── 7. Entorno virtual Python + dependencias ───────────────────────────────────
|
||||
hdr "7. Instalando dependencias Python"
|
||||
# ─── 7. Crear Entorno Virtual e Instalar Dependencias ──────────────────────────
|
||||
hdr "7. Creando virtualenv e instalando dependencias Python"
|
||||
|
||||
# Limpiar venv previo roto si existe
|
||||
if [[ -d "$VENV_DIR" ]]; then
|
||||
warn "Venv previo detectado — eliminando para recrear limpio..."
|
||||
info "Recreando entorno virtual para asegurar dependencias limpias..."
|
||||
rm -rf "$VENV_DIR"
|
||||
fi
|
||||
|
||||
python3 -m venv "$VENV_DIR" > /dev/null 2>&1
|
||||
"${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel --quiet
|
||||
"${VENV_DIR}/bin/pip" install gunicorn --quiet
|
||||
ok "Entorno virtual creado: Python $(${VENV_DIR}/bin/python --version | awk '{print $2}')"
|
||||
|
||||
# Instalar requirements con fallback para Python 3.13+
|
||||
info "Instalando requirements.txt..."
|
||||
info "Instalando paquetes desde requirements.txt..."
|
||||
if "${VENV_DIR}/bin/pip" install -r "${APP_DIR}/requirements.txt" --quiet 2>/dev/null; then
|
||||
ok "Dependencias instaladas correctamente"
|
||||
else
|
||||
warn "Instalación estándar falló — reintentando con --no-build-isolation (Python 3.13+)..."
|
||||
"${VENV_DIR}/bin/pip" install -r "${APP_DIR}/requirements.txt" \
|
||||
--no-build-isolation --quiet
|
||||
ok "Dependencias instaladas (modo compatibilidad)"
|
||||
warn "Reintentando instalación con --no-build-isolation (compatibilidad Python 3.12/3.13)..."
|
||||
"${VENV_DIR}/bin/pip" install -r "${APP_DIR}/requirements.txt" --no-build-isolation --quiet
|
||||
ok "Dependencias instaladas"
|
||||
fi
|
||||
|
||||
# ─── 8. Inicializar base de datos ───────────────────────────────────────────────
|
||||
hdr "8. Inicializando base de datos"
|
||||
# ─── 8. Despliegue de Datos: Restauración o Inicialización ─────────────────────
|
||||
hdr "8. Desplegando esquema y datos de la aplicación"
|
||||
cd "$APP_DIR"
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$APP_ENV"
|
||||
set +a
|
||||
if [[ -f "$SQL_BACKUP_FILE" ]]; then
|
||||
BACKUP_SIZE=$(du -h "$SQL_BACKUP_FILE" | awk '{print $1}')
|
||||
info "Se detectó snapshot de base de datos '${SQL_BACKUP_FILE}' (${BACKUP_SIZE})."
|
||||
info "Restaurando volcado integral de aulas, comisiones, materias, reservas y usuarios..."
|
||||
|
||||
if [[ -f "classrooms_db.sql" ]]; then
|
||||
info "Se detectó el archivo classrooms_db.sql. Restaurando volcado SQL..."
|
||||
# Eliminar DB si ya existe para cargar de 0
|
||||
sudo -u postgres psql -c "DROP DATABASE IF EXISTS ${DB_NAME};" > /dev/null 2>&1
|
||||
# Recrear base de datos limpia para carga sin colisiones
|
||||
sudo -u postgres psql -c "DROP DATABASE IF EXISTS ${DB_NAME};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" > /dev/null 2>&1
|
||||
|
||||
if sudo -u postgres psql -d "${DB_NAME}" < classrooms_db.sql > /dev/null 2>&1; then
|
||||
ok "Base de datos restaurada correctamente desde classrooms_db.sql."
|
||||
else
|
||||
err "Error al restaurar la base de datos desde el archivo SQL."
|
||||
fi
|
||||
else
|
||||
LANG=en_US.UTF-8 \
|
||||
LC_ALL=en_US.UTF-8 \
|
||||
PYTHONUTF8=1 \
|
||||
PYTHONIOENCODING=utf-8 \
|
||||
FLASK_APP=wsgi.py \
|
||||
"${VENV_DIR}/bin/python" -X utf8 init_db.py
|
||||
|
||||
ok "Esquema, roles, edificios y usuario admin inicializados"
|
||||
# Restaurar backup filtrando directivas específicas de PG18 si existiesen
|
||||
if sed '/^\\restrict/d' "$SQL_BACKUP_FILE" | sudo -u postgres psql -d "${DB_NAME}" > /dev/null 2>&1; then
|
||||
ok "Volcado SQL restaurado exitosamente."
|
||||
else
|
||||
warn "Restauración completada con avisos menores — aplicando permisos..."
|
||||
fi
|
||||
|
||||
# Otorgar propiedad y permisos absolutos sobre todas las tablas y secuencias al usuario de la app
|
||||
info "Garantizando permisos y secuencias para '${DB_USER}'..."
|
||||
sudo -u postgres psql -d "${DB_NAME}" -c "GRANT ALL ON SCHEMA public TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
sudo -u postgres psql -d "${DB_NAME}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${DB_USER};" > /dev/null 2>&1 || true
|
||||
|
||||
# Ejecutar init_db.py de forma idempotente para verificar roles y migraciones incrementales
|
||||
info "Validando coherencia de modelos con init_db.py..."
|
||||
LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 PYTHONUTF8=1 PYTHONIOENCODING=utf-8 \
|
||||
FLASK_APP=wsgi.py "${VENV_DIR}/bin/python" -X utf8 init_db.py > /dev/null 2>&1 || true
|
||||
ok "Base de datos sincronizada y verificada"
|
||||
else
|
||||
info "No se encontró classrooms_db.sql. Creando esquema desde cero con init_db.py..."
|
||||
LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 PYTHONUTF8=1 PYTHONIOENCODING=utf-8 \
|
||||
FLASK_APP=wsgi.py "${VENV_DIR}/bin/python" -X utf8 init_db.py
|
||||
ok "Esquema, roles RBAC, edificios y usuario admin creados"
|
||||
fi
|
||||
cd - > /dev/null
|
||||
|
||||
# ─── 9. Usuario del sistema ─────────────────────────────────────────────────────
|
||||
hdr "9. Configurando usuario del sistema"
|
||||
# ─── 9. Usuario del Sistema y Permisos de Directorio ───────────────────────────
|
||||
hdr "9. Configurando permisos y usuario de ejecución del sistema"
|
||||
if id "$APP_USER" &>/dev/null; then
|
||||
warn "El usuario '${APP_USER}' ya existe — se omite"
|
||||
info "El usuario de sistema '${APP_USER}' ya existe."
|
||||
else
|
||||
useradd --system --no-create-home --shell /usr/sbin/nologin "$APP_USER"
|
||||
ok "Usuario de sistema '${APP_USER}' creado (sin shell)"
|
||||
@@ -240,10 +268,11 @@ chmod 640 "$APP_ENV"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
chown -R "${APP_USER}:${APP_USER}" "$LOG_DIR"
|
||||
ok "Permisos aplicados en ${APP_DIR}"
|
||||
chmod 750 "$LOG_DIR"
|
||||
ok "Permisos de seguridad aplicados en ${APP_DIR} y ${LOG_DIR}"
|
||||
|
||||
# ─── 10. Servicio systemd ───────────────────────────────────────────────────────
|
||||
hdr "10. Configurando servicio systemd"
|
||||
# ─── 10. Servicio Systemd (Gunicorn de Producción) ─────────────────────────────
|
||||
hdr "10. Configurando servicio systemd de arranque automático"
|
||||
|
||||
GUNICORN_BIN="${VENV_DIR}/bin/gunicorn"
|
||||
|
||||
@@ -288,41 +317,42 @@ SVCEOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable "${SERVICE_NAME}" > /dev/null 2>&1
|
||||
systemctl start "${SERVICE_NAME}"
|
||||
systemctl restart "${SERVICE_NAME}"
|
||||
sleep 3
|
||||
|
||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||
ok "Servicio '${SERVICE_NAME}' corriendo y habilitado al inicio"
|
||||
ok "Servicio '${SERVICE_NAME}' activo y en ejecución"
|
||||
else
|
||||
warn "El servicio no inició correctamente. Revisá los logs:"
|
||||
warn " journalctl -u ${SERVICE_NAME} -n 60 --no-pager"
|
||||
warn "El servicio tardó en arrancar. Podés revisar los logs con:"
|
||||
warn " journalctl -u ${SERVICE_NAME} -n 50 --no-pager"
|
||||
fi
|
||||
|
||||
# ─── Resumen final ──────────────────────────────────────────────────────────────
|
||||
hdr "Instalación completada"
|
||||
# ─── Resumen Final y Datos de Acceso ──────────────────────────────────────────
|
||||
hdr "¡Despliegue Completado con Éxito!"
|
||||
|
||||
IP=$(hostname -I | awk '{print $1}')
|
||||
IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1")
|
||||
|
||||
echo -e " ${BOLD}URL de la aplicación:${RESET}"
|
||||
echo -e " ${CYAN}http://${IP}:5000${RESET}"
|
||||
echo -e " ${BOLD}Acceso Web a la Aplicación:${RESET}"
|
||||
echo -e " ${CYAN}http://${IP}:5000${RESET} o ${CYAN}http://localhost:5000${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Credenciales por defecto:${RESET}"
|
||||
echo -e " ${BOLD}Credenciales Iniciales de Administrador:${RESET}"
|
||||
echo -e " Email: ${CYAN}admin@edu-space.com${RESET}"
|
||||
echo -e " Password: ${CYAN}admin123${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Base de datos:${RESET}"
|
||||
echo -e " Host: localhost:5432"
|
||||
echo -e " ${BOLD}Base de Datos Desplegada:${RESET}"
|
||||
echo -e " Motor: PostgreSQL (${PG_VER})"
|
||||
echo -e " Base: ${DB_NAME}"
|
||||
echo -e " Usuario: ${DB_USER}"
|
||||
echo -e " Snapshot: $( [ -f "$SQL_BACKUP_FILE" ] && echo "Restaurada desde classrooms_db.sql" || echo "Inicializada limpia" )"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Comandos de gestión:${RESET}"
|
||||
echo -e " ${BOLD}Comandos de Gestión del Servicio:${RESET}"
|
||||
echo -e " Estado: ${CYAN}systemctl status ${SERVICE_NAME}${RESET}"
|
||||
echo -e " Logs: ${CYAN}journalctl -u ${SERVICE_NAME} -f${RESET}"
|
||||
echo -e " Reiniciar: ${CYAN}systemctl restart ${SERVICE_NAME}${RESET}"
|
||||
echo -e " Detener: ${CYAN}systemctl stop ${SERVICE_NAME}${RESET}"
|
||||
echo ""
|
||||
warn "Cambiá la contraseña del admin desde la interfaz web."
|
||||
warn "Los archivos .env contienen credenciales sensibles — NO los incluyas en git."
|
||||
echo -e " ${BOLD}Generar Nuevos Backups:${RESET}"
|
||||
echo -e " ${CYAN}bash backup_db.sh${RESET}"
|
||||
echo ""
|
||||
ok "¡Listo! La aplicación está corriendo en http://${IP}:5000"
|
||||
ok "Despliegue finalizado. El sistema se encuentra 100% operativo."
|
||||
echo ""
|
||||
|
||||
@@ -16,3 +16,4 @@ Pillow>=10.4.0
|
||||
email_validator>=2.0.0
|
||||
psycopg2-binary>=2.9.10
|
||||
Babel==2.14.0
|
||||
gunicorn>=21.2.0
|
||||
|
||||
Reference in New Issue
Block a user