# Teacher Cover, Substitutions & Exam Invigilation Manager > **Open-source** web app, developed with **Claude Code**, to organise **teacher cover / substitutions** and > **exam invigilation** in a school: automatic, fair duty assignment, PDF reports > and timetable import from [FET](https://lalescu.ro/liviu/fet/). [Català](README.md) · [Castellano](README.es.md) · **English** [![License: AGPL v3+](https://img.shields.io/badge/License-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) **Live demo:** [demo.gestor.alienamrt.org](https://demo.gestor.alienamrt.org) — log in with `user_demo` or `admin_demo` (to see the admin features), password `demo1234`. _The demo resets daily at 4:00._ > ⚠️ The demo is **public and shared**: other people may be using it at the same time and can see whatever you enter. Do not upload timetables or real personal data from your school. If you are looking for **cover supervision software**, a **substitute teacher scheduler** or a tool to build a fair **exam invigilation rota**, this project covers the whole workflow in a single, self-hostable, free application. --- ## Screenshots | Daily substitutions | Invigilation & groups | Exam scheduler | |---|---|---| | ![Substitutions view](docs/img/substitucions.png) | ![Invigilation view](docs/img/vigilancies.png) | ![Exam scheduler](docs/img/planificador.png) | > _Screenshots use the sample institution (`Prof 1`, `Prof 2`…); no real data._ --- ## Features - **Daily cover & substitutions** with automatic substitute assignment - **Exam invigilation** and management of groups without class - **Fair workload distribution** based on load and configurable constraints - **PDF export** (substitutions, invigilation, intervals, management reports) - **Exam scheduler** with automatic optimisation (3 generation engines) - **FET timetable import** (XML from _Free Timetabling Software_) - **Multi-institution**: one database per school, global authentication - **Roles**: `super_admin`, `admin`, `user` - **Multilingual UI**: Catalan, Spanish, English and Italian ## Architecture **Stack:** FastAPI + Vue 3 + PrimeVue · SQLite · Docker + Caddy ``` Caddy (reverse proxy + HTTPS) ├── /api/* → backend:8000 (FastAPI) └── /* → frontend:80 (Vue 3 + Nginx) data/ ├── auth.db # Global users └── {institution}/ ├── gestor.db # School data ├── *.xml # School's FET timetable └── exports/ # Generated PDFs ``` The FastAPI backend exposes the API under `/api/*`, handles XML timetable parsing, automatic assignment of substitutes and invigilators, and PDF export. The Vue 3 + PrimeVue frontend organises navigation into tabs: substitutions, invigilation, groups and the exam scheduler. The scheduling engine (`scheduler_engine/`) ships three generators (attempts, backtracking and Simulated Annealing) with a configurable constraint model. The logic called by the routes lives in `serveis/` (the state of a day, `DiaEscolar`, and the single rule for when a substitute can no longer cover; generating substitutions, marking absences, validating before the PDF), and the assignment engine in `core/`. ### Rules - A teacher is free if their group is marked as having no class at that hour (exactly, including combined groups) or if they are released. - Only the "Generate pending", "Regenerate all" and "Reassign" (in the pre-PDF validation) buttons assign substitutes. Changes to the day (released groups, absences, covered absences) remove substitutes who can no longer cover and leave the row pending, with a notice. - A teacher with a covered absence (their classes are covered in the XML, by a substitute or by other teachers) is left out of everything during the period: they neither cover, nor are covered, nor supervise. Over 700 tests (`cd backend && pytest tests/`) on temporary institutions with the sample timetable. For the full dependency map and the rules in detail see [ARQUITECTURA.md](ARQUITECTURA.md) (in Catalan). --- ## Quick local test To try the app with the bundled sample data, without Docker or a domain. **Requirements:** Python 3.10+ and Node.js 22+ ### 1. Clone ```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 # Edit .env: set absolute paths in DATA_DIR and AUTH_DB_PATH pointing to # {project path}/data and {project path}/data/auth.db uvicorn main:app --reload --host 0.0.0.0 --port 8000 ``` The backend automatically creates the databases (`auth.db` and `data/{APP_INSTITUCIO}/gestor.db`) on first start and, if there are no users yet, a super_admin from `ADMIN_USERNAME`/`ADMIN_PASSWORD` in `.env`. API at `http://localhost:8000` · Docs at `http://localhost:8000/docs`. ### 3. Frontend In another terminal: ```bash cd frontend npm install npm run dev ``` Open `http://localhost:5173` and log in with the `super_admin` credentials defined in the backend `.env`. The dev frontend proxies to the backend via Vite. ### 4. Load the sample data Once inside, go to **Settings > Import XML** and upload `data/exemple/teachers.xml`. It is a sample timetable generated with FET. --- ## Local Docker (no domain) To try it with Docker without configuring a domain or HTTPS, there is a `docker-compose.local.yml.example` that starts only backend + frontend (no Caddy) and serves the app at `http://localhost:8080`. ```bash cp .env.example .env cp docker-compose.local.yml.example docker-compose.local.yml # Edit .env: generate SECRET_KEY (openssl rand -hex 32), set COOKIE_SECURE=false # (required for plain HTTP) and an ADMIN_PASSWORD docker compose -f docker-compose.local.yml up --build -d ``` Open `http://localhost:8080`. > ⚠️ This mode has no HTTPS; local development only. **Do not use in production**, > tokens travel in clear text. --- ## Production deployment Recommended with Docker + Caddy (automatic HTTPS). You will need: - A server with Docker and Docker Compose - A domain pointing to the server ### 1. Clone and configure ```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 ``` Edit `.env`: ```env SECRET_KEY= # required — generate with: openssl rand -hex 32 APP_INSTITUCIO=school_name # slug without spaces/accents ADMIN_INSTITUCIO=school_name # identical to APP_INSTITUCIO ADMIN_PASSWORD=a-strong-password ``` Edit `Caddyfile` and replace `el-teu-domini.exemple.com` with your real domain (with DNS already pointing to the server). ### 2. Start ```bash docker compose up --build -d ``` Caddy obtains the TLS certificate automatically. The app will be at `https://{your-domain}`. The application **will not start** without `SECRET_KEY`, or, on a new installation (no users yet), without `ADMIN_PASSWORD`. Neither has a default value, on purpose: no installation should end up exposed with credentials that are published in this repository. If super_admin access is lost, it can be recovered with `ADMIN_PASSWORD_RECUPERACIO` (see `.env.example`), which must be removed from the environment after use. ### 3. First login - Log in as `super_admin` with the password set in `ADMIN_PASSWORD` - **Change the password** immediately from Settings > Users - Upload your school's XML (generated by [FET](https://lalescu.ro/liviu/fet/)) from Settings > Import XML ### Access log If you enable the proxy's access log (see `Caddyfile.example`), IP addresses are stored. The example ships with a configurable retention period. ### Updates ```bash cd /path/to/project git pull docker compose up --build -d ``` --- ## Sample data The `data/exemple/` directory includes a `teachers.xml`: a sample timetable generated with [FET](https://lalescu.ro/liviu/fet/). --- ## Backups All data lives in the `./data/` folder. Make periodic copies of this folder with your preferred tool (cron + rsync, rclone to a cloud, etc.). ```bash # Minimal rsync example rsync -a /opt/gestor/data/ /backup/gestor-data/ ``` --- ## License Released under the [GNU Affero General Public License, version 3 or later](LICENSE). Copyright (C) 2026 Martí Villaret Ausellé.