e4eb4cd932
STATO_PROGETTO.md - Bumped snapshot date to 2026-04-27. - Added "Hardening post-restructure (smoke test 2026-04-26)" section recording the four runtime regressions surfaced by the local smoke test (env_file path, missing Station import, UPLOAD_DIR default, apostrophe-in-translation in Alpine), plus the recipe-assignment modal UX rework and the new node-based JS syntax test guard. - Added "Smoke test status" section listing the verified end-to-end flow (MySQL up, alembic, seed, login, admin pages, MeasurementTec workflow, hot reload). - Bumped frontend test count 44 → 46 to reflect test_template_js_syntax.py. ROADMAP.md - Added a tech-debt entry for the user-reported task_complete riepilogo rendering anomaly (still under investigation: the curl fetch returns the table populated, but the user reports an empty body in the browser). - Added a tech-debt entry for the still-pending Docker container smoke test of the new uv-based Dockerfiles. src/frontend/flask_app/templates/measure/task_complete.html - Replaced sort(attribute='task_info.order_index,subtask.marker_number') with two chained stable sorts. Jinja's sort filter does not accept a comma-separated multi-attribute string; the previous form sorted on a non-existent attribute and only worked by accident because the API already returned rows in the desired order. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
146 lines
9.4 KiB
Markdown
146 lines
9.4 KiB
Markdown
# 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 `<script>`. Riscritti con delimitatori `"..."` o riformulazione testuale.
|
||
|
||
Inoltre **UX rework** della modale assegnazione ricette su `/admin/stations`: dropdown sostituita da layout a 2 colonne (disponibili / assegnate) con bottone inline `+ Assegna`, search filter, empty state esplicativo (mostrava silenziosamente lista vuota se tutte le ricette erano già assegnate).
|
||
|
||
Test guard aggiunto: `test_template_js_syntax.py` valida ogni inline `<script>` E ogni espressione Alpine (`x-*`, `@*`, `:*`) della pagina con `node --check`. Cattura automaticamente il bug-class apostrofo. Skip se Node non è installato.
|
||
|
||
## Layout repository (V2.0.0)
|
||
|
||
```
|
||
TieMeasureFlow/
|
||
├── pyproject.toml + uv.lock + .python-version
|
||
├── Dockerfile (backend) + Dockerfile.frontend
|
||
├── docker-compose.dev.yml + docker-compose.yml
|
||
├── nginx/
|
||
├── uploads/ # volume Docker
|
||
├── docs/ # raggruppata e indicizzata
|
||
│ ├── README.md (indice)
|
||
│ ├── API.md / DEPLOYMENT.md / USER_GUIDE.md / I18N_SETUP.md
|
||
│ ├── architecture/ # questo file + ROADMAP.md
|
||
│ ├── archive/ # piani storici
|
||
│ ├── specs/ # spec esterne (.docx)
|
||
│ └── superpowers/plans/ # piani TDD dettagliati
|
||
└── src/
|
||
├── backend/
|
||
│ ├── main.py / config.py / database.py
|
||
│ ├── api/{routers,middleware}/
|
||
│ ├── models/{orm,api}/
|
||
│ ├── services/
|
||
│ ├── migrations/
|
||
│ ├── templates/
|
||
│ └── tests/
|
||
└── frontend/
|
||
└── flask_app/
|
||
├── app.py / config.py / compile_translations.py
|
||
├── blueprints/ (auth, maker, measure, statistics, admin)
|
||
├── services/ (api_client.py)
|
||
├── templates/ + static/ + translations/
|
||
└── tests/
|
||
```
|
||
|
||
## Smoke test status
|
||
|
||
Validazione end-to-end in locale (2026-04-26):
|
||
|
||
- ✅ MySQL container Docker up, schema creato, alembic stamp head OK
|
||
- ✅ uvicorn `--reload` su :8000, `/api/health` risponde
|
||
- ✅ Seed `/api/setup/seed` con `SETUP_PASSWORD=adriano77` → admin + 4 utenti demo + DEMO-001 + ST-DEFAULT con assegnazione automatica
|
||
- ✅ Login `admin/admin123` via web, sessione persistente
|
||
- ✅ `/admin/stations`: tabella, modal create/edit, modal gestione assegnazioni a 2 colonne con search, eliminazione con cascade
|
||
- ✅ `/admin/users`, `/maker/recipes`, `/measure/select` (filtrato per stazione), `/statistics/dashboard`
|
||
- ✅ Workflow MeasurementTec end-to-end: select_recipe → task_list → task_execute → task_complete (riepilogo con misure)
|
||
- ✅ Hot reload Flask + uvicorn `--reload` + Tailwind watch attivi durante lo sviluppo
|
||
|
||
## Test status
|
||
| Backend (`src/backend/tests/`) | 127 | 3 | Fail pre-esistenti: `test_recipes` (2) + `test_tasks` (1). Nessuno introdotto dalla V2.0.0. |
|
||
| Frontend (`src/frontend/flask_app/tests/`) | 46 | 1 | +2 test post-restructure (`test_template_js_syntax.py`). Fail pre-esistente: `test_save_measurement_proxy`. |
|
||
| **Totale** | **173** | **4** | Tutti i fallimenti tracciati come tech debt da risolvere. |
|
||
|
||
## Stack confermato
|
||
|
||
- **Backend:** FastAPI + SQLAlchemy 2.0 async + MySQL 8 + Alembic + Pydantic v2 + WeasyPrint + Plotly/Kaleido.
|
||
- **Frontend:** Flask + Jinja2 + Alpine.js + TailwindCSS + Fabric.js 5.3.1 + html5-qrcode + Plotly.js + Flask-Babel.
|
||
- **Deploy:** Docker Compose. Dev = Nginx; Prod = Traefik + Let's Encrypt SSL.
|
||
- **Tooling:** uv (package mgmt), pytest + pytest-asyncio + httpx + aiosqlite (test).
|
||
|
||
## Decisioni architetturali rilevanti
|
||
|
||
| Decisione | Stato | Note |
|
||
|---|---|---|
|
||
| Frontend Flask invece di React (deroga vs spec §8) | **Confermata** | Tablet UX server-side, USB calipers/barcode, Fabric.js editor, i18n Babel collaudato. Vedi conversazione 2026-04-25. |
|
||
| NATS messaging (spec §7) | **Skippato** | Monorepo single-host, no microservizi. Nessuno stub `nats_client/` creato. |
|
||
| Envelope risposta `{success,data,error}` (spec §6) | **Rimandato** | Costo 4-5gg refactor + rotture client. Eventuale v2 API in M2. |
|
||
| Header `X-API-Key` vs spec `X-Api-Key` | **Mantenuto attuale** | Rinominare costa 50+ punti di codice + breaking per deploy. Rivedere in M2. |
|
||
| Variabili `.env` (DB_HOST, SERVER_PORT, ...) | **Mantenute attuali** | Rename a SERVICE_NAME/SERVICE_DOMAIN/API_KEY rinviato (impatta deploy esistenti). |
|
||
|
||
## Branch git
|
||
|
||
- **Default:** `V2.0.0` (lavoro corrente)
|
||
- **Mantenuti:** `V1.0.0` … `V1.0.7` (release branches storiche)
|
||
- **Mergiato e chiuso:** `feature/rev04-phase1-stations` (in `V2.0.0` con commit `ea8e468`)
|