docs: porta stato e roadmap ai fatti del 28/07
Erano fermi allo snapshot V2.0.0 di fine aprile: parlavano di Fasi rev04 da iniziare, di quattro test rotti e di uno stack che scaricava le librerie da CDN. Niente di tutto questo è più vero, e un documento di stato sbagliato è peggio di un documento di stato assente — qualcuno ci si fida. STATO_PROGETTO: i quindici punti con il loro stato e dove vivono nel codice, cosa è entrato in V3.0.0 raggruppato per tema, le dieci migrazioni, la suite a 360 pass. In fondo la sezione che conta di più, «cosa non è stato provato»: tredici punti sono in esercizio e nessuno li ha percorsi su un tablet, il punto 14 non è mai stato riprodotto su un dispositivo, il punto 12 non è mai stato provato a rete staccata. Serve a non confondere «i test passano» con «funziona in reparto». ROADMAP: riscritta intorno a quello che resta e in che ordine — il collaudo prima di tutto, perché è l'unico lavoro che non aspetta risposte da nessuno, con la tabella di cosa guardare e come si vede che è giusto. Poi il punto 4 (due o tre giorni, a decisioni chiuse), l'innesto GAIA e l'installazione di settembre. Le decisioni aperte sono ora le D-1…D-9 del documento del 28/07, non più le D-0.x di aprile: la corrispondenza è scritta, così chi torna sui vecchi documenti non si perde. Stessa cosa per le sette Fasi rev04, con dove è finita ciascuna: due assorbite, una sostituita, una ridimensionata dal fatto che una rete isolata rende l'aggiornamento automatico privo di senso. Lo snapshot V2.0.0 è conservato in docs/archive/ invece di essere sovrascritto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+126
-75
@@ -1,95 +1,146 @@
|
||||
# Roadmap TieMeasureFlow — V2.0.0 → V1.1.0 (rev04 / M1 demo cliente)
|
||||
# Roadmap TieMeasureFlow — V3.0.0 → installazione a Tràfilo
|
||||
|
||||
> Aggiornare ad ogni Fase chiusa.
|
||||
> Aggiornata al 2026-07-28. Aggiornare a ogni punto chiuso.
|
||||
|
||||
## Riferimenti
|
||||
## Dove siamo
|
||||
|
||||
- Master plan dettagliato: [`../superpowers/plans/2026-04-17-rev04-master-roadmap.md`](../superpowers/plans/2026-04-17-rev04-master-roadmap.md)
|
||||
- Spec sorgente: [`../specs/2026-04-16-schema-sviluppo-rev04.docx`](../specs/2026-04-16-schema-sviluppo-rev04.docx)
|
||||
- Stato corrente: [`STATO_PROGETTO.md`](STATO_PROGETTO.md)
|
||||
La roadmap rev04 di aprile (Fasi 1-7, milestone M1/M2) è **superata dai fatti**: il
|
||||
sopralluogo del 28/07 ha prodotto un elenco di quindici punti concreti che è oggi il
|
||||
piano di lavoro. Buona parte delle vecchie Fasi 2 e 4 è dentro quei punti ed è già
|
||||
fatta; il resto è confluito o decaduto. La mappatura è in fondo, per non perdere il
|
||||
filo con i documenti di aprile.
|
||||
|
||||
## Strategia: due milestone
|
||||
- Piano corrente: [`../../TieMeasureFlow_modifiche_2026-07-28.md`](../../TieMeasureFlow_modifiche_2026-07-28.md)
|
||||
- Stato di dettaglio: [`STATO_PROGETTO.md`](STATO_PROGETTO.md)
|
||||
- Piano rev04 (storico): [`../superpowers/plans/2026-04-17-rev04-master-roadmap.md`](../superpowers/plans/2026-04-17-rev04-master-roadmap.md)
|
||||
|
||||
| Milestone | Scope | Obiettivo |
|
||||
**Scadenza che comanda tutto: l'installazione on-premise a Tràfilo è prevista per
|
||||
settembre 2026.**
|
||||
|
||||
## I quindici punti
|
||||
|
||||
| # | Punto | Stato |
|
||||
|---|---|---|
|
||||
| **M1 — Demo cliente** | Fasi 1-5 + deploy "demo" | Sistema testabile end-to-end col cliente per raccogliere feedback |
|
||||
| **M2 — Produzione** | Fasi 6-7 + correzioni post-feedback + GAIA live | Rollout su tablet/PC reali |
|
||||
| 1 | Memoria della produzione in corso | ✅ fatto |
|
||||
| 2 | Tipo di task esplicito | ✅ fatto |
|
||||
| 3 | Loop di misura e ripetizione | ✅ fatto |
|
||||
| 4 | Limite di tentativi prima del capoturno | ⛔ **fermo** su D-6, D-7, D-9 |
|
||||
| 5 | Avanzamento solo se in tolleranza | ✅ fatto |
|
||||
| 6 | Fermo linea e Fine produzione con effetto | ✅ fatto (innesto GAIA fermo su D-1, D-2) |
|
||||
| 7 | Gestione delle stazioni | ✅ fatto |
|
||||
| 8 | Tracciabilità obbligatoria | ✅ fatto |
|
||||
| 9 | Blocco dell'inserimento manuale | ✅ fatto |
|
||||
| 10 | Interfaccia operatore: sequenza e conferme | ✅ fatto |
|
||||
| 11 | Formattazione delle descrizioni | ✅ fatto |
|
||||
| 12 | Funzionamento senza internet | ✅ fatto |
|
||||
| 13 | Generazione task dalla scheda tecnica con l'AI | — fuori offerta (quotazione su D-8) |
|
||||
| 14 | Stabilità del layout | ✅ fatto |
|
||||
| 15 | Statistica separata dalla registrazione | ✅ fatto |
|
||||
|
||||
## Stato Fasi (M1)
|
||||
## Cosa resta, in ordine
|
||||
|
||||
| Fase | Scope | Stato | Branch / Commit |
|
||||
### 1. Collaudo sul campo — *da fare adesso, non serve nessuna risposta*
|
||||
|
||||
È il lavoro più urgente rimasto, ed è l'unico che non dipende da nessuno. Tredici
|
||||
punti sono in esercizio e **nessuno li ha percorsi su un tablet**.
|
||||
|
||||
Le ricette `COLLAUDO-A` e `COLLAUDO-B` sono già sul sistema, con un utente
|
||||
`capoturno` (`scripts/seed_collaudo.py`). Da verificare, in quest'ordine:
|
||||
|
||||
| Cosa | Come si vede che è giusto |
|
||||
|---|---|
|
||||
| Ciclo di misura (3) | Il conto alla rovescia sopravvive al cambio task, prosegue in rosso oltre lo zero, riporta alla misura |
|
||||
| Fuori tolleranza (5) | Con `12.00` sulla prima quota non si passa alla seconda finché il capoturno non autorizza, o finché non si rimisura dentro |
|
||||
| Tracciabilità (8) | Su `COLLAUDO-B` l'avvio non parte finché lotto e seriale non ci sono |
|
||||
| Inserimento manuale (9) | Su `COLLAUDO-B` il tastierino numerico non c'è affatto |
|
||||
| Sequenza (10) | La ricetta si apre sul primo task; un task lasciato a metà appare «Incompiuto 1/2» nella lista |
|
||||
| Senza rete (12) | Staccare la rete e percorrere login → ricetta → task → annotazione → statistiche → report, console aperta |
|
||||
| Layout (14) | **Da un monitor da scrivania**, non solo dal tablet: è lì che il difetto si vedeva |
|
||||
|
||||
Il giro del punto 12 va fatto **ora**, perché la Content-Security-Policy è appena
|
||||
entrata in vigore: se una libreria avesse bisogno di un permesso non concesso, il
|
||||
sintomo compare adesso.
|
||||
|
||||
### 2. Punto 4 — appena arrivano le risposte
|
||||
|
||||
Numero di tentativi prima del capoturno. Serve sapere **D-7** (quanti, e se uguali
|
||||
per tutte le ricette), **D-6** (password, PIN o badge) e **D-9** (se cambiare i
|
||||
parametri crea una nuova versione). Il resto dell'impianto è pronto: il blocco per
|
||||
fuori tolleranza e l'autorizzazione del capoturno esistono già, manca il contatore.
|
||||
|
||||
Stima, a decisioni chiuse: **2-3 giorni**.
|
||||
|
||||
### 3. Innesto verso GAIA — fermo su D-1 e D-2
|
||||
|
||||
Avvio produzione, fermo linea e fine produzione hanno già lo stato e gli eventi; la
|
||||
fine produzione emette già il file di statistica. Manca solo il canale verso il
|
||||
gestionale, e non si può nemmeno disegnare finché non si sa **come** si parla con
|
||||
GAIA (D-1) e **da dove** (D-2).
|
||||
|
||||
Stima, a protocollo noto: **1-2 settimane**, molto dipendente dalla risposta.
|
||||
|
||||
### 4. Installazione a Tràfilo — settembre
|
||||
|
||||
| Cosa | Blocco |
|
||||
|---|---|
|
||||
| Macchina server, spazio disco | D-4 |
|
||||
| Immagini Docker portate in reparto, non repository da compilare sul posto | — |
|
||||
| Colonnina luminosa, se serve | D-5 |
|
||||
| Validazione rete e macchine con l'IT | D-4 |
|
||||
|
||||
Nota: **la costruzione delle immagini richiede rete** (npm, apt, uv); è l'esecuzione
|
||||
a non richiederla. In reparto va portata l'immagine già costruita.
|
||||
|
||||
## Decisioni in attesa del cliente
|
||||
|
||||
Da girare a Tràfilo tramite Menoncin. Le prime due e la D-4 hanno l'orizzonte di
|
||||
settembre; D-6 e D-7 bloccano lavoro che sappiamo già fare.
|
||||
|
||||
| ID | Decisione | Blocca | Chi risponde |
|
||||
|---|---|---|---|
|
||||
| **1** | Stazioni + identità per-tablet | ✅ **COMPLETATA** | `V2.0.0` (merge `ea8e468` da `feature/rev04-phase1-stations`) |
|
||||
| 2 | Ruolo Capoturno (Supervisor) + override token breve | ⏳ Da iniziare | — |
|
||||
| 3 | Editor ricetta a blocchi (preparation + measurement) | ⏳ Da iniziare | — |
|
||||
| 4 | Workflow operatore (retry/timer/autologout/avvio produzione) | ⏳ Da iniziare | — |
|
||||
| 5 (M1) | `ImportOnlyGaiaClient` + UI import dati cliente reali | ⏳ Da iniziare | — |
|
||||
| Deploy M1 | VPS demo (compose + Traefik + LE, no registry) | ⏳ Da iniziare | — |
|
||||
| **D-1** | Protocollo del gestionale GAIA: servizi web, database condiviso, file? | innesto GAIA (punto 6) | IT Tràfilo + fornitore GAIA |
|
||||
| **D-2** | Rete e credenziali per raggiungere GAIA | come sopra | IT Tràfilo |
|
||||
| ~~D-3~~ | ~~Una app per stazione o una sola?~~ **Decisa il 28/07**: dati sul server, app di stazione su ogni PC | — | chiusa; validazione in D-4 |
|
||||
| **D-4** | Server: quale macchina, quanto spazio disco (database **e** disegni) | installazione | IT Tràfilo |
|
||||
| **D-5** | Cicalino: basta il suono del browser o serve una colonnina luminosa? | punto 3 (completamento) | Tràfilo |
|
||||
| **D-6** | Autorizzazione capoturno: password come oggi, o PIN / badge? Venti volte al giorno la password è attrito | punti 4, 5, 6 | Tràfilo |
|
||||
| **D-7** | Quanti tentativi prima del capoturno, e uguali per tutte le ricette? | punto 4 | Tràfilo |
|
||||
| **D-8** | Schede tecniche: quante, formato standard, conversione una-tantum o funzione permanente? | punto 13 e la sua quotazione | Tràfilo |
|
||||
| **D-9** | Cambiare i parametri di una ricetta (timer, tentativi) crea una nuova versione? | punti 3, 4 | noi, con conferma cliente |
|
||||
|
||||
## Stato Fasi (M2)
|
||||
Una decisione già presa che va **confermata**: la migrazione 009 ha impostato
|
||||
`allow_manual_input = 1` su tutte le ricette esistenti, per conservare il
|
||||
comportamento in essere invece di irrigidire di colpo ricette già in uso. Va deciso
|
||||
ricetta per ricetta quali devono passare a solo calibro.
|
||||
|
||||
| Fase | Scope | Stato |
|
||||
|---|---|---|
|
||||
| 5 (M2) | GAIA reale (protocollo TBD, polling, comandi produzione) | ⏳ Bloccata da decisioni cliente (D-0.1, D-0.2) |
|
||||
| 6 | Deploy B industriale (registry privato + Watchtower + STATION_ID per-tablet + CI release) | ⏳ Pianificata |
|
||||
| 7 | Hardening, security review, E2E sito pilota, docs aggiornati, i18n delta | ⏳ Pianificata |
|
||||
|
||||
## Decisioni aperte (bloccanti per M2 / future fasi)
|
||||
|
||||
Da: master plan §0 "Precondizioni e Decisioni Aperte". Da risolvere col cliente prima della Fase 5/6.
|
||||
|
||||
| ID | Decisione | Stato | Bloccante per |
|
||||
|---|---|---|---|
|
||||
| D-0.1 | Protocollo integrazione GAIA (REST / DB shared / OPC-UA / file) | **Aperta** | Fase 5 reale (M2) |
|
||||
| D-0.2 | Credenziali e rete GAIA (VPN / firewall / whitelist IP) | **Aperta** | Fase 5 reale (M2) |
|
||||
| D-0.3 | Target hardware "tablet" (Windows / Linux industriale / Android) | **Aperta** | Fase 6 (deploy B) |
|
||||
| D-0.4 | Cicalino/luce avviso (audio HTML5 / hardware USB / entrambi) | **Rimandata a M2** | Fase 4 finale |
|
||||
| D-0.5 | Parametri runtime modificabili vs versione immutabile | **Aperta** (raccomandato B: separare volatili) | Fase 3 |
|
||||
| D-0.6 | Auth capoturno durante override (modale / PIN / RFID) | **Aperta** | Fase 2 |
|
||||
| D-0.7 | Timeout auto-logout | **Risolta** | — |
|
||||
| D-0.8 | Naming ruolo capoturno | **Proposta:** `Supervisor` | Fase 2 |
|
||||
| D-0.9 | Tag versione immagine docker | **Proposta:** SemVer + `latest` | Fase 6 |
|
||||
| D-0.10 | Registry esposto su Internet o solo VPN | **Proposta:** solo VPN cliente | Fase 6 |
|
||||
|
||||
## Tech debt da chiudere
|
||||
## Tech debt
|
||||
|
||||
| Item | Priorità | Note |
|
||||
|---|---|---|
|
||||
| 3 test backend pre-esistenti rotti (`test_recipes`, `test_tasks`) | Media | Investigare prima di Fase 3 (toccano recipe + task router). |
|
||||
| 1 test client pre-esistente rotto (`test_save_measurement_proxy`) | Bassa | Probabilmente CSRF/payload. Risolvere con Fase 4. |
|
||||
| Pagina `task_complete` riepilogo: utente segnala riga vuota in alcuni scenari | Media | Da debuggare (rendering corretto via curl ma utente vede vuoto in browser, possibile interazione con sessione lot/serial). |
|
||||
| `.env` rename a convenzione spec (SERVICE_NAME, SERVICE_DOMAIN, API_KEY) | Bassa | Rinviato (impatto deploy). |
|
||||
| Header `X-API-Key` rename a `X-Api-Key` | Bassa | Vedere se M2 lo richiede. |
|
||||
| Envelope risposta `{success,data,error}` | Bassa | Eventuale API v2 in M2. |
|
||||
| `Dockerfile.frontend`: `pybabel compile` via `uv run` non testato in build reale | Alta | Verificare al primo `docker compose build`. |
|
||||
| Smoke test in container Docker (non solo locale uvicorn+gunicorn) | Alta | Validare che i Dockerfile riscritti con `uv` buildino e girino correttamente prima di chiudere V2.0.0. |
|
||||
| `components/barcode_scanner.html` dipende da `html5-qrcode`, mai caricata | Media | Codice morto: il componente non è incluso da nessuna parte e il lettore che l'operatore usa è un campo di testo. Da rimuovere, o da completare portando la libreria in `static/vendor/` |
|
||||
| Pagina `task_complete`: riga vuota segnalata in alcuni scenari | Media | Segnalazione di aprile mai riprodotta. Da verificare durante il collaudo, ora che il flusso è cambiato |
|
||||
| `.env` rename a convenzione spec (SERVICE_NAME, SERVICE_DOMAIN, API_KEY) | Bassa | Rinviato: impatta i deploy esistenti |
|
||||
| Header `X-API-Key` → `X-Api-Key` | Bassa | Breaking per i deploy esistenti |
|
||||
| Envelope risposta `{success,data,error}` | Bassa | Eventuale API v2 |
|
||||
| Test di carico a 20 tablet reali | Bassa | La capacità è dimensionata ma mai misurata sotto carico vero |
|
||||
|
||||
## Open per scelta utente prima della prossima sessione
|
||||
I quattro test rotti tracciati nello snapshot V2.0.0 non risultano più: la suite è a
|
||||
360 pass, 0 fail.
|
||||
|
||||
1. **Quale fase iniziare adesso?** Opzioni: Fase 2 (Supervisor — sblocca workflow), Fase 3 (block editor — independente), Fase 5 (import GAIA dati reali — sblocca demo).
|
||||
2. **Revisione decisioni aperte col cliente** — D-0.1 / D-0.2 / D-0.3 / D-0.6 prima di pianificare Fase 5 e 6.
|
||||
3. **Smoke test Docker** della nuova struttura V2.0.0 (`docker compose -f docker-compose.dev.yml up --build`) per validare i Dockerfile riscritti.
|
||||
4. **Test di carico** (k6/locust) a 20 VU su `/measure/save-measurement` per validare la scalatura worker (capacità annunciata: 20-30 tablet contemporanei).
|
||||
## Mappatura con la roadmap rev04 di aprile
|
||||
|
||||
## Stima tempi residui M1 (post-Fase 1)
|
||||
Per chi torna sui documenti di aprile e non ritrova le Fasi.
|
||||
|
||||
| Task | Stima full-time |
|
||||
| Fase rev04 | Che fine ha fatto |
|
||||
|---|---|
|
||||
| Fase 2 — Supervisor + override | 1 settimana |
|
||||
| Fase 3 — Block editor | 1.5 settimane |
|
||||
| Fase 4 — Workflow operatore | 2 settimane |
|
||||
| Fase 5 (M1) — Import-only GAIA | 1 settimana |
|
||||
| Deploy M1 demo | 0.5 settimane |
|
||||
| **Totale M1 residuo** | **~6 settimane** |
|
||||
| 1 — Stazioni per-tablet | Chiusa in V2.0.0, ampliata dal punto 7 |
|
||||
| 2 — Ruolo Capoturno + override | Assorbita dai punti 5 e 6: il ruolo `Supervisor` esiste e l'autorizzazione resta scritta sulla misura. L'override a token breve dipende da D-6 |
|
||||
| 3 — Editor ricetta a blocchi | Sostituita dal punto 2 (tipo di task dichiarato), che risolve il problema vero senza riscrivere l'editor |
|
||||
| 4 — Workflow operatore | Assorbita dai punti 3, 4, 10: timer e sequenza sono fatti, i tentativi sono il punto 4 |
|
||||
| 5 — Import GAIA | Diventata l'innesto del punto 6, ferma su D-1 e D-2 |
|
||||
| 6 — Deploy industriale (registry + Watchtower) | Ridimensionata: con una rete isolata l'aggiornamento automatico non ha senso. Restano immagini versionate portate a mano |
|
||||
| 7 — Hardening | Parzialmente assorbita: CSP, versioni congelate e impronte sono entrate col punto 12 |
|
||||
|
||||
## Stima tempi M2 (dopo feedback)
|
||||
|
||||
| Task | Stima |
|
||||
|---|---|
|
||||
| Aggiustamenti post-feedback | variabile (1-2 sett.) |
|
||||
| Fase 5 reale GAIA | 1-2 settimane |
|
||||
| Fase 6 deploy B | 1 settimana |
|
||||
| Fase 7 hardening | 1-2 settimane |
|
||||
| **Totale M2** | **~4-7 settimane** |
|
||||
|
||||
**Totale fino a produzione:** ~10-13 settimane full-time da oggi (2026-04-25), assumendo decisioni aperte risolte in tempo utile.
|
||||
Le vecchie decisioni `D-0.x` sono confluite nelle `D-x` qui sopra: D-0.1→D-1,
|
||||
D-0.2→D-2, D-0.4→D-5, D-0.5→D-9, D-0.6→D-6. D-0.8 (nome del ruolo capoturno) è
|
||||
chiusa: si chiama `Supervisor`. D-0.7 (auto-logout) era già risolta.
|
||||
|
||||
+157
-110
@@ -1,145 +1,192 @@
|
||||
# Stato Progetto TieMeasureFlow — V2.0.0
|
||||
# Stato Progetto TieMeasureFlow — V3.0.0
|
||||
|
||||
> Snapshot al 2026-04-27. Aggiornare ad ogni milestone.
|
||||
> Snapshot al 2026-07-28. Aggiornare ad ogni milestone.
|
||||
> Lo snapshot V2.0.0 è in [`../archive/STATO_PROGETTO_V2.0.0_2026-04-27.md`](../archive/STATO_PROGETTO_V2.0.0_2026-04-27.md).
|
||||
|
||||
## Versione corrente
|
||||
|
||||
**V2.0.0** (in sviluppo, branch `V2.0.0` come default su `git.tielogic.xyz`).
|
||||
**V3.0.0** (in sviluppo, branch `V3.0.0`). Versione precedente: `V2.0.0`.
|
||||
|
||||
Versione precedente di produzione: `V1.0.7`.
|
||||
In esercizio su `tieflow.tielogic.xyz` (Docker Compose + Traefik + Let's Encrypt),
|
||||
schema alla migrazione `010_meas_authorisation`.
|
||||
|
||||
## 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/`).
|
||||
V2.0.0 aveva chiuso stazioni per-tablet, ristrutturazione monorepo con `uv` e la
|
||||
tenuta multi-tablet. **V3.0.0 nasce dal sopralluogo del 28/07 e lavora sui quindici
|
||||
punti** raccolti in [`TieMeasureFlow_modifiche_2026-07-28.md`](../../TieMeasureFlow_modifiche_2026-07-28.md).
|
||||
|
||||
## Cosa funziona oggi (V2.0.0 — branch corrente)
|
||||
Il filo che li tiene insieme: fino a V2.0.0 il sistema *registrava* misure ma non
|
||||
*governava* la produzione. Le regole vivevano nell'interfaccia — una modale che si
|
||||
poteva chiudere, un timer che moriva al cambio pagina, un tipo di task dedotto dalla
|
||||
presenza di quote. V3.0.0 le sposta sul server, dove non si aggirano.
|
||||
|
||||
### 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.
|
||||
Tredici punti su quindici sono fatti e in esercizio. Uno è fermo sulle risposte del
|
||||
cliente, uno è fuori offerta.
|
||||
|
||||
### 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.
|
||||
## I quindici punti
|
||||
|
||||
### 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).
|
||||
| # | Punto | Stato | Dove vive |
|
||||
|---|---|---|---|
|
||||
| 1 | Memoria della produzione in corso | ✅ | `production_runs`, `production_service` |
|
||||
| 2 | Tipo di task esplicito | ✅ | `RecipeTask.task_type`, migrazione 007 |
|
||||
| 3 | Loop di misura e ripetizione | ✅ | `production-clock.js`, `production_service`, migrazione 008 |
|
||||
| 4 | Limite di tentativi prima del capoturno | ⛔ **Fermo su D-6, D-7, D-9** | — |
|
||||
| 5 | Avanzamento solo se in tolleranza | ✅ | `measurement_service.pending_authorisation`, migrazione 010 |
|
||||
| 6 | Fermo linea e Fine produzione con effetto | ✅ | `production_service`, `production_export_service` |
|
||||
| 7 | Gestione delle stazioni | ✅ | `station_service`, `/admin/stations` |
|
||||
| 8 | Tracciabilità obbligatoria | ✅ | `Recipe.requires_lot/serial`, migrazione 009 |
|
||||
| 9 | Blocco dell'inserimento manuale | ✅ | `Recipe.allow_manual_input`, `numpad.js` |
|
||||
| 10 | Interfaccia operatore: sequenza e conferme | ✅ | `/measure/start`, `task-progress`, `task_list`, `task_execute` |
|
||||
| 11 | Formattazione delle descrizioni | ✅ | filtro `rich_text`, `rich-text.js` |
|
||||
| 12 | Funzionamento senza internet | ✅ | `static/vendor/`, CSP in `app.py` |
|
||||
| 13 | Generazione task dalla scheda tecnica con l'AI | — **Fuori offerta** | — |
|
||||
| 14 | Stabilità del layout | ✅ | `.tmf-page` in `themes.css`, [`LAYOUT.md`](LAYOUT.md) |
|
||||
| 15 | Statistica separata dalla registrazione | ✅ | `@role_required("Metrologist")`, test dedicato |
|
||||
|
||||
### 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.
|
||||
## Cosa è entrato in V3.0.0
|
||||
|
||||
### Hardening post-restructure (smoke test 2026-04-26)
|
||||
### Produzione come stato, non come pulsante (punti 1, 6)
|
||||
|
||||
Sequenza di smoke test in locale (uvicorn + gunicorn + MySQL Docker) ha fatto emergere quattro regressioni che sarebbero rimaste invisibili al test suite:
|
||||
- Tabelle `production_runs` e `production_events`; una produzione si apre su una
|
||||
stazione, accumula eventi (`start`, `cycle_completed`, `task_measured`,
|
||||
`remeasure`, `line_stop`, `resume`, `close`) e si chiude.
|
||||
- Fermo linea, ripresa e fine produzione richiedono le credenziali del capoturno e
|
||||
**cambiano lo stato**: prima erano modali che si chiudevano.
|
||||
- La fine produzione emette il file di statistica dell'intera produzione
|
||||
(`production_export_service`), CSV con i delimitatori configurati, marcando le
|
||||
misure come esportate. Il punto d'innesto verso GAIA è lì accanto, in attesa di
|
||||
D-1 e D-2.
|
||||
|
||||
- **`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.
|
||||
### Il ciclo di misura (punto 3)
|
||||
|
||||
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).
|
||||
- L'intervallo della ricetta è calcolato dal server (`seconds_to_next_measurement`,
|
||||
`overdue`, `server_time`): sopravvive al cambio task, che è un ricaricamento di
|
||||
pagina, e non si azzera cambiando schermata.
|
||||
- Il conteggio **prosegue oltre lo zero**: il ritardo si vede, in rosso.
|
||||
- A scadenza l'operatore viene riportato al primo task di misura della ricetta, con
|
||||
cinque secondi di preavviso; chi sta già misurando viene lasciato finire.
|
||||
- Cicalino via WebAudio, nessun file audio da caricare.
|
||||
- `remeasure` distingue una seconda lettura dello stesso pezzo da un ciclo nuovo:
|
||||
girare il pezzo e rimisurare non deve far ripartire l'intervallo.
|
||||
|
||||
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.
|
||||
### Le regole della ricetta (punti 8, 9)
|
||||
|
||||
## Layout repository (V2.0.0)
|
||||
- `requires_lot`, `requires_serial`, `allow_manual_input` per ricetta, verificati in
|
||||
`measurement_service`: **ogni porta d'ingresso passa di lì**, barcode compreso.
|
||||
- Con `allow_manual_input` a falso il tastierino non viene disegnato — non nascosto:
|
||||
il markup non esiste — e il server rifiuta comunque un valore digitato a mano.
|
||||
- Il rilevamento del calibro USB è stato reso più tollerante (Enter veloce), perché
|
||||
con la regola stretta una lettura corta come `9.5` veniva scambiata per digitazione.
|
||||
|
||||
```
|
||||
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/
|
||||
```
|
||||
### Il fuori tolleranza (punto 5)
|
||||
|
||||
## Smoke test status
|
||||
- Una quota fuori tolleranza e non autorizzata **blocca** il salvataggio della quota
|
||||
successiva e la chiusura del ciclo (409).
|
||||
- Conta l'**ultima** lettura di ogni quota: rimisurare la stessa quota resta
|
||||
possibile — il calibro scivola, il pezzo si riposiziona — e una lettura dentro i
|
||||
limiti scioglie il blocco senza chiamare nessuno.
|
||||
- L'autorizzazione del capoturno resta scritta sulla misura (`supervisor_id`,
|
||||
`authorised_at`) e finisce in due colonne del CSV di produzione: il file mostra il
|
||||
guasto **e** la decisione.
|
||||
- Quanti tentativi siano ammessi è il punto 4, non questo.
|
||||
|
||||
Validazione end-to-end in locale (2026-04-26):
|
||||
### L'interfaccia dell'operatore (punti 10, 11, 14, 15)
|
||||
|
||||
- ✅ 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
|
||||
- La ricetta si apre sul primo task (`/measure/start/<id>`), non su un elenco. La
|
||||
lista scende a secondo livello e mostra quali task sono rimasti incompiuti
|
||||
(`GET /api/measurements/task-progress`, contato per quota e non per tentativo).
|
||||
- «Fine ciclo misura» è visibile da subito e spento finché mancano quote, con
|
||||
scritto quante ne mancano.
|
||||
- Descrizioni con `**grassetto**` e a capo: marcatura, non HTML, così la
|
||||
sanificazione è per costruzione.
|
||||
- Una cornice sola per tutte le viste, allineata alla navbar — vedi [`LAYOUT.md`](LAYOUT.md).
|
||||
- Nessun percorso dell'operatore porta alla statistica, ed è un test.
|
||||
|
||||
### Fuori dalla rete (punto 12)
|
||||
|
||||
- Alpine, Plotly, PDF.js (+ worker), Fabric e i font sono nell'installazione, con
|
||||
versione nel nome e impronta SHA-256 verificata da un test.
|
||||
- Content-Security-Policy a sola origine locale servita **dal client Flask**: prima
|
||||
esisteva solo sul backend, cioè sulle risposte API e non sulle pagine.
|
||||
- Tailwind fissato a `3.4.19` nel `Dockerfile.frontend`.
|
||||
|
||||
## Migrazioni
|
||||
|
||||
| # | Contenuto |
|
||||
|---|---|
|
||||
| 001–004 | image_path, stazioni, intervallo di misura + auto-logout, `input_duration_ms` |
|
||||
| 005–006 | `production_runs`, misure legate alla produzione |
|
||||
| 007 | `task_type` dichiarato |
|
||||
| 008 | tipi di evento del ciclo + `task_id` sull'evento |
|
||||
| 009 | `requires_lot`, `requires_serial`, `allow_manual_input` |
|
||||
| 010 | `supervisor_id`, `authorised_at` sulla misura |
|
||||
|
||||
Le 008 e 010 usano il *batch mode* per compatibilità SQLite (i test) e sono state
|
||||
verificate anche in resa MySQL (`alembic upgrade X:Y --sql`) prima del deploy.
|
||||
|
||||
La 009 imposta `allow_manual_input = 1` sulle ricette esistenti: conserva il
|
||||
comportamento in essere invece di irrigidire di colpo ricette già in uso. È una
|
||||
scelta, e va confermata dal cliente ricetta per ricetta.
|
||||
|
||||
## 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
|
||||
| | Test | Fail |
|
||||
|---|---|---|
|
||||
| Backend (`src/backend/tests/`) | 212 | 0 |
|
||||
| Frontend (`src/frontend/flask_app/tests/`) | 148 | 0 |
|
||||
| **Totale** | **360** | **0** |
|
||||
|
||||
- **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).
|
||||
I quattro fallimenti pre-esistenti tracciati nello snapshot V2.0.0 non ci sono più.
|
||||
|
||||
Tre file di test non renderizzano niente e leggono i sorgenti, perché guardano
|
||||
proprietà che sopravvivono solo se qualcuno le controlla:
|
||||
|
||||
| File | Cosa impedisce |
|
||||
|---|---|
|
||||
| `test_offline.py` | Una libreria caricata dalla rete, un worker PDF.js lasciato sul CDN, una libreria sostituita senza aggiornare l'impronta |
|
||||
| `test_layout_shell.py` | Una vista che torna a dichiararsi la propria larghezza |
|
||||
| `test_template_js_syntax.py` | Una traduzione con l'apostrofo dentro una stringa JS a virgolette singole, che spegne Alpine su tutta la pagina |
|
||||
|
||||
## Cosa non è stato provato
|
||||
|
||||
Va detto perché non si confonda «i test passano» con «funziona in reparto».
|
||||
|
||||
- **Il collaudo sul campo non è mai stato percorso.** Le ricette `COLLAUDO-A` e
|
||||
`COLLAUDO-B` sono state seminate sul sistema in esercizio (`scripts/seed_collaudo.py`)
|
||||
insieme a un utente `capoturno`, ma nessuno ha ancora guidato il flusso su un
|
||||
tablet vero.
|
||||
- **Il punto 14 non è stato riprodotto su un dispositivo**: è dimostrato che il
|
||||
layout *poteva* muoversi per quattro motivi e che ora non può più, non che
|
||||
l'operatore vedesse esattamente quelli.
|
||||
- **Il punto 12 non è stato provato a rete staccata.** È dimostrato che nessuna
|
||||
risorsa esterna viene richiesta. Il giro con la rete staccata va fatto, ed è ora il
|
||||
momento giusto perché la CSP è appena entrata in vigore.
|
||||
- **Il cicalino** è il suono del browser. Se serva una colonnina luminosa è D-5.
|
||||
|
||||
## Stack
|
||||
|
||||
- **Backend:** FastAPI + SQLAlchemy 2.0 async + MySQL 8 + Alembic + Pydantic v2 +
|
||||
WeasyPrint + Plotly/Kaleido. 12 router.
|
||||
- **Frontend:** Flask + Jinja2 + Alpine.js 3.15.12 + TailwindCSS 3.4.19 +
|
||||
Fabric.js 5.3.1 + PDF.js 3.11.174 + Plotly.js 2.32.0 + Flask-Babel —
|
||||
**tutte copie locali**, nessun CDN.
|
||||
- **Deploy:** Docker Compose. Dev = Nginx; Prod = Traefik + Let's Encrypt.
|
||||
- **Tooling:** uv, pytest + pytest-asyncio + httpx + aiosqlite.
|
||||
|
||||
## 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). |
|
||||
| Le regole di misura stanno sul server, non sullo schermo | **Confermata (V3.0.0)** | Tracciabilità, inserimento manuale, fuori tolleranza: la schermata può nasconderle, il server le rifiuta. |
|
||||
| Una app di stazione per PC, dati sul server | **Decisa il 28/07** (D-3) | Validazione con l'IT di Tràfilo aperta come D-4. |
|
||||
| Descrizioni in marcatura, non in HTML | **Confermata** | Niente HTML accettato in ingresso: la sanificazione è per costruzione, non per filtro. |
|
||||
| Frontend Flask invece di React (deroga vs spec §8) | **Confermata** | Tablet UX server-side, calibri USB, editor Fabric.js, i18n Babel collaudato. |
|
||||
| NATS messaging (spec §7) | **Skippato** | Monorepo single-host, nessun microservizio. |
|
||||
| Envelope risposta `{success,data,error}` (spec §6) | **Rimandato** | Costo alto, rotture client. Eventuale API v2. |
|
||||
| Header `X-API-Key` vs spec `X-Api-Key` | **Mantenuto attuale** | Rinominare è breaking per i 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`)
|
||||
- **Corrente:** `V3.0.0`
|
||||
- **Precedenti:** `V2.0.0`, `V1.0.0` … `V1.0.7` (release storiche)
|
||||
|
||||
Reference in New Issue
Block a user