Add dev commands (test, migrations, i18n, Docker), architecture overview, critical patterns (copy-on-write versioning, pass/fail, SPC, auth flow), and dependency/configuration reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.4 KiB
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)
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)
# 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
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
# 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)
# 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:
SettingsPydantic 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
- Login con username/password → server ritorna
api_key(64 char random) - Client salva api_key in Flask session
- Ogni richiesta API include header
X-API-Key - Middleware
get_current_user()autentica,require_role()autorizza - 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