docs: rewrite CLAUDE.md with comprehensive project guidance for Claude Code
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>
This commit is contained in:
@@ -1,71 +1,149 @@
|
|||||||
# TieMeasureFlow - Istruzioni Progetto
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
## Panoramica
|
## Panoramica
|
||||||
|
|
||||||
TieMeasureFlow by Tielogic - Sistema di gestione task per misurazioni con calibro manuale.
|
TieMeasureFlow by Tielogic - Sistema di gestione task per misurazioni con calibro manuale.
|
||||||
Monorepo con server FastAPI (backend API) e client Flask (frontend tablet).
|
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.
|
||||||
|
|
||||||
## Stack
|
## Comandi di Sviluppo
|
||||||
- **Server**: FastAPI + SQLAlchemy 2.0 async + asyncmy + MySQL 8.0
|
|
||||||
- **Client**: Flask 3.0 + Jinja2 + htmx 2.0 + Alpine.js 3.x + TailwindCSS 3.x
|
|
||||||
- **Grafici**: Plotly.js (browser) + Kaleido (server export SVG)
|
|
||||||
- **PDF**: WeasyPrint (HTML → PDF), PDF.js (rendering browser)
|
|
||||||
- **Annotazioni**: Fabric.js 6.x (editor Maker), Canvas overlay (display MeasurementTec)
|
|
||||||
- **i18n**: Flask-Babel (server) + alpinejs-i18n (client), lingue IT/EN
|
|
||||||
- **Auth**: API Key nell'header X-API-Key
|
|
||||||
|
|
||||||
## Struttura
|
### 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
|
||||||
```
|
```
|
||||||
TieMeasureFlow/
|
|
||||||
├── server/ # FastAPI Backend (porta 8000)
|
### Avvio manuale (senza Docker)
|
||||||
│ ├── main.py # Entry point
|
```bash
|
||||||
│ ├── config.py # Settings da .env
|
# Server (terminale 1)
|
||||||
│ ├── database.py # SQLAlchemy async engine
|
cd server && uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
||||||
│ ├── models/ # SQLAlchemy ORM models
|
|
||||||
│ ├── schemas/ # Pydantic validation schemas
|
# Client (terminale 2)
|
||||||
│ ├── routers/ # API endpoint routers
|
cd client && flask run --host 0.0.0.0 --port 5000
|
||||||
│ ├── services/ # Business logic
|
|
||||||
│ ├── middleware/ # API key auth, logging
|
# TailwindCSS watch (terminale 3)
|
||||||
│ └── migrations/ # Alembic migrations
|
cd client && npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --watch
|
||||||
├── client/ # Flask Frontend (porta 5000)
|
|
||||||
│ ├── app.py # Entry point
|
|
||||||
│ ├── blueprints/ # Flask route blueprints
|
|
||||||
│ ├── services/ # API client wrapper
|
|
||||||
│ ├── templates/ # Jinja2 HTML templates
|
|
||||||
│ ├── static/ # CSS, JS, immagini
|
|
||||||
│ └── translations/ # Flask-Babel i18n
|
|
||||||
└── docs/ # Documentazione
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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
|
## Convenzioni Codice
|
||||||
- Python 3.11+, type hints ovunque
|
- Python 3.11+, type hints ovunque
|
||||||
- async/await per tutte le operazioni DB (server)
|
- async/await per tutte le operazioni DB (server)
|
||||||
- Pydantic v2 per validazione I/O API
|
- Pydantic v2 per validazione I/O API
|
||||||
- Nomi variabili e commenti in inglese, UI strings in IT/EN via i18n
|
- Nomi variabili e commenti in inglese, UI strings in IT/EN via i18n
|
||||||
- File imports ordinati: stdlib, third-party, local
|
- Import ordinati: stdlib → third-party → local, nessun wildcard
|
||||||
- Nessun import wildcard (from x import *)
|
- DB queries: `selectinload()` per eager loading, `scalar_one_or_none()` per single-row
|
||||||
|
|
||||||
## Database
|
## Dipendenze Chiave
|
||||||
- MySQL 8.0 con charset utf8mb4
|
- **Server**: fastapi, uvicorn, sqlalchemy[asyncio], asyncmy, alembic, pydantic, bcrypt, pillow, plotly, kaleido, weasyprint, jinja2
|
||||||
- SQLAlchemy 2.0 async con asyncmy driver
|
- **Client**: flask, flask-babel, flask-wtf, requests
|
||||||
- Alembic per migrations
|
- **Test server**: pytest, pytest-asyncio, httpx, aiosqlite (SQLite in-memory per test)
|
||||||
- Tabelle: users, recipes, recipe_versions, recipe_tasks, recipe_subtasks, measurements, access_logs, system_settings, recipe_version_audit
|
- **Test client**: pytest, coverage
|
||||||
|
|
||||||
## Ruoli Utente
|
## Configurazione
|
||||||
- **Maker**: crea/modifica ricette
|
Variabili d'ambiente in `.env` (copiare da `.env.example`):
|
||||||
- **MeasurementTec**: esegue misurazioni
|
- DB: `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`
|
||||||
- **Metrologist**: visualizza statistiche SPC
|
- Server: `SERVER_HOST`, `SERVER_PORT`, `SERVER_SECRET_KEY`, `SERVER_CORS_ORIGINS`
|
||||||
- Ruoli combinabili (JSON array), flag `is_admin` separato
|
- Client: `CLIENT_HOST`, `CLIENT_PORT`, `CLIENT_SECRET_KEY`, `API_SERVER_URL`
|
||||||
|
- Upload: `UPLOAD_DIR`, `MAX_UPLOAD_SIZE_MB`
|
||||||
## Pattern Importanti
|
- Setup: `SETUP_PASSWORD` (vuota = endpoint disabilitato)
|
||||||
- **Recipe Versioning**: copy-on-write immutabile. Modifiche creano nuova versione, misure restano legate a versione originale
|
- SSL: `SSL_CERTFILE`, `SSL_KEYFILE`
|
||||||
- **Pass/Fail**: calcolato da tolleranze UTL/UWL/LWL/LTL su ogni subtask
|
|
||||||
- **File Storage**: uploads/{images,pdfs,logos,reports}/{recipe_id}/{version_id}/
|
|
||||||
|
|
||||||
## Comandi
|
|
||||||
- Server: `cd server && uvicorn main:app --reload --host 0.0.0.0 --port 8000`
|
|
||||||
- Client: `cd client && flask run --host 0.0.0.0 --port 5000`
|
|
||||||
- Migrations: `cd server && alembic upgrade head`
|
|
||||||
- TailwindCSS: `cd client && npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --watch`
|
|
||||||
|
|
||||||
## Brand
|
## Brand
|
||||||
- Colore primario: #2563EB (blu industriale)
|
- Colore primario: #2563EB (blu industriale)
|
||||||
|
|||||||
Reference in New Issue
Block a user