# Gestor de Guàrdies, Substitucions i Vigilàncies d'Exàmens > Aplicació web **de codi obert**, desenvolupada amb **Claude Code**, per organitzar les **guàrdies i substitucions** > del professorat i les **vigilàncies d'exàmens** d'un centre educatiu: assignació > automàtica i equitativa, informes en PDF i importació de l'horari des de > [FET](https://lalescu.ro/liviu/fet/). **Català** · [Castellano](README.es.md) · [English](README.en.md) [![Llicència: AGPL v3+](https://img.shields.io/badge/Llic%C3%A8ncia-AGPLv3%2B-blue.svg)](LICENSE) ![Stack](https://img.shields.io/badge/FastAPI-Vue%203-009688) ![Self-hosted](https://img.shields.io/badge/self--hosted-Docker%20%2B%20Caddy-2496ED) **Demo pública:** [demo.gestor.alienamrt.org](https://demo.gestor.alienamrt.org) — entra amb `user_demo` o `admin_demo` (per veure les funcions d'administració), contrasenya `demo1234`. _La demo es reinicia cada dia a les 4:00._ > ⚠️ La demo és **pública i compartida**: altres persones hi poden estar treballant alhora i veure el que hi introduïu. No hi pugeu horaris ni dades personals reals del vostre centre. Si busques un **programa de guàrdies escolars**, un **gestor de substitucions de professorat** o una eina per **repartir vigilàncies d'exàmens** de manera justa, aquest projecte cobreix tot el flux en una sola aplicació, autoallotjable i gratuïta. --- ## Captures de pantalla | Substitucions diàries | Vigilàncies i grups | Planificador d'exàmens | |---|---|---| | ![Vista de substitucions](docs/img/substitucions.png) | ![Vista de vigilàncies](docs/img/vigilancies.png) | ![Planificador d'exàmens](docs/img/planificador.png) | > _Captures fetes amb la institució d'exemple (`Prof 1`, `Prof 2`…); cap dada real._ --- ## Funcionalitats - **Guàrdies i substitucions diàries** amb assignació automàtica de substituts - **Vigilàncies d'exàmens** i gestió de grups sense classe - **Repartiment equitatiu** de tasques segons càrrega i restriccions configurables - **Exportació a PDF** (substitucions, vigilàncies, intervals, informes de direcció) - **Planificador d'exàmens** amb optimització automàtica (3 motors de generació) - **Importació d'horari des de FET** (XML de _Free Timetabling Software_) - **Multi-institució**: una base de dades per centre, autenticació global - **Rols**: `super_admin`, `admin`, `user` - **Multilingüe**: català, castellà, anglès i italià ## Arquitectura **Stack:** FastAPI + Vue 3 + PrimeVue · SQLite · Docker + Caddy ### Topologia (runtime) ``` Caddy (reverse proxy + HTTPS) ├── /api/* → backend:8000 (FastAPI) └── /* → frontend:80 (Vue 3 + Nginx) data/ ├── auth.db # Usuaris globals └── {institucio}/ ├── gestor.db # Dades del centre ├── *.xml # Horari FET del centre └── exports/ # PDFs generats ``` ### Backend (FastAPI) - `main.py` — punt d'entrada, registra 18 routers a `/api/*` - `routes/` — endpoints per àrea funcional (substitucions, vigilàncies, scheduler, informes, PDFs, auth, files, settings...) - `core/` — lògica de negoci: motor d'assignació automàtica de substituts i vigilàncies, alliberats (`sense_classe`: qui no té classe a una hora) i absències - `serveis/` — la lògica que criden les rutes: l'estat d'un dia (`DiaEscolar`) i la regla de quan un substitut ja no pot ser-ho, generar les substitucions d'un dia, marcar absències, cobertures dels vigilants, i validar abans del PDF. Cap servei no depèn de `routes/` - `scheduler_engine/` — motor de planificació d'exàmens amb 3 generadors (v2-intents, v2-backtrack, v3-SA Simulated Annealing) i model de restriccions configurable - `export/` — exportadors PDF (substitucions, vigilàncies, intervals, informes de direcció i professorat) - `repositories.py` + `models.py` — accés a dades SQLAlchemy - `auth/` — autenticació JWT amb cookie httpOnly; la sessió caduca després d'1 h sense activitat (es renova mentre s'hi treballa) i com a màxim 24 h després d'entrar - `usuaris/` — gestió d'usuaris (admin del centre i superadmin) - `prioritats/` — ordre i pes de les activitats per triar substituts i «No substituir» - `absencia_coberta/` — absències cobertes: un professor absent que té les classes cobertes a l'XML queda fora de l'horari dels dies de l'absència - `noms.py` — els noms de professors, assignatures i aules, sense espais al principi ni al final (`migracions/noms_nets.py` hi adapta les dades desades) ### Frontend (Vue 3) - Sense Vue Router: navegació per pestanyes amb PrimeVue TabView - 4 vistes principals: `SubstitucionsView`, `VigilanciesView`, `GrupsView`, `SchedulerView` (planificador d'exàmens) - Composables a `views/scheduler/use*.js` per a la lògica de cada subàrea del planificador (API, resultats, restriccions, slots, incidències) - i18n amb `vue-i18n`: traduccions a `locales/{ca,es,en,it}.json` - Pinia per a estat compartit (preparada però amb poc ús actual) ### Regles de funcionament - Un professor queda lliure si el seu grup està marcat sense classe aquella hora (exactament, també les agrupacions) o si ell està alliberat. - Només assignen substituts els botons «Genera pendents», «Regenera tot» i «Reassigna» (a la validació abans del PDF). Els canvis del dia (alliberats, absències, absències cobertes) treuen els substituts que ja no poden ser-ho i deixen la fila pendent, amb un avís. - Un professor amb una absència coberta (té les classes cobertes a l'XML, per un substitut o per altres professors) queda fora de tot durant el període: ni substitueix, ni és substituït, ni vigila. Per al mapa de dependències complet, les regles en detall i el flux de la generació, veure [ARQUITECTURA.md](ARQUITECTURA.md). --- ## Prova ràpida en local Per provar l'aplicació amb les dades d'exemple incloses, sense Docker ni domini. **Requisits:** Python 3.10+ i Node.js 22+ ### 1. Clonar ```bash git clone https://github.com/mrtvillaret/fet-substitutions-manager.git cd fet-substitutions-manager ``` ### 2. Backend ```bash cd backend python3 -m venv venv source venv/bin/activate # Linux/macOS # venv\Scripts\activate # Windows pip install -r requirements.txt cp .env.example .env # Edita .env: posa rutes absolutes a DATA_DIR i AUTH_DB_PATH apuntant a # {ruta del projecte}/data i {ruta del projecte}/data/auth.db uvicorn main:app --reload --host 0.0.0.0 --port 8000 ``` El backend crea automàticament les BDs (`auth.db` i `data/{APP_INSTITUCIO}/gestor.db`) al primer arrencat i, si encara no hi ha cap usuari, un super_admin segons `ADMIN_USERNAME`/`ADMIN_PASSWORD` del `.env`. API disponible a `http://localhost:8000` · Documentació a `http://localhost:8000/docs` (només amb `ENVIRONMENT=development`; en qualsevol altre entorn no es publica). ### 3. Frontend En una altra terminal: ```bash cd frontend npm install npm run dev ``` Obre `http://localhost:5173` i entra amb les credencials `super_admin` definides al `.env` del backend. El frontend en dev apunta al backend mitjançant el proxy de Vite. ### 4. Carregar les dades d'exemple Un cop dins, ves a **Configuració > Importar XML** i puja `data/exemple/teachers.xml`. És un horari d'exemple generat amb FET. ### 5. Proves (opcional) ```bash cd backend pip install -r requirements-dev.txt pytest tests/ ``` Més de 700 tests sobre institucions temporals amb l'horari d'exemple; la majoria passen per l'API i comproven el que queda desat a la BD. Els de regressió del planificador fan servir també l'horari d'exemple. --- ## Docker local (sense domini) Si vols provar-ho amb Docker sense haver de configurar un domini ni HTTPS, hi ha un `docker-compose.local.yml.example` que aixeca només backend + frontend (sense Caddy) i serveix l'app a `http://localhost:8080`. ```bash cp .env.example .env cp docker-compose.local.yml.example docker-compose.local.yml # Edita .env: genera SECRET_KEY (openssl rand -hex 32), posa COOKIE_SECURE=false # (cal per HTTP plain) i una ADMIN_PASSWORD docker compose -f docker-compose.local.yml up --build -d ``` Obre `http://localhost:8080`. > ⚠️ Aquest mode no té HTTPS; només per a desenvolupament local. **No usar en > producció**, els tokens viatgen en clar. --- ## Desplegar en producció Recomanat amb Docker + Caddy (HTTPS automàtic). Et caldrà: - Un servidor amb Docker i Docker Compose - Un domini apuntant al servidor ### 1. Clonar i configurar ```bash git clone https://github.com/mrtvillaret/fet-substitutions-manager.git cd fet-substitutions-manager cp .env.example .env cp Caddyfile.example Caddyfile cp docker-compose.yml.example docker-compose.yml ``` Edita el `.env`: ```env SECRET_KEY= # obligatòria — genera-la amb: openssl rand -hex 32 APP_INSTITUCIO=nom_del_centre # slug sense espais/accents ADMIN_INSTITUCIO=nom_del_centre # idèntic a APP_INSTITUCIO ADMIN_PASSWORD=una-contrasenya-segura # obligatòria al primer arrencada ``` Edita el `Caddyfile` i substitueix `el-teu-domini.exemple.com` pel teu domini real (amb DNS apuntant ja al servidor). ### 2. Arrencar ```bash docker compose up --build -d ``` Caddy obté el certificat TLS automàticament. L'aplicació estarà a `https://{el-teu-domini}`. L'aplicació **no arrenca** si falta `SECRET_KEY` o, en una instal·lació nova (sense cap usuari), `ADMIN_PASSWORD`. No tenen valor per defecte a propòsit: així cap instal·lació queda oberta amb credencials que consten en aquest repositori. Si es perd l'accés del superadmin, es recupera amb `ADMIN_PASSWORD_RECUPERACIO` (vegeu `.env.example`), que cal treure de l'entorn després de fer-la servir. ### 3. Primer accés - Entra com a `super_admin` amb la contrasenya definida a `ADMIN_PASSWORD` - **Canvia la contrasenya** immediatament des de Configuració > Usuaris - Puja l'XML del teu centre (generat per [FET](https://lalescu.ro/liviu/fet/)) des de Configuració > Importar XML ### Registre d'accessos Si activeu l'`access log` del proxy (vegeu `Caddyfile.example`), es desen adreces IP. L'exemple porta un termini de conservació configurable. ### Actualitzacions ```bash cd /ruta/al/projecte git pull docker compose up --build -d ``` --- ## Estructura del projecte ``` fet-substitutions-manager/ ├── backend/ │ ├── main.py # FastAPI app + startup │ ├── routes/ # Endpoints: substitucions, vigilancies, scheduler... │ ├── core/ # Lògica de negoci (motor, alliberats, absències) │ ├── serveis/ # Lògica que criden les rutes (DiaEscolar, generació, PDF...) │ ├── scheduler_engine/ # Motor de planificació d'exàmens │ ├── config/ # Configuració, constants, settings │ ├── repositories.py # Accés a dades (SQLAlchemy) │ ├── requirements.txt │ ├── Dockerfile │ └── .env.example ├── frontend/ │ ├── src/ │ │ ├── App.vue │ │ ├── views/ # SubstitucionsView, VigilanciesView, SchedulerView... │ │ ├── components/ # Dialogs, navbar, etc. │ │ └── locales/ # Traduccions ca/es/en/it │ ├── Dockerfile │ └── package.json ├── data/ │ └── exemple/ │ └── teachers.xml # Horari d'exemple generat amb FET ├── scripts/ │ ├── setup-server.sh # Preparació inicial del servidor │ └── sync-server.sh.example # Script de sincronització al servidor ├── docker-compose.yml.example # Plantilla Docker Compose (copia a docker-compose.yml) ├── docker-compose.local.yml.example # Plantilla per a proves locals sense domini ├── Caddyfile.example # Plantilla Caddy (copia a Caddyfile) └── .env.example # Plantilla variables d'entorn (copia a .env) ``` --- ## Dades d'exemple El directori `data/exemple/` inclou un `teachers.xml`: un horari d'exemple generat amb [FET](https://lalescu.ro/liviu/fet/). --- ## Backups Totes les dades es troben a la carpeta `./data/`. Fes còpies periòdiques d'aquesta carpeta amb l'eina que prefereixis (cron + rsync, rclone a un núvol, etc.). ```bash # Exemple mínim amb rsync rsync -a /opt/gestor/data/ /backup/gestor-data/ ``` --- ## Llicència Publicat sota la [GNU Affero General Public License, versió 3 o posterior](LICENSE). Copyright (C) 2026 Martí Villaret Ausellé.