Ctrl K
ComunidadGuías…

README.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 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.

<!--
  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 |
|---|---|---|
| ![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é.