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>
9.4 KiB
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_PASSWORDper 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 migration002_add_stations.py). - Modelli ORM:
Station,StationRecipeAssignmentcon vincolo unique(station_id, recipe_id). - Schemas Pydantic:
StationCreate/Update/Response,StationRecipeAssignmentCreate/Response,RecipeSummary. - Service
station_service(CRUD + assegnazioni + cascade delete). - Router
/api/stationscon CRUD admin + endpoint operatoreGET /api/stations/by-code/{code}/recipes. - Seed automatico
ST-DEFAULTcon tutte le ricette esistenti (idempotente). - Variabile env client
STATION_CODEletta daConfig, helperAPIClient.get_station_recipes(). - Filtro
select_recipe: il client mostra solo le ricette assegnate alla propria stazione, errore seSTATION_CODEnon 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. APIClientpropagaX-Forwarded-For+X-Real-IP(sia JSON che multipart).- 12 test aggiuntivi (7 server + 5 client).
Aggiunte V2.0.0 (struttura monorepo)
pyproject.tomlunico con extraserver/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.frontendriscritti conuv sync --frozen --no-dev --extra server|client.docker-compose.{dev,}.ymlcon build context..- Alembic env.py aggiunge project root a
sys.path;script_location = %(here)sresta valido. .dockerignoreaggiornato.
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_fileera cwd-relative (../../.env). Rotto fuori dasrc/backend/. Risolto con percorso assolutoPath(__file__).resolve().parents[2] / ".env".src/backend/models/orm/__init__.py:StationeStationRecipeAssignmentnon erano esportati, quindiBase.metadata.create_allnon creava le tabelle stations. Aggiunti agli import..env.example:UPLOAD_DIR=server/uploadsera residuo della vecchia struttura → file landavano fuori dall'albero di progetto. Aggiornato aUPLOAD_DIR=uploads.- Apostrofi italiani in template Alpine (
l'utente,nell'eliminazione,nell'assegnazione): chiudevano prematuramente JS string literals dentrox-texte 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
--reloadsu :8000,/api/healthrisponde - ✅ Seed
/api/setup/seedconSETUP_PASSWORD=adriano77→ admin + 4 utenti demo + DEMO-001 + ST-DEFAULT con assegnazione automatica - ✅ Login
admin/admin123via 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(inV2.0.0con commitea8e468)