# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Panoramica TieMeasureFlow by Tielogic - Sistema di gestione task per misurazioni con calibro manuale. Monorepo con **server FastAPI** (backend API, porta 8000) e **client Flask** (frontend tablet, porta 5000), orchestrati con Docker Compose + Nginx reverse proxy + MySQL 8.0. ## Comandi di Sviluppo ### Avvio servizi (Docker) ```bash docker compose up -d # Avvia tutto docker compose logs -f server # Log server docker compose logs -f client # Log client docker compose ps # Stato servizi ``` ### Avvio manuale (senza Docker) ```bash # Server (terminale 1) cd server && uvicorn main:app --reload --host 0.0.0.0 --port 8000 # Client (terminale 2) cd client && flask run --host 0.0.0.0 --port 5000 # TailwindCSS watch (terminale 3) cd client && npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --watch ``` ### Database & Migrations ```bash cd server && alembic upgrade head # Applica migrazioni cd server && alembic revision --autogenerate -m "descrizione" # Genera migrazione cd server && alembic downgrade -1 # Rollback ultima docker compose exec server alembic upgrade head # Via Docker ``` ### Test ```bash # Server (usa SQLite in-memory via aiosqlite, no MySQL richiesto) cd server && pytest # Tutti i test cd server && pytest tests/test_auth.py # Singolo modulo cd server && pytest tests/test_auth.py::test_login_success # Singolo test cd server && pytest --cov # Con copertura # Client cd client && pytest cd client && pytest tests/test_auth.py ``` ### i18n (Traduzioni) ```bash # Estrai stringhe cd client && pybabel extract -F babel.cfg -k _ -o translations/messages.pot . # Aggiorna catalogo cd client && pybabel update -i translations/messages.pot -d translations # Compila .po → .mo cd client && pybabel compile -d translations # oppure cd client && python compile_translations.py ``` ### Setup iniziale Dopo primo avvio, aprire `http://localhost/api/setup` (o `:8000/api/setup` senza Nginx) con la SETUP_PASSWORD dal `.env` per: inizializzare DB, creare admin, caricare dati demo. ## Architettura ### Flusso richieste ``` Browser/Tablet → Nginx (:80/443) → Flask Client (:5000) → APIClient → FastAPI Server (:8000) → MySQL ``` Il client Flask è un frontend server-side che comunica col backend via REST API. Ogni richiesta dal client al server include l'header `X-API-Key` per autenticazione. ### Server (FastAPI) — `server/` - **main.py**: entry point, registra middleware (RateLimit → SecurityHeaders → CORS → AccessLog) e 10 router - **config.py**: `Settings` Pydantic che legge da `.env` - **database.py**: SQLAlchemy 2.0 async engine con `AsyncSession`, pool 10+20 overflow - **routers/**: auth, recipes, tasks, measurements, statistics, reports, files, users, settings, setup - **services/**: recipe_service (versioning copy-on-write), measurement_service (calcolo pass/fail), spc_service (Cp/Cpk/control chart, puro stdlib senza numpy), report_service (WeasyPrint PDF + Kaleido SVG), auth_service (bcrypt + API key) - **middleware/**: api_key.py (auth dependency), rate_limit.py (sliding window per-IP), security_headers.py (CSP, HSTS), logging.py (audit trail async su DB) - **models/**: User, Recipe, RecipeVersion, RecipeTask, RecipeSubtask, Measurement, AccessLog, SystemSetting - **schemas/**: Pydantic v2 per validazione I/O API - **migrations/**: Alembic con alembic.ini nella directory migrations - **tests/**: pytest + pytest-asyncio, database SQLite in-memory (aiosqlite), fixture pre-popolate (admin_user, maker_user, etc.), WeasyPrint mockato ### Client (Flask) — `client/` - **app.py**: factory pattern `create_app()`, CSRF protection, Babel i18n - **blueprints/**: auth (login/logout/session), maker (editor ricette con Fabric.js), measure (esecuzione misurazioni), statistics (dashboard SPC con Plotly.js) - **services/api_client.py**: singleton `APIClient` — wrapper HTTP (get/post/put/delete) con gestione errori normalizzata, timeout 30s, header X-API-Key da session - **templates/**: Jinja2 con `{{ _('string') }}` per i18n - **static/js/**: Alpine.js components, Fabric.js editor, locales JSON (it.json, en.json) - **translations/**: Flask-Babel, cataloghi .po/.mo per IT/EN ## Pattern Critici ### Recipe Versioning (Copy-on-Write) Le ricette usano un versioning immutabile: ogni modifica crea una nuova `RecipeVersion` con deep-copy di tasks e subtasks. Le misurazioni restano sempre legate alla versione originale. La versione corrente ha `is_current=True`, le precedenti `False`. Audit trail in `recipe_version_audit` (CREATE, UPDATE, RETIRE). Logica in `server/services/recipe_service.py`. ### Calcolo Pass/Fail Ogni subtask ha 4 limiti di tolleranza: UTL (upper tolerance), UWL (upper warning), LWL (lower warning), LTL (lower tolerance) più un valore nominale. Il calcolo in `server/services/measurement_service.py`: - Fuori UTL/LTL → **fail** - Fuori UWL/LWL ma dentro UTL/LTL → **warning** - Dentro UWL/LWL → **pass** ### SPC (Statistical Process Control) Calcoli in `server/services/spc_service.py` usando solo `math` e `statistics` stdlib (no numpy/scipy): summary (conteggi pass/warning/fail), capability (Cp, Cpk, Pp, Ppk), control chart (UCL/LCL = mean ± 3σ), histogram (20 bin + curva normale). ### Autenticazione 1. Login con username/password → server ritorna `api_key` (64 char random) 2. Client salva api_key in Flask session 3. Ogni richiesta API include header `X-API-Key` 4. Middleware `get_current_user()` autentica, `require_role()` autorizza 5. Dependency pre-built: `require_maker`, `require_measurement_tec`, `require_metrologist`, `require_admin_user` ### Ruoli Utente Combinabili (JSON array): **Maker** (crea ricette), **MeasurementTec** (esegue misurazioni), **Metrologist** (dashboard SPC). Flag `is_admin` separato. ### File Storage Upload in `uploads/{recipe_id}/{version_id}/`. Tipi ammessi: JPEG, PNG, GIF, WebP, PDF. Limite 50MB. Thumbnail auto-generate per immagini (Pillow). Sanitizzazione filename. ## Convenzioni Codice - Python 3.11+, type hints ovunque - async/await per tutte le operazioni DB (server) - Pydantic v2 per validazione I/O API - Nomi variabili e commenti in inglese, UI strings in IT/EN via i18n - Import ordinati: stdlib → third-party → local, nessun wildcard - DB queries: `selectinload()` per eager loading, `scalar_one_or_none()` per single-row ## Dipendenze Chiave - **Server**: fastapi, uvicorn, sqlalchemy[asyncio], asyncmy, alembic, pydantic, bcrypt, pillow, plotly, kaleido, weasyprint, jinja2 - **Client**: flask, flask-babel, flask-wtf, requests - **Test server**: pytest, pytest-asyncio, httpx, aiosqlite (SQLite in-memory per test) - **Test client**: pytest, coverage ## Configurazione Variabili d'ambiente in `.env` (copiare da `.env.example`): - DB: `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` - Server: `SERVER_HOST`, `SERVER_PORT`, `SERVER_SECRET_KEY`, `SERVER_CORS_ORIGINS` - Client: `CLIENT_HOST`, `CLIENT_PORT`, `CLIENT_SECRET_KEY`, `API_SERVER_URL` - Upload: `UPLOAD_DIR`, `MAX_UPLOAD_SIZE_MB` - Setup: `SETUP_PASSWORD` (vuota = endpoint disabilitato) - SSL: `SSL_CERTFILE`, `SSL_KEYFILE` ## Brand - Colore primario: #2563EB (blu industriale) - Colore secondario: #64748B (grigio acciaio) - Font UI: Inter - Font numeri: JetBrains Mono