# Stato Progetto TieMeasureFlow — V2.0.0 > Snapshot al 2026-04-27. Aggiornare ad ogni milestone. ## Versione corrente **V2.0.0** (in sviluppo, branch `V2.0.0` come default su `git.tielogic.xyz`). Versione precedente di produzione: `V1.0.7`. ## Sintesi esecutiva Il sistema base (V1.0.7) è completo e collaudato: ricette, task, misurazioni, SPC, report PDF, gestione utenti, dashboard metrologist. La V2.0.0 in corso aggiunge il primo blocco della migrazione **rev04** (stazioni per-tablet) e ristruttura l'intero monorepo secondo lo standard `python-project-spec-design.md` (uv + `src/backend/` + `src/frontend/flask_app/`). ## Cosa funziona oggi (V2.0.0 — branch corrente) ### Funzionalità ereditate da V1.0.7 - Autenticazione username/password + API key per-utente, ruoli combinabili (Maker, MeasurementTec, Metrologist) + flag `is_admin`. - Recipe versioning copy-on-write: una nuova versione si crea solo se la corrente ha già measurements; altrimenti update in-place. - Editor ricette (Maker) con annotation editor Fabric.js (~1200 LOC, collaudato su tablet). - Workflow operatore tablet: select_recipe → task_list → task_execute → task_complete, con barcode scanner e numpad touch (input USB calibro con burst detection). - Calcolo pass/fail con limiti UTL/UWL/LWL/LTL. - Dashboard SPC: capability (Cp/Cpk/Pp/Ppk), control chart (UCL/LCL = mean ± 3σ), istogramma con curva normale, calcoli puro stdlib (no numpy). - Report PDF (WeasyPrint + Kaleido SVG). - Setup page protetta da `SETUP_PASSWORD` per inizializzazione DB e seed. - i18n IT/EN (Flask-Babel + Alpine.js JSON). - Tema light/dark via `Alpine.store('theme')` + localStorage. ### Aggiunte V2.0.0 (rev04 Fase 1 — Stazioni per-tablet) - Tabelle `stations` + `station_recipe_assignments` (Alembic migration `002_add_stations.py`). - Modelli ORM: `Station`, `StationRecipeAssignment` con vincolo unique `(station_id, recipe_id)`. - Schemas Pydantic: `StationCreate/Update/Response`, `StationRecipeAssignmentCreate/Response`, `RecipeSummary`. - Service `station_service` (CRUD + assegnazioni + cascade delete). - Router `/api/stations` con CRUD admin + endpoint operatore `GET /api/stations/by-code/{code}/recipes`. - Seed automatico `ST-DEFAULT` con tutte le ricette esistenti (idempotente). - Variabile env client `STATION_CODE` letta da `Config`, helper `APIClient.get_station_recipes()`. - Filtro `select_recipe`: il client mostra solo le ricette assegnate alla propria stazione, errore se `STATION_CODE` non configurato. - **GUI admin completa** in `/admin/stations`: tabella con search, modal create/edit, modal gestione assegnazioni ricette, conferma eliminazione, link in navbar (desktop + mobile). - 47 nuovi test (32 server + 15 client) tutti pass. ### Aggiunte V2.0.0 (performance + multi-utente) - Gunicorn 5 workers × 4 thread (gthread) — capacità ~20 richieste concorrenti Flask, regge 20+ tablet. - Uvicorn 4 workers + `--proxy-headers --forwarded-allow-ips='*'`. - Rate limit middleware: identificazione IP reale via `X-Forwarded-For` → `X-Real-IP` → `request.client.host`. - Rate limit general 100 → 300 req/min/IP (per-tablet ora, non più condiviso). - Flask `ProxyFix(x_for=1, x_proto=1, x_host=1)` per IP reale dietro Nginx. - `APIClient` propaga `X-Forwarded-For` + `X-Real-IP` (sia JSON che multipart). - 12 test aggiuntivi (7 server + 5 client). ### Aggiunte V2.0.0 (struttura monorepo) - `pyproject.toml` unico con extra `server`/`client`/`dev`. Niente più `requirements.txt`. - `uv.lock` (77 pacchetti) + `.python-version` (3.11) committati per build riproducibili. - Layout `src/backend/` + `src/frontend/flask_app/` (vedi sotto). - `Dockerfile` (root) + `Dockerfile.frontend` riscritti con `uv sync --frozen --no-dev --extra server|client`. - `docker-compose.{dev,}.yml` con build context `.`. - Alembic env.py aggiunge project root a `sys.path`; `script_location = %(here)s` resta valido. - `.dockerignore` aggiornato. ### Hardening post-restructure (smoke test 2026-04-26) Sequenza di smoke test in locale (uvicorn + gunicorn + MySQL Docker) ha fatto emergere quattro regressioni che sarebbero rimaste invisibili al test suite: - **`src/backend/config.py`**: `env_file` era cwd-relative (`../../.env`). Rotto fuori da `src/backend/`. Risolto con percorso assoluto `Path(__file__).resolve().parents[2] / ".env"`. - **`src/backend/models/orm/__init__.py`**: `Station` e `StationRecipeAssignment` non erano esportati, quindi `Base.metadata.create_all` non creava le tabelle stations. Aggiunti agli import. - **`.env.example`**: `UPLOAD_DIR=server/uploads` era residuo della vecchia struttura → file landavano fuori dall'albero di progetto. Aggiornato a `UPLOAD_DIR=uploads`. - **Apostrofi italiani in template Alpine** (`l'utente`, `nell'eliminazione`, `nell'assegnazione`): chiudevano prematuramente JS string literals dentro `x-text` e blocchi `