README.md
Rama main · réplica de lectura del repositorio original.
# 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)
[](LICENSE)


**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.
<!--
Paraules clau (per a la descoberta a cercadors):
gestió de guàrdies · substitucions del professorat · vigilàncies d'exàmens ·
cobertura d'absències · guardias y sustituciones · reparto de guardias ·
teacher cover / substitute scheduling · exam invigilation · cover supervision.
-->
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 |
|---|---|---|
|  |  |  |
> _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é.