767bede43b22f7c82afdaba8c683624ab560946b
17 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
98b1c5ae85 |
feat(measure): l'operatore entra nella sequenza, non in un elenco
Punto 10. Sceglere una ricetta apriva la lista completa dei task e chiedeva all'operatore di decidere da dove cominciare prima ancora di aver fatto qualcosa. Ora AVVIA IN SEQUENZA porta dentro il primo task; la lista resta, un livello sotto, per quando serve vederla tutta o tornare indietro. - nuova rotta /measure/start/<ricetta>: memorizza lotto e seriale (prima lo faceva la lista, che ora si salta), verifica la tracciabilità obbligatoria e apre il primo task - GET /api/measurements/task-progress: quante quote ha già preso ogni task di misura, contate per quota e non per tentativo, delimitate dalla produzione aperta o, fuori produzione, dall'operatore - la lista distingue «Incompiuto 1/3» da «Completato 3/3»: un task lasciato a metà non somigliava più a uno mai aperto - «Visualizza singolo TASK» non è più riservato al Maker — dire «incompiuto» senza dare la strada per tornarci sarebbe una lamentela, non una funzione - nel task «Riepilogo» diventa «Completato» e si apre solo quando il ciclo è chiuso: altrimenti sarebbe la scorciatoia che rende facoltative le quote - «Fine ciclo misura» si vede da subito, spento, e dice quante quote mancano; prima compariva a task già finito, quando non serviva più saperlo Il pulsante verde del footer diventa «Task successivo»: due bottoni con la stessa parola addosso sono uno di troppo. Test: 334 (erano 309). Il side effect dei mock del client Flask ora risponde per endpoint invece che per turno — una lista posizionale si rompe appena una pagina fa una domanda in più al server, che è come crescono tutte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e2aae88858 |
feat(measure): il fuori tolleranza si autorizza, non si chiude
Il punto 5 c'era a meta': la schermata apriva il modale del capoturno e bloccava l'avanzamento automatico, ma il modale si chiudeva con Annulla o con un click sullo sfondo, la misura era gia' salvata, e `pendingAdvance` veniva impostato e non letto da nessuno. Era una conferma, non uno sbarramento. Ora l'autorizzazione finisce sulla misura: due colonne, chi ha autorizzato e quando. Finche' una quota e' fuori tolleranza e nessuno l'ha autorizzata, il server rifiuta la misura della quota successiva e la chiusura del ciclo. Non e' la schermata a impedirlo: la schermata risparmia solo il viaggio. Rimisurare la stessa quota resta possibile — il calibro scivola, il pezzo si riposiziona — e una seconda lettura in tolleranza libera il blocco, perche' conta l'ultima lettura di ogni quota. Quante volte si possa riprovare e' il punto 4, e tutte le letture restano comunque a registro. Un warning non blocca: fuori dai limiti di attenzione ma dentro la tolleranza e' dentro la tolleranza. Ricaricare la pagina era il modo piu' semplice per scavalcare il vecchio gate. Non lo e' piu': la schermata chiede al server, all'apertura, se una quota sta aspettando, e si ritrova davanti lo stesso blocco. Sparisce /validate-supervisor, che verificava le credenziali e buttava via la risposta. Al suo posto un endpoint che le credenziali le usa per scrivere l'approvazione dove serve. Il controllo del capoturno si sposta in auth_service, accanto al resto delle credenziali: fermo linea, chiusura e fuori tolleranza fanno la stessa domanda, e solo una delle tre riguarda la produzione. Il file di statistica guadagna authorised_by e authorised_at: mostrava il fallimento e non la decisione, che e' la meta' che un auditor chiede. Migrazione 010: due colonne nullable sulle misure. Le righe esistenti restano nulle — retrodatare un'autorizzazione mai avvenuta sarebbe inventarsi un record di audit, e quelle produzioni sono chiuse da un pezzo. Test: +18 (309). Coprono il rifiuto della quota successiva, la rimisura ammessa, il rilascio del blocco con una lettura buona, il ciclo che non si chiude, il capoturno registrato sulla misura, le credenziali sbagliate e chi capoturno non e', il file di statistica, e il fatto che una produzione chiusa non blocchi la successiva. Aggiunto uploads/statistics/ al gitignore: i test che chiudono una produzione scrivevano nel repository. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5aa3d595ad |
feat(recipes): la ricetta decide cosa pretendere e come si misura
Tre cose che prima non erano di nessuno diventano regole della ricetta, decise da chi la scrive e fatte valere dal server. Punto 8 — tracciabilita' obbligatoria. Lotto e seriale si dichiarano obbligatori sulla ricetta. L'operatore li inserisce alla selezione, dove il pulsante non si attiva finche' mancano, e la stessa regola vale sulla lista task, raggiungibile anche per link diretto, e sul barcode: uno sbarramento che dura solo finche' qualcuno prende in mano il lettore non e' uno sbarramento. La produzione non si apre e la misura non si salva senza cio' che la ricetta pretende, perche' un valore senza il suo lotto non e' riconducibile a niente, e accorgersene dopo significa accorgersene tardi. Punto 9 — inserimento manuale. Una ricetta puo' vietare i valori digitati, ed e' il valore predefinito: il calibro e' lo strumento, digitare e' cio' che va concesso. Dove e' vietato il tastierino non viene disegnato (non nascosto con i CSS: il markup nascosto e' markup che si puo' rimostrare) e restano correzione e conferma, perche' una lettura sbagliata va cancellata. Il controllo vero e' sul server: una regola che vive solo nel frontend e' un consiglio. Migliorato al passaggio il riconoscimento del calibro. Contava solo la raffica di cifre, cosi' una lettura corta come "9.5" — tre battute — finiva registrata come digitata a mano; ora conta anche l'Invio che il wedge manda dentro la stessa raffica. Senza questa correzione il divieto avrebbe respinto misure legittime. Punto 11 — formattazione delle descrizioni. A capo e grassetto sopravvivono: chi scrive le ricette incolla dal PDF della scheda tecnica e il testo arrivava appiattito, da risistemare a mano ogni volta. Nessun HTML viene accettato o salvato — il testo viene escapato e gli unici tag nel risultato sono quelli prodotti dal renderer. La sanificazione e' questa: non c'e' niente da sanificare perche' non si accetta niente. Le stesse due regole in Jinja e in JS, cosi' una descrizione si legge uguale ovunque. E la descrizione ora si vede anche in esecuzione: era scritta per l'operatore e la vedeva solo chi la scriveva. Migrazione 009: tre colonne sulla ricetta. Le due di tracciabilita' partono false, che e' il comportamento di oggi; l'inserimento manuale parte *vero* sulle ricette gia' esistenti — il default della colonna e' falso, quindi le ricette nuove sono solo-calibro, ma spegnerlo d'ufficio su quelle in uso fermerebbe una linea alla misura successiva. Chi possiede la ricetta lo decide dall'editor. Test: +26 (291). Coprono il rifiuto sul server per lotto, seriale e valore digitato, il calibro sempre ammesso, le regole che sopravvivono alla nuova versione, il tastierino assente in pagina, l'Avvia sbarrato, e il renderer delle descrizioni compreso il caso in cui si prova a farci passare un tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
bde8fafd77 |
feat(production): la misura torna a cercare l'operatore
Il conto alla rovescia non si ferma piu' a zero. Oltre la scadenza continua nell'altro senso e dice da quanto la linea e' in ritardo, in rosso, su tutte le schermate del percorso di misura: lista task, riepilogo, scelta ricetta. Un ritardo va letto, non dedotto. Allo scadere dell'intervallo la misura si ripropone da sola: cicalino, un avviso di cinque secondi, e l'operatore torna al primo task di misura da qualunque schermata si trovi. Un ciclo copre tutti i task di misura della ricetta, quindi solo l'ultimo lo chiude e fa ripartire l'intervallo. Farlo ripartire al primo avrebbe fatto misurare una ricetta con tre task di misura tre volte piu' spesso di come e' configurata. E' il server a decidere quale task chiude il ciclo, perche' e' lui a conoscere la sequenza: la lista dei task di misura viaggia con la produzione (measurement_task_ids), che e' anche cio' che permette a una schermata qualsiasi di sapere dove riportare l'operatore. Aggiunta la rimisura: si gira il pezzo e si misura di nuovo dentro lo stesso ciclo, senza chiudere niente e senza guadagnare tempo sulla scadenza. Le due letture restano entrambe in statistica, che e' il motivo per cui si prendono. Chi sta gia' misurando quando l'intervallo scade e' in ritardo, non perso: la banda diventa rossa e lo si lascia lavorare. Portarlo altrove a meta' ciclo cancellerebbe quote che ha davanti agli occhi senza guadagnare nulla. Sistemato anche il pulsante "Conferma ciclo" dell'overlay: alzava una bandierina locale e basta, quindi il ciclo non veniva mai registrato sul server da quella strada. Ora passa da confirmCycle come il pulsante della barra. Migrazione 008: il registro eventi della produzione impara task_measured e remeasure, e la colonna task_id — un task_measured che non dice quale task non registra niente di utile. I valori nuovi entrano tutti insieme perche' allargare una enum MySQL riscrive la tabella, stesso ragionamento dei tipi di task in 007. Verificata su SQLite (batch mode) e in MySQL con --sql. La logica dell'orologio — come si legge un ritardo, quando suona, dove sta la misura — vive in un solo posto (production-clock.js) e la schermata di misura la usa invece di riscriverla. Test: +10 (265). Coprono il ciclo che non riparte a meta', la rimisura che non sposta la scadenza, il rifiuto a linea ferma, la sequenza dei task di misura esposta dalla produzione, i proxy Flask e la validita' JS della lista task. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6fbff2fe76 |
feat(tasks): il tipo di un task si dichiara, non si deduce
Punto 2 del documento modifiche del 28/07, prima richiesta di Menoncin. Il sistema distingueva un task di misura da uno documentale deducendolo: se aveva quote era una misura, altrimenti una nota. Conseguenza: un task di misura a cui le quote non erano ancora state inserite veniva trattato come nota, e il sistema si comportava in modo diverso a seconda di quanto fosse completa la ricetta. Nuovo campo task_type su recipe_tasks (migrazione 007) con nota, misura e disegno, piu' xf_compare e camera_measure gia' nell'enum: allargare un enum MySQL piu' avanti e' un ALTER su tabella viva, e non costa nulla prevederli adesso. Il backfill riproduce la classificazione che era a schermo, cosi' nessuna ricetta cambia comportamento all'aggiornamento: i task con quote diventano 'measure'; quelli senza quote ma con un disegno allegato diventano 'drawing' e non 'note', perche' e' gia' cio' che mostravano - chiamarli note sarebbe stato l'unico punto in cui questa migrazione cambiava le carte in tavola. Il tipo viene copiato esplicitamente nel copy-on-write del versioning: una nuova versione che lo perdesse riclassificherebbe in silenzio tutti i task alla prima modifica di una ricetta. Il task "Technical Drawing" creato d'ufficio quando si carica un disegno su una ricetta senza task nasce come 'drawing'. Lato operatore decide ora il tipo, non il conteggio delle quote: fermo linea, fine produzione, avvio produzione e la barra di produzione seguono il tipo. Dove servono davvero delle quote da mostrare - elenco marker, tastierino, fine ciclo misura - resta anche il controllo che ce ne sia almeno una, e un task di misura ancora privo di quote lo dichiara invece di somigliare a una nota. Nella lista task compare il tipo, cosi' si vede prima di aprire. La 007 e' stata eseguita su SQLite usa e getta con tre righe costruite apposta - una con quote, una col solo disegno, una nota secca - e il backfill le classifica come atteso. Il backfill girera' sui dati reali del cliente, provarlo a mano non bastava. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
7bc3c1f938 |
feat(production): fermo linea e fine produzione fanno finalmente qualcosa
Punto 6 del documento modifiche del 28/07. I due pulsanti esistevano, chiedevano correttamente l'autorizzazione del capoturno, e poi ricadevano su un commento: "handled by GAIA integration (future)". E' la riga di apertura del documento. Ora agiscono sulla produzione aperta: fermo linea sospende e congela il conto alla rovescia, la ripresa lo fa ripartire restituendo il tempo del fermo, fine produzione chiude ed emette il file di statistica. Le credenziali del capoturno vanno direttamente all'endpoint, che le verifica e registra sulla traccia della produzione chi ha autorizzato cosa: validarle a parte non lascerebbe traccia, e sarebbe un giro in piu'. In interfaccia il pulsante di fermo diventa "Riprendi" quando la linea e' ferma, e una banda dichiara lo stato con il valore congelato a video: una linea ferma che sembra in marcia e' il modo in cui si salta un intervallo di misura senza accorgersene. Il file di statistica ha richiesto di legare le misure alla produzione (migrazione 006): Measurement conosceva solo versione, lotto e seriale, nessuno dei quali separa una produzione dalla successiva sulla stessa ricetta e sullo stesso lotto, quindi "le misure dell'intera produzione" non era una query. Il CSV rispetta i separatori configurati in system_settings e porta con se' i limiti di tolleranza: senza quelli un esito pass/fail non e' piu' ricalcolabile dal file a distanza di anni, che e' il senso di un documento per audit. Le misure esportate vengono marcate synced_to_csv. Se la produzione non ha misure non viene scritto nulla: un file vuoto sarebbe rumore nella cartella, non evidenza. L'invio a GAIA resta assente e non abbozzato, con il punto d'innesto dichiarato in close_run: una chiamata vuota che sembra collegata e' peggio di niente. Tutto il resto del punto 6 non dipende dal gestionale e funziona adesso. Anche la 006 e' stata eseguita su SQLite usa e getta prima di essere considerata buona, e anche qui la prova ha trovato un difetto: aggiungere una colonna con foreign key fa emettere ad alembic un ALTER di vincolo, che SQLite rifiuta. Ora usa batch_alter_table con il vincolo nominato, che su MySQL resta un ALTER normale e su SQLite ricostruisce la tabella - cosi' la chiave esterna non va persa per far contento il dialetto dei test. Corretto anche il proxy di salvataggio misura, che chiamava /api/measurements senza slash finale e pagava un redirect 307 a ogni singola misura. Traduzioni: pybabel aveva di nuovo indovinato sette voci marcandole fuzzy, e in italiano "Linea ferma" era diventato "Lingua Preferita" su una banda di sicurezza. Tradotte per esteso in IT ed EN e tolti i flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
55e5e0153f |
feat(production): dai una vita propria alla produzione, lato server
Punto 1 del documento modifiche del 28/07, il prerequisito su cui poggiano i punti 3, 4 e 6. Lo stato di una produzione viveva dentro una pagina del browser: timer, conteggio cicli e flag "produzione avviata" erano variabili Alpine di task_execute.html, e la navigazione fra task e' un ricaricamento completo, quindi cambiando task si perdeva tutto. Da qui il loop di misura che non reggeva, il fermo linea che non aveva nulla da fermare e l'assenza di storico. Nuove tabelle production_runs e production_events (migrazione 005), endpoint REST senza stato in memoria di processo - con un'app di stazione installata su ogni PC il database e' l'unico posto condiviso - e il frontend che legge lo stato all'apertura invece di tenerlo in memoria. Tre scelte di modello: - la scadenza e' un timestamp assoluto (next_measurement_at), non un contatore: il countdown si ricalcola da li' a ogni caricamento, e lasciarne andare la differenza sotto zero dara' gratis il contatore del ritardo del punto 3. Al client vanno i secondi gia' calcolati, non il timestamp: un datetime naive verrebbe letto nel fuso del browser e il conto sarebbe sfasato dell'offset UTC; - l'intervallo di misura e' copiato sulla produzione, non referenziato: modificare la ricetta a produzione avviata non deve spostare una scadenza in corso; - active_station_id rispecchia la stazione finche' la produzione e' aperta e va a NULL alla chiusura. Con un vincolo unico sopra, "una stazione = una produzione aperta" e' una garanzia del database e non un controllo soggetto a race; i NULL non collidono, quindi le produzioni chiuse si accumulano senza disturbo. Il fermo linea congela il conto alla rovescia e alla ripresa la scadenza viene traslata della durata del fermo, non ricalcolata: un fermo non regala ne' toglie tempo all'operatore. L'autorizzazione del capoturno passa da authenticate_user e non da un login, che rigenererebbe la sua API key buttando giu' la sessione che ha aperta altrove. La migrazione e' stata eseguita davvero, non solo scritta, su uno SQLite usa e getta: upgrade e downgrade girano e le colonne coincidono con i modelli. La prova ha trovato un difetto - create_unique_constraint dopo create_table e' un ALTER, che SQLite rifiuta - ora il vincolo e' dichiarato dentro create_table. Fuori da questo commit, per stare nei confini del punto 1: l'API espone gia' pause, resume e close, ma i pulsanti fermo linea e fine produzione restano da collegare (punto 6), e il rientro forzato sulla misura allo scadere e' il punto 3. Corretti due difetti trovati strada facendo: env.py non importava ne' Station ne' ProductionRun, quindi l'autogenerate di Alembic era gia' cieco sulle stazioni; e task_execute.html, lo schermo con piu' JavaScript dell'applicazione, non era coperto dal test di sintassi. Aggiungerlo ha richiesto di correggere l'helper, che validava le espressioni Alpine solo come espressione singola e bocciava @click="a = false; b = true", forma che Alpine accetta: ora prova entrambe le letture e fallisce solo se cadono tutte e due. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
28ee44bcf1 |
feat(client): includi librerie e font in locale per il funzionamento offline
Punto 12 del documento modifiche del 28/07. L'installazione a Trafilo e' on-premise su rete di produzione isolata: finche' Alpine.js, Plotly, PDF.js, Fabric.js e i Google Fonts venivano scaricati da CDN a ogni apertura di pagina, senza internet l'interfaccia non partiva affatto (Alpine governa tutta la UI). Le librerie passano in static/vendor/ con la versione nel nome del file, e la CSP del backend si chiude a solo-origine. Il worker di PDF.js e' ripuntato in tutti e quattro i template che lo usano: ripuntando solo lo script principale la libreria si carica in locale ma il worker continua a cercare internet, e sembra funzionare finche' non si apre un disegno. Alpine e' pinnato a 3.15.12, cioe' la versione a cui "3.x.x" risolveva oggi: il congelamento non cambia il comportamento di quanto e' gia' in esercizio, e toglie il fatto che l'applicazione cambiasse da sola a ogni rilascio degli autori - rilevante per le evidenze ISO 9001 / IATF 16949. Inter e JetBrains Mono sono font variabili: un solo woff2 per subset copre tutti i pesi, dove Google serviva lo stesso file sotto otto URL. Tenuti i soli subset latin e latin-ext, che coprono per intero italiano e inglese. Provenienza, versioni e impronte SHA-256 in static/vendor/VERSIONS.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3f03ec8ab1 |
feat(measure): memorizza tempo di inserimento misura (input_duration_ms)
Colonna nullable su measurements + migration 003, campo opzionale negli schemi API (create singola e batch), passthrough nel proxy Flask e timer lato client in task_execute: parte all'attivazione del subtask, si resetta su auto-advance e navigazione manuale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
34a72e6f1a |
fix(measure): skip completion overlay during START, auto-advance task
The previous change made the overlay phase-aware but it still popped up after every task during the START run. Operators want to flow straight to the next task. Skip showing the "Misurazioni Complete" overlay when production has not started and this is not the last task — call goToNextTask() directly. The overlay still appears on the last START task (for "Avvio Produzione") and in production (for "Conferma ciclo"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4c75a32186 |
fix(measure): START cycle advances to next task instead of blocking
When all subtasks of a task were measured, the completion overlay only offered "Riepilogo" (which leaves the work cycle) or "Conferma ciclo" (which keeps the operator stuck on the same task and starts the interval timer). During the START run — before production is started — neither lets the operator simply move to the next task. Make the overlay phase-aware: - Production started: keep "Conferma ciclo". - START, not the last task: primary action "Task successivo" -> goToNextTask(). - START, last task: "Avvio Produzione" -> startProductionFromOverlay(), which starts production and kicks off the interval cycle. Add isLastTask getter and startProductionFromOverlay(); EN translation for the new label. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fdfe1072d8 |
fix(measure): supervisor modal — backspace works and fields reset
Two issues with the "Autorizzazione capoturno" modal: 1. Backspace and digits did not work in the username/password fields. The numpad component installs a window-level keydown handler (for the USB caliper / keyboard wedge) that preventDefault()s digits, Backspace, Enter etc. It swallowed those keys inside the modal inputs. Guard handleKeydown to ignore events whose target is an editable field (INPUT/TEXTAREA/SELECT/ contentEditable); the numpad has no text inputs of its own. 2. The modal kept the previous username/password: opening only set the flag, and closing via backdrop click did not clear the fields. Add openSupervisorModal()/closeSupervisorModal() that always reset username, password and error, and use them for every open (fermo_linea, fine_produzione, out_of_tolerance) and close (backdrop, Annulla). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9bd605c958 |
feat(measure): bottone Avvio Produzione placeholder dopo primo ciclo misura
- Bottone "Avvio Produzione" appare solo dopo il primo ciclo confermato - Conferma visiva "Produzione avviata" dopo il click - Placeholder per futura integrazione GAIA (TODO nel codice) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e05eb66a1c |
feat(measure): timer cicalino con countdown e buzzer dopo fine ciclo misura
- Blueprint: passa measurement_interval_minutes dalla ricetta al template task_execute - Dopo "Fine ciclo misura": countdown MM:SS con banner amber animato sopra footer - Allo scadere: 3 beep sonori (Web Audio API 880Hz) + reset misurazioni per nuovo ciclo - Contatore cicli visibile nel banner timer Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1da7e5c7af |
feat(measure): rework workflow operatore — AVVIA, capoturno, fine ciclo, blocco logout
- task_list: pulsante AVVIA sequenziale + rinomina "Inizia Misure" → "Visualizza Task" - task_execute header: aggiunti "Lista task", "Riepilogo", "Fermo linea", "Fine Produzione" - task_execute footer: "Fine ciclo misura" (attivo quando tutte le quote misurate) + "Completato" - Rimosso auto-advance al task successivo, flusso ora esplicito via Completato - Misura fuori tolleranza: blocco avanzamento + modale login capoturno (validate_supervisor) - Blocco logout durante misurazioni attive (icona lucchetto su task_execute) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
52d78ea5c1 |
fix(measure): task_execute full-screen layout + hide UI for tasks without measurements
Pagina /measure/execute/<task_id>: - Il pannello destro (info subtask + tolerance + numpad) e la sidebar marker spariscono quando il task non ha subtask: l'utente vede solo intestazione + disegno a tutta larghezza, senza colonne vuote né tastierina numerica fuori contesto. - Risolta la scrollbar verticale: la pagina ora occupa esattamente l'altezza del viewport. Il layout sfrutta nuovi block override in base.html (body_class, wrapper_class, main_class, footer) per disattivare scroll e footer solo su questa pagina, lasciando intatte le altre. Cataloghi i18n: aggiunte traduzioni EN/IT mancanti e rimossi i flag "fuzzy" auto-generati da pybabel update (Flask-Babel ignora i fuzzy, mostrando il msgid italiano anche in EN). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1a0431366f |
chore(v2): restructure monorepo to src/ layout with uv
Aligns the repo with the python-project-spec-design.md template chosen for V2.0.0. Big move, no logic changes. The 3 pre-existing test failures (test_recipes::test_update_recipe, test_recipes:: test_recipe_versioning, test_tasks::test_reorder_tasks, plus the client test_save_measurement_proxy) survive unchanged. Layout changes - server/ -> src/backend/ - server/middleware/ -> src/backend/api/middleware/ - server/routers/ -> src/backend/api/routers/ - server/models/ -> src/backend/models/orm/ - server/schemas/ -> src/backend/models/api/ - server/uploads/ -> uploads/ (project root, mounted volume) - server/tests/ -> src/backend/tests/ - client/ -> src/frontend/flask_app/ (Flask kept; React deroga is documented in CLAUDE.md, justified by tablet UX, USB caliper/barcode workflow and Fabric.js integration) Tooling - pyproject.toml: monorepo with [project] core deps and optional-dependencies server / client / dev. Replaces both server/requirements.txt and client/requirements.txt. - uv.lock + .python-version (3.11) committed for reproducible builds. - Dockerfile (root, backend) and Dockerfile.frontend rewritten to use uv sync --frozen --no-dev --extra server|client; legacy Dockerfiles preserved as Dockerfile.legacy for reference but excluded from build context via .dockerignore. - docker-compose.dev.yml + docker-compose.yml: build context now ".", dockerfile pointing to the root files. Code adjustments forced by the move - Every "from config|database|models|schemas|services|routers|middleware import ..." rewritten to its src.backend.* equivalent (50+ files including indented inline imports inside test bodies). - src/backend/migrations/env.py: insert project root into sys.path so alembic can resolve src.backend.* imports regardless of cwd. - src/backend/config.py: env_file ../../.env (was ../.env), upload_path resolves project root via parents[2]. - src/backend/tests/conftest.py + tests: import ... from src.backend.* instead of bare names; old per-directory pytest.ini files removed in favor of root pyproject.toml [tool.pytest.ini_options]. - .gitignore: uploads/ at root, src/frontend/flask_app/static/css/ tailwind.css path; .dockerignore tightened. - CLAUDE.md: rewrote sections "Layout del repository", "Comandi di Sviluppo", "Database & Migrations", "Test", "i18n", and all path references throughout the architecture sections. Verified - uv lock resolves 77 packages; uv sync --extra server --extra client --extra dev installs cleanly. - uv run pytest: 171 passed, 4 pre-existing failures. - uv run alembic -c src/backend/migrations/alembic.ini check loads config and metadata (errors only on the absent local MySQL). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |