chore(docs): consolidate documentation, drop dead files
Cleanup - Remove src/backend/Dockerfile.legacy and src/frontend/flask_app/Dockerfile.legacy (history is in git, build uses the new uv-based root Dockerfile / Dockerfile.frontend). - Remove src/frontend/flask_app/verify_i18n.py (had hardcoded paths pointing at the old client/ tree). Group docs/ - New docs/README.md indexes everything in one place. - New docs/architecture/STATO_PROGETTO.md: snapshot of what works in V2.0.0 (inherited V1.0.7 features, rev04 Phase 1 stations, worker scaling, src/ restructure, test status, stack, decisions). - New docs/architecture/ROADMAP.md: what's next — Phases 2-7 of the rev04 migration with status, open client decisions (D-0.1 through D-0.10), tech debt and time estimates for M1 / M2. - Move PIANO_IMPLEMENTAZIONE.md (90KB V1.0.0 plan) to docs/archive/2026-02-06-piano-implementazione-v1.md (historical). - Move Schema sviluppo SW TieFlow_rev04-2026.docx to docs/specs/ with ISO date filename so the customer spec is now tracked. - Move src/frontend/flask_app/I18N_SETUP.md to docs/I18N_SETUP.md and rewrite paths to the new src/frontend/flask_app/ tree. .dockerignore: simplified now that legacy Dockerfiles are gone; docs/ stays excluded from the build context. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+2
-6
@@ -28,14 +28,10 @@ htmlcov/
|
|||||||
# Node
|
# Node
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# Local-only notes (Markdown docs are still copied if a Dockerfile asks)
|
# Local-only notes
|
||||||
PIANO_IMPLEMENTAZIONE.md
|
docs/
|
||||||
*.docx
|
*.docx
|
||||||
|
|
||||||
# Legacy Dockerfiles preserved in tree but not needed in builds
|
|
||||||
src/backend/Dockerfile.legacy
|
|
||||||
src/frontend/flask_app/Dockerfile.legacy
|
|
||||||
|
|
||||||
# Uploads are a runtime volume, never baked in.
|
# Uploads are a runtime volume, never baked in.
|
||||||
uploads/
|
uploads/
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ TieMeasureFlow supports complete internationalization (i18n) with:
|
|||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
client/
|
src/frontend/flask_app/
|
||||||
├── translations/ # Flask-Babel translations
|
├── translations/ # Flask-Babel translations
|
||||||
│ ├── babel.cfg # Extraction config
|
│ ├── babel.cfg # Extraction config
|
||||||
│ ├── it/LC_MESSAGES/
|
│ ├── it/LC_MESSAGES/
|
||||||
@@ -99,7 +99,7 @@ flash(_("Profilo aggiornato con successo"))
|
|||||||
After editing .po files:
|
After editing .po files:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd client
|
cd src/frontend/flask_app
|
||||||
python compile_translations.py
|
python compile_translations.py
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Documentazione TieMeasureFlow
|
||||||
|
|
||||||
|
Indice della documentazione del progetto.
|
||||||
|
|
||||||
|
## Stato e direzione
|
||||||
|
|
||||||
|
| Documento | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| [`architecture/STATO_PROGETTO.md`](architecture/STATO_PROGETTO.md) | Cosa è fatto oggi (V2.0.0). Snapshot del sistema, componenti e capacità. |
|
||||||
|
| [`architecture/ROADMAP.md`](architecture/ROADMAP.md) | Cosa resta da fare. Fasi 2-7 della migrazione rev04 verso V1.1.0/M1 demo cliente. |
|
||||||
|
|
||||||
|
## Riferimenti operativi
|
||||||
|
|
||||||
|
| Documento | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| [`API.md`](API.md) | Riferimento endpoint REST esposti dal backend FastAPI. |
|
||||||
|
| [`DEPLOYMENT.md`](DEPLOYMENT.md) | Guida deploy su VPS (Hostinger/Tielogic con Traefik + Let's Encrypt). |
|
||||||
|
| [`USER_GUIDE.md`](USER_GUIDE.md) | Manuale utente (operatore, maker, metrologist, admin). |
|
||||||
|
| [`I18N_SETUP.md`](I18N_SETUP.md) | Setup e workflow traduzioni (Flask-Babel + Alpine.js). |
|
||||||
|
|
||||||
|
## Piani dettagliati TDD (rev04)
|
||||||
|
|
||||||
|
| Documento | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| [`superpowers/plans/2026-04-17-rev04-master-roadmap.md`](superpowers/plans/2026-04-17-rev04-master-roadmap.md) | Master plan rev04 V1.0.7 → V1.1.0, le 7 fasi e le decisioni aperte. |
|
||||||
|
| [`superpowers/plans/2026-04-17-rev04-phase1-stations.md`](superpowers/plans/2026-04-17-rev04-phase1-stations.md) | Piano TDD dettagliato Fase 1 (stazioni e identità per-tablet). **COMPLETATO.** |
|
||||||
|
|
||||||
|
## Specifiche esterne (binari)
|
||||||
|
|
||||||
|
| Documento | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| [`specs/2026-04-16-schema-sviluppo-rev04.docx`](specs/2026-04-16-schema-sviluppo-rev04.docx) | Schema sviluppo SW TieFlow rev04-2026 fornito dal committente. |
|
||||||
|
|
||||||
|
## Storico
|
||||||
|
|
||||||
|
| Documento | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| [`archive/2026-02-06-piano-implementazione-v1.md`](archive/2026-02-06-piano-implementazione-v1.md) | Piano implementazione V1.0.0 originale (riferimento storico). |
|
||||||
|
|
||||||
|
## Ulteriori riferimenti nel repo
|
||||||
|
|
||||||
|
- [`/CLAUDE.md`](../CLAUDE.md) — guidance per Claude Code (architettura, comandi, pattern critici).
|
||||||
|
- [`/README.md`](../README.md) — entry point del progetto.
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Roadmap TieMeasureFlow — V2.0.0 → V1.1.0 (rev04 / M1 demo cliente)
|
||||||
|
|
||||||
|
> Aggiornare ad ogni Fase chiusa.
|
||||||
|
|
||||||
|
## Riferimenti
|
||||||
|
|
||||||
|
- 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)
|
||||||
|
|
||||||
|
## Strategia: due milestone
|
||||||
|
|
||||||
|
| Milestone | Scope | Obiettivo |
|
||||||
|
|---|---|---|
|
||||||
|
| **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 |
|
||||||
|
|
||||||
|
## Stato Fasi (M1)
|
||||||
|
|
||||||
|
| Fase | Scope | Stato | Branch / Commit |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **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 | — |
|
||||||
|
|
||||||
|
## Stato Fasi (M2)
|
||||||
|
|
||||||
|
| 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
|
||||||
|
|
||||||
|
| 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. |
|
||||||
|
| `.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`. |
|
||||||
|
|
||||||
|
## Open per scelta utente prima della prossima sessione
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
## Stima tempi residui M1 (post-Fase 1)
|
||||||
|
|
||||||
|
| Task | Stima full-time |
|
||||||
|
|---|---|
|
||||||
|
| 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** |
|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# Stato Progetto TieMeasureFlow — V2.0.0
|
||||||
|
|
||||||
|
> Snapshot al 2026-04-25. 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.
|
||||||
|
|
||||||
|
## 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/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test status
|
||||||
|
|
||||||
|
| Suite | Pass | Fail | Note |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 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/`) | 44 | 1 | Fail pre-esistente: `test_save_measurement_proxy`. |
|
||||||
|
| **Totale** | **171** | **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`)
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
FROM python:3.11-slim
|
|
||||||
|
|
||||||
# Installa dipendenze sistema per WeasyPrint
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
libpango-1.0-0 \
|
|
||||||
libpangocairo-1.0-0 \
|
|
||||||
libcairo2 \
|
|
||||||
libgdk-pixbuf-2.0-0 \
|
|
||||||
libffi-dev \
|
|
||||||
shared-mime-info \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Crea directory uploads
|
|
||||||
RUN mkdir -p uploads/images uploads/pdfs uploads/logos uploads/reports
|
|
||||||
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
# Entry point: Alembic upgrade + Uvicorn
|
|
||||||
CMD ["sh", "-c", "alembic -c migrations/alembic.ini upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 --proxy-headers --forwarded-allow-ips='*'"]
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
FROM python:3.11-slim AS base
|
|
||||||
|
|
||||||
# Installa Node.js per Tailwind CSS build
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
curl \
|
|
||||||
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
|
||||||
&& apt-get install -y --no-install-recommends nodejs \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt gunicorn
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Install and build Tailwind CSS
|
|
||||||
RUN npm install tailwindcss@3 && npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --minify
|
|
||||||
|
|
||||||
# Compile Flask-Babel translations
|
|
||||||
RUN pybabel compile -d translations
|
|
||||||
|
|
||||||
EXPOSE 5000
|
|
||||||
|
|
||||||
CMD ["gunicorn", \
|
|
||||||
"--workers", "5", \
|
|
||||||
"--threads", "4", \
|
|
||||||
"--worker-class", "gthread", \
|
|
||||||
"--timeout", "60", \
|
|
||||||
"--bind", "0.0.0.0:5000", \
|
|
||||||
"--access-logfile", "-", \
|
|
||||||
"--forwarded-allow-ips", "*", \
|
|
||||||
"app:create_app()"]
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"""Verify i18n setup for TieMeasureFlow."""
|
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def check_file(path: Path, description: str) -> bool:
|
|
||||||
"""Check if a file exists and report."""
|
|
||||||
exists = path.exists()
|
|
||||||
status = "[OK]" if exists else "[FAIL]"
|
|
||||||
print(f"{status} {description}: {path}")
|
|
||||||
return exists
|
|
||||||
|
|
||||||
def check_json_valid(path: Path) -> bool:
|
|
||||||
"""Check if JSON file is valid."""
|
|
||||||
try:
|
|
||||||
with open(path, 'r', encoding='utf-8') as f:
|
|
||||||
json.load(f)
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ERROR: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Verify i18n setup."""
|
|
||||||
print("=== TieMeasureFlow i18n Verification ===\n")
|
|
||||||
|
|
||||||
root = Path(__file__).parent
|
|
||||||
all_good = True
|
|
||||||
|
|
||||||
# Check Flask-Babel files
|
|
||||||
print("Flask-Babel (Server-side):")
|
|
||||||
files = [
|
|
||||||
(root / "translations/babel.cfg", "Babel config"),
|
|
||||||
(root / "translations/it/LC_MESSAGES/messages.po", "Italian .po"),
|
|
||||||
(root / "translations/it/LC_MESSAGES/messages.mo", "Italian .mo"),
|
|
||||||
(root / "translations/en/LC_MESSAGES/messages.po", "English .po"),
|
|
||||||
(root / "translations/en/LC_MESSAGES/messages.mo", "English .mo"),
|
|
||||||
]
|
|
||||||
|
|
||||||
for path, desc in files:
|
|
||||||
if not check_file(path, desc):
|
|
||||||
all_good = False
|
|
||||||
|
|
||||||
# Count messages in .po files
|
|
||||||
if (root / "translations/it/LC_MESSAGES/messages.po").exists():
|
|
||||||
with open(root / "translations/it/LC_MESSAGES/messages.po", 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
msgid_count = content.count('msgid "') - 1 # Exclude header
|
|
||||||
print(f" Italian: {msgid_count} messages")
|
|
||||||
|
|
||||||
if (root / "translations/en/LC_MESSAGES/messages.po").exists():
|
|
||||||
with open(root / "translations/en/LC_MESSAGES/messages.po", 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
msgid_count = content.count('msgid "') - 1
|
|
||||||
print(f" English: {msgid_count} messages")
|
|
||||||
|
|
||||||
# Check Alpine.js i18n files
|
|
||||||
print("\nAlpine.js i18n (Client-side):")
|
|
||||||
json_files = [
|
|
||||||
(root / "static/js/locales/it.json", "Italian locale"),
|
|
||||||
(root / "static/js/locales/en.json", "English locale"),
|
|
||||||
]
|
|
||||||
|
|
||||||
for path, desc in json_files:
|
|
||||||
if check_file(path, desc):
|
|
||||||
if not check_json_valid(path):
|
|
||||||
all_good = False
|
|
||||||
else:
|
|
||||||
with open(path, 'r', encoding='utf-8') as f:
|
|
||||||
data = json.load(f)
|
|
||||||
keys = count_keys(data)
|
|
||||||
print(f" {keys} total keys")
|
|
||||||
|
|
||||||
# Check app.py integration
|
|
||||||
print("\nApp Integration:")
|
|
||||||
app_py = root / "app.py"
|
|
||||||
if check_file(app_py, "app.py"):
|
|
||||||
with open(app_py, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
checks = [
|
|
||||||
("from flask_babel import Babel", "Flask-Babel imported"),
|
|
||||||
("def get_locale()", "get_locale() function"),
|
|
||||||
("Babel(app, locale_selector=get_locale)", "Babel initialized"),
|
|
||||||
("def set_language(lang)", "set_language endpoint"),
|
|
||||||
]
|
|
||||||
for check_str, check_desc in checks:
|
|
||||||
found = check_str in content
|
|
||||||
status = "[OK]" if found else "[FAIL]"
|
|
||||||
print(f" {status} {check_desc}")
|
|
||||||
if not found:
|
|
||||||
all_good = False
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
print("\n" + "="*40)
|
|
||||||
if all_good:
|
|
||||||
print("[OK] i18n setup verified successfully!")
|
|
||||||
print("\nNext steps:")
|
|
||||||
print("1. Use _() in Python code and templates")
|
|
||||||
print("2. Use $t() in Alpine.js components")
|
|
||||||
print("3. Test language switching: /set-language/en or /set-language/it")
|
|
||||||
else:
|
|
||||||
print("[FAIL] Some checks failed - review errors above")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def count_keys(obj, depth=0):
|
|
||||||
"""Recursively count keys in nested dict."""
|
|
||||||
if not isinstance(obj, dict):
|
|
||||||
return 0
|
|
||||||
count = len(obj)
|
|
||||||
for value in obj.values():
|
|
||||||
if isinstance(value, dict):
|
|
||||||
count += count_keys(value, depth + 1)
|
|
||||||
return count
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
exit(main())
|
|
||||||
Reference in New Issue
Block a user