Adriano Dal Pastro 55e5e0153f feat(production): dai una vita propria alla produzione, lato server
Punto 1 del documento modifiche del 28/07, il prerequisito su cui poggiano i
punti 3, 4 e 6.

Lo stato di una produzione viveva dentro una pagina del browser: timer, conteggio
cicli e flag "produzione avviata" erano variabili Alpine di task_execute.html, e
la navigazione fra task e' un ricaricamento completo, quindi cambiando task si
perdeva tutto. Da qui il loop di misura che non reggeva, il fermo linea che non
aveva nulla da fermare e l'assenza di storico.

Nuove tabelle production_runs e production_events (migrazione 005), endpoint REST
senza stato in memoria di processo - con un'app di stazione installata su ogni PC
il database e' l'unico posto condiviso - e il frontend che legge lo stato
all'apertura invece di tenerlo in memoria.

Tre scelte di modello:

- la scadenza e' un timestamp assoluto (next_measurement_at), non un contatore:
  il countdown si ricalcola da li' a ogni caricamento, e lasciarne andare la
  differenza sotto zero dara' gratis il contatore del ritardo del punto 3. Al
  client vanno i secondi gia' calcolati, non il timestamp: un datetime naive
  verrebbe letto nel fuso del browser e il conto sarebbe sfasato dell'offset UTC;
- l'intervallo di misura e' copiato sulla produzione, non referenziato: modificare
  la ricetta a produzione avviata non deve spostare una scadenza in corso;
- active_station_id rispecchia la stazione finche' la produzione e' aperta e va a
  NULL alla chiusura. Con un vincolo unico sopra, "una stazione = una produzione
  aperta" e' una garanzia del database e non un controllo soggetto a race; i NULL
  non collidono, quindi le produzioni chiuse si accumulano senza disturbo.

Il fermo linea congela il conto alla rovescia e alla ripresa la scadenza viene
traslata della durata del fermo, non ricalcolata: un fermo non regala ne' toglie
tempo all'operatore. L'autorizzazione del capoturno passa da authenticate_user e
non da un login, che rigenererebbe la sua API key buttando giu' la sessione che ha
aperta altrove.

La migrazione e' stata eseguita davvero, non solo scritta, su uno SQLite usa e
getta: upgrade e downgrade girano e le colonne coincidono con i modelli. La prova
ha trovato un difetto - create_unique_constraint dopo create_table e' un ALTER,
che SQLite rifiuta - ora il vincolo e' dichiarato dentro create_table.

Fuori da questo commit, per stare nei confini del punto 1: l'API espone gia'
pause, resume e close, ma i pulsanti fermo linea e fine produzione restano da
collegare (punto 6), e il rientro forzato sulla misura allo scadere e' il punto 3.

Corretti due difetti trovati strada facendo: env.py non importava ne' Station ne'
ProductionRun, quindi l'autogenerate di Alembic era gia' cieco sulle stazioni; e
task_execute.html, lo schermo con piu' JavaScript dell'applicazione, non era
coperto dal test di sintassi. Aggiungerlo ha richiesto di correggere l'helper, che
validava le espressioni Alpine solo come espressione singola e bocciava
@click="a = false; b = true", forma che Alpine accetta: ora prova entrambe le
letture e fallisce solo se cadono tutte e due.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 16:20:43 +00:00

TieMeasureFlow by Tielogic

Sistema di gestione task per misurazioni con calibro manuale. Soluzione tablet-first, multi-ruolo, con statistiche SPC (Statistical Process Control) integrate, identità per-stazione e rate limiting per-tablet.

Versione corrente: V2.0.0 (in sviluppo) — branch default V2.0.0. Per stato dettagliato e prossimi passi vedi docs/architecture/STATO_PROGETTO.md e docs/architecture/ROADMAP.md.


Panoramica

TieMeasureFlow guida l'operatore attraverso sequenze di misurazione definite dal Maker, registra i valori (da calibro USB HID o numpad touch), e valuta automaticamente la conformità rispetto alle tolleranze configurate. I dati raccolti alimentano una dashboard SPC con indici di capability e control chart, esportabili in PDF.

Caratteristiche principali:

  • Recipe versioning condizionale (copy-on-write se esistono misurazioni, update in-place altrimenti)
  • Calcolo pass/fail/warning su quattro limiti di tolleranza (UTL, UWL, LWL, LTL)
  • Tracciamento del tempo di inserimento per ogni misura (input_duration_ms): il client misura quanto tempo l'operatore impiega su ciascun subtask, dall'attivazione al salvataggio, utile per analisi di tempo ciclo e produttività
  • SPC: Cp, Cpk, Pp, Ppk, control chart UCL/LCL, istogramma con curva normale — puro stdlib (no numpy)
  • Annotazioni grafiche su disegni tecnici (Fabric.js) con viewer sincronizzato all'esecuzione
  • Identità per-stazione (STATION_CODE): ogni tablet vede solo le ricette assegnate alla propria stazione, gestione assegnazioni via GUI admin
  • Interfaccia completamente localizzata IT/EN, dark mode, ottimizzata per tablet touch
  • Autenticazione API Key, rate limiting sliding window per-IP reale (X-Forwarded-For-aware), audit log persistente
  • Capacità testata per ~20 tablet contemporanei (gunicorn 5 workers × 4 thread + uvicorn 4 workers async)

Architettura

Browser/Tablet
      |
Reverse Proxy (Nginx — sviluppo | Traefik+SSL — produzione)
      |
Flask Frontend  :5000  (rendering server-side, Jinja2 + Alpine.js)
      |  X-API-Key + X-Forwarded-For
FastAPI Backend  :8000  (API REST asincrona)
      |
MySQL 8.0

Il frontend Flask non espone mai le credenziali al browser: ogni chiamata al backend avviene server-side con l'header X-API-Key estratto dalla sessione Flask. L'IP reale del tablet è propagato in X-Forwarded-For per il rate limiter.


Stack Tecnologico

Backend (src/backend/)

Componente Versione Ruolo
FastAPI ultima stabile Framework API REST asincrono
SQLAlchemy 2.0 async ORM con pool connessioni 10+20
asyncmy ultima stabile Driver MySQL asincrono
MySQL 8.0 Database relazionale
Alembic ultima stabile Migrazioni schema
Pydantic v2 v2 Validazione I/O
WeasyPrint ultima stabile Generazione report PDF
Kaleido ultima stabile Export grafici Plotly in SVG
bcrypt ultima stabile Hashing password
Pillow ultima stabile Thumbnail automatici upload

Frontend (src/frontend/flask_app/)

Componente Versione Ruolo
Flask 3.x Framework web server-side
gunicorn 21+ WSGI server (5 workers × 4 thread gthread)
Jinja2 incluso in Flask Template engine
Alpine.js 3.x (CDN) Reattività leggera lato client
TailwindCSS 3.x CSS utility-first
Plotly.js CDN Grafici SPC interattivi
Fabric.js 5.3.1 (CDN) Editor annotazioni disegni tecnici
html5-qrcode CDN Scanner barcode/QR camera
Flask-Babel ultima stabile i18n IT/EN

Tooling

Componente Ruolo
uv Package manager Python (no requirements.txt)
pyproject.toml Dipendenze monorepo con extra server/client/dev
uv.lock Lockfile per build riproducibili
.python-version Pin Python 3.11
pytest + pytest-asyncio + httpx + aiosqlite Test stack

Quick Start con Docker

Docker Compose è il metodo raccomandato. Gestisce database, migrazioni, build delle immagini con uv e configurazione Nginx in un solo comando.

# 1. Clona il repository
git clone ssh://git@git.tielogic.xyz:222/Adriano/TieMeasureFlow.git
cd TieMeasureFlow

# 2. Configura le variabili d'ambiente
cp .env.example .env
# Modifica .env: credenziali DB, chiavi segrete, SETUP_PASSWORD, STATION_CODE per ogni tablet

# 3. Avvia i servizi (ambiente di sviluppo)
docker compose -f docker-compose.dev.yml up -d --build

# 4. Verifica lo stato dei container
docker compose -f docker-compose.dev.yml ps

# 5. Setup iniziale (solo al primo avvio)
# Apri http://localhost/api/setup nel browser
# Usa SETUP_PASSWORD configurata in .env
# Lo script seed crea anche la stazione ST-DEFAULT con tutte le ricette assegnate

L'applicazione sarà disponibile su:

Per il deployment in produzione (Traefik + SSL) consulta docs/DEPLOYMENT.md.


Setup Iniziale

Dopo il primo avvio, la pagina /api/setup (protetta da SETUP_PASSWORD) permette di:

  • Initialize Database — crea tutte le tabelle
  • Create Admin User — crea l'utente amministratore con credenziali da .env
  • Seed Demo Data — carica ricette, misurazioni e utenti di esempio + crea stazione ST-DEFAULT con tutte le ricette assegnate
  • Reset Database — elimina e ricrea tutte le tabelle (attenzione: cancella tutti i dati)
  • Gestione utenti — crea, modifica, attiva/disattiva account dalla stessa pagina

Se SETUP_PASSWORD è vuota o assente nel .env, l'endpoint è disabilitato.

Per gestire stazioni e assegnazioni ricette dopo il setup: /admin/stations (richiede login admin).


Setup Manuale (Senza Docker)

Requisiti

  • Python 3.11 o superiore
  • Node.js 18 o superiore (per TailwindCSS)
  • MySQL 8.0
  • uv installato

1. Database MySQL

mysql -u root -p <<'SQL'
CREATE DATABASE tiemeasureflow CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'tielogic'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON tiemeasureflow.* TO 'tielogic'@'localhost';
FLUSH PRIVILEGES;
SQL

2. Configurazione

cp .env.example .env
# Imposta DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, SERVER_SECRET_KEY,
# CLIENT_SECRET_KEY, SETUP_PASSWORD, STATION_CODE

3. Installa dipendenze (uv)

uv sync --extra server --extra client --extra dev

4. Backend FastAPI

uv run alembic -c src/backend/migrations/alembic.ini upgrade head
uv run uvicorn src.backend.main:app --reload --host 0.0.0.0 --port 8000

5. Frontend Flask

# Compila i cataloghi i18n una volta
cd src/frontend/flask_app && uv run --project ../../.. pybabel compile -d translations && cd -

# Avvia (development)
cd src/frontend/flask_app && uv run --project ../../.. flask run --host 0.0.0.0 --port 5000

6. TailwindCSS (watch per sviluppo)

cd src/frontend/flask_app
npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --watch

Accesso diretto (senza Nginx):


Ruoli Utente

I ruoli sono combinabili (array JSON per utente). Il flag is_admin è separato dai ruoli funzionali.

Ruolo Descrizione
Maker Crea e gestisce ricette di misurazione: caricamento disegni (PDF/immagini), annotazioni Fabric.js, definizione task/subtask, configurazione tolleranze, versioning copy-on-write
MeasurementTec Esegue misurazioni: scansione barcode per selezione ricetta, interfaccia task-driven, input da calibro USB HID o numpad touch, validazione real-time pass/warning/fail. Vede solo le ricette assegnate alla propria stazione (STATION_CODE)
Metrologist Analisi qualità: dashboard SPC (X-bar, R, Cp, Cpk, Pp, Ppk), filtri multi-dimensionali, export report PDF, analisi capability e control chart
Admin (flag) Gestione sistema: CRUD utenti, cambio password, attivazione/disattivazione account, CRUD stazioni e assegnazioni ricette

Struttura Progetto

TieMeasureFlow/
├── pyproject.toml              # Dipendenze monorepo (uv)
├── uv.lock                     # Lockfile riproducibile
├── .python-version             # 3.11
├── Dockerfile                  # Backend (uv + uvicorn)
├── Dockerfile.frontend         # Frontend (uv + gunicorn + Tailwind + Babel)
├── docker-compose.dev.yml      # Sviluppo (Nginx, porta 80)
├── docker-compose.yml          # Produzione (Traefik, SSL)
├── nginx/                      # Config Nginx (dev)
├── uploads/                    # Volume Docker file caricati
├── docs/                       # Documentazione (vedi indice docs/README.md)
└── src/
    ├── backend/                # FastAPI Backend
    │   ├── main.py             # Entry point, lifespan, middleware, 11 router
    │   ├── config.py           # Settings (pydantic_settings.BaseSettings)
    │   ├── database.py         # SQLAlchemy 2.0 async engine
    │   ├── api/
    │   │   ├── routers/        # auth, users, recipes, tasks, measurements,
    │   │   │                   #   files, settings, statistics, reports,
    │   │   │                   #   setup, stations
    │   │   └── middleware/     # api_key, rate_limit, security_headers, logging
    │   ├── models/
    │   │   ├── orm/            # SQLAlchemy: User, Recipe, RecipeVersion,
    │   │   │                   #   RecipeTask, RecipeSubtask, Measurement,
    │   │   │                   #   AccessLog, SystemSetting,
    │   │   │                   #   RecipeVersionAudit, Station,
    │   │   │                   #   StationRecipeAssignment
    │   │   └── api/            # Pydantic v2 schemas request/response
    │   ├── services/           # recipe_service, measurement_service,
    │   │                       #   spc_service, report_service,
    │   │                       #   auth_service, station_service
    │   ├── migrations/         # Alembic (alembic.ini + env.py)
    │   ├── templates/          # Pagina setup (Jinja2)
    │   └── tests/              # pytest + httpx + aiosqlite
    └── frontend/
        └── flask_app/          # Flask Frontend
            ├── app.py          # Factory + ProxyFix + CSRF + Babel
            ├── config.py       # STATION_CODE, API_SERVER_URL, ecc.
            ├── compile_translations.py
            ├── blueprints/     # auth, maker, measure, statistics, admin
            ├── services/       # APIClient (proxy verso FastAPI con XFF)
            ├── templates/      # Jinja2 + Alpine.js
            ├── static/
            │   ├── css/        # TailwindCSS compilato
            │   └── js/         # numpad, caliper, barcode, csv-export,
            │                   #   spc-charts, annotation-editor/viewer
            ├── translations/   # Flask-Babel .po/.mo IT/EN
            └── tests/

Comandi Utili

Docker

Comando Descrizione
docker compose -f docker-compose.dev.yml up -d --build Avvia servizi in sviluppo (build incluso)
docker compose -f docker-compose.dev.yml down Ferma e rimuove i container
docker compose logs -f server Segui log server in tempo reale
docker compose logs -f client Segui log client in tempo reale
docker compose ps Stato di tutti i servizi
docker compose build --no-cache Ricostruisci immagini senza cache
docker compose exec mysql mysql -u root -p tiemeasureflow CLI MySQL

Alembic (migrations)

alembic.ini si trova in src/backend/migrations/, è richiesto il flag -c. env.py aggiunge la project root a sys.path per risolvere src.backend.*.

uv run alembic -c src/backend/migrations/alembic.ini upgrade head                              # Applica migrazioni
uv run alembic -c src/backend/migrations/alembic.ini revision --autogenerate -m "descrizione"  # Genera
uv run alembic -c src/backend/migrations/alembic.ini downgrade -1                              # Rollback ultima

Via Docker:

docker compose exec server uv run alembic -c src/backend/migrations/alembic.ini upgrade head

i18n (Traduzioni)

cd src/frontend/flask_app
uv run --project ../../.. pybabel extract -F babel.cfg -k _ -o translations/messages.pot .   # Estrai
uv run --project ../../.. pybabel update -i translations/messages.pot -d translations         # Aggiorna
uv run --project ../../.. pybabel compile -d translations                                     # Compila .po → .mo

Gestione dipendenze (uv)

uv add <pacchetto>                           # core (entrambi)
uv add --optional server <pacchetto>         # solo backend
uv add --optional client <pacchetto>         # solo frontend
uv add --optional dev <pacchetto>            # solo dev/test
uv sync --extra server --extra client --extra dev  # reinstalla
uv lock                                      # rigenera uv.lock

Testing

Il backend usa SQLite in-memory tramite aiosqlite: i test girano senza MySQL installato.

# Tutti i test (backend + frontend)
uv run pytest

# Solo backend
uv run pytest src/backend/tests/
uv run pytest src/backend/tests/test_auth.py
uv run pytest src/backend/tests/test_auth.py::test_login_success
uv run pytest --cov src/backend

# Solo frontend
uv run pytest src/frontend/flask_app/tests/

Stato corrente: 183 pass, 0 fail (vedi docs/architecture/STATO_PROGETTO.md).


Variabili d'Ambiente

Copia .env.example in .env e configura:

Variabile Descrizione
DB_HOST, DB_PORT, DB_NAME Connessione MySQL
DB_USER, DB_PASSWORD Credenziali utente MySQL
DB_ROOT_PASSWORD Password root MySQL (solo Docker)
SERVER_SECRET_KEY Chiave segreta FastAPI
SERVER_CORS_ORIGINS Origini CORS ammesse
CLIENT_SECRET_KEY Chiave segreta Flask (sessioni, CSRF)
API_SERVER_URL URL del backend visto dal client (es. http://server:8000)
STATION_CODE Per-tablet — codice stazione (es. ST-001). Senza, il client mostra errore configurazione.
UPLOAD_DIR Percorso upload file (default: uploads, project root)
MAX_UPLOAD_SIZE_MB Limite dimensione upload (default 50)
RATE_LIMIT_LOGIN Login req/min/IP (default 5)
RATE_LIMIT_GENERAL Richieste req/min/IP (default 300, per-tablet)
NGINX_PORT, NGINX_SSL_PORT Porte Nginx (solo compose dev)
SETUP_PASSWORD Password pagina setup (vuota = endpoint disabilitato)
SSL_CERTFILE, SSL_KEYFILE Certificato SSL (solo setup manuale)

Documentazione

Indice completo: docs/README.md.

Stato e direzione

Documento Contenuto
docs/architecture/STATO_PROGETTO.md Snapshot V2.0.0: cosa funziona oggi, test status, decisioni architetturali
docs/architecture/ROADMAP.md Cosa resta da fare (Fasi 2-7 rev04, decisioni cliente aperte, stime)

Riferimenti operativi

Documento Contenuto
docs/API.md Riferimento completo API REST (endpoint, parametri, schemi)
docs/DEPLOYMENT.md Guida deployment VPS: Docker, Traefik, SSL, DNS, firewall
docs/USER_GUIDE.md Manuale utente per ruolo (Maker, MeasurementTec, Metrologist)
docs/I18N_SETUP.md Setup e workflow traduzioni (Flask-Babel + Alpine.js)

Piani dettagliati

Documento Contenuto
docs/superpowers/plans/2026-04-17-rev04-master-roadmap.md Master plan rev04 (M1 + M2, decisioni aperte, stime)
docs/superpowers/plans/2026-04-17-rev04-phase1-stations.md Piano TDD Fase 1 stazioni (completato)

Licenza

Proprietary — Tielogic. All rights reserved.

Questo software è di proprietà esclusiva di Tielogic ed è protetto dalle leggi sul copyright. Non è consentita la distribuzione, modifica o utilizzo senza autorizzazione scritta.

S
Description
No description provided
Readme 5.4 MiB
Languages
HTML 48.3%
Python 40.6%
JavaScript 9.7%
CSS 1.2%
Dockerfile 0.1%