Wizard DXF: anteprima del disegno appena caricato, con L × H mm #101

Closed
opened 2026-10-25 14:51:44 +00:00 by Adriano · 2 comments
Owner

Richiesta Adriano 25/10: quando si carica un dxf sulla ricetta mostrare l'anteprima. Fatto quando: scelto il .dxf nel wizard «Crea ricetta da DXF» compare il disegno (letto dal worker con read_xld_dxf) e le misure L × H.

Richiesta Adriano 25/10: quando si carica un dxf sulla ricetta mostrare l'anteprima. Fatto quando: scelto il .dxf nel wizard «Crea ricetta da DXF» compare il disegno (letto dal worker con read_xld_dxf) e le misure L × H.
Adriano added the sviluppo label 2026-10-25 14:51:44 +00:00
Author
Owner

ESITO: richiesta
COSA: anteprima del disegno appena si sceglie il .dxf nel wizard «Crea ricetta da DXF» (editor task, tipo dxf_compare), con dimensioni L × H in mm, prima di creare la ricetta.

Richiesta di Adriano 25/10/2026: «quando carichi un dxf sulla ricetta fai vedere anteprima».
Scelta di Opus: il DXF si legge sul vision worker con lo stesso lettore del confronto (visionsuite.io.dxf.read_xld_dxf, usato in src/vision/costruttori.py:227), così l'anteprima mostra esattamente la geometria che CompareDxf userà. Niente parser DXF in JavaScript.

CONTRATTO (identico per piano A e piano B)

  • Worker: POST /dxf/preview (src/vision_worker/main.py), multipart campo dxf.
  • Backend: POST /api/vision/dxf-preview (src/backend/api/routers/vision.py), require_maker, Form dxf: UploadFile; inoltra al worker e restituisce la stessa risposta.
  • Flask: POST /maker/api/vision/dxf-preview (blueprints/maker.py), @login_required + @role_required("Maker"), campo file dxf; inoltra al backend.
  • Risposta 200 (JSON, identica ai tre livelli):
    {"contorni": [[[x, y], ...], ...], "bbox": {"xmin": f, "ymin": f, "xmax": f, "ymax": f}, "larghezza_mm": f, "altezza_mm": f, "entita": int, "punti": int}
    Coordinate in mm del DXF, asse y verso il basso (come immagine e SVG): y = -y_dxf. Arrotondate a 3 decimali. Totale punti ≤ 20000 (decimazione uniforme per contorno se si supera, mantenendo primo e ultimo punto). entita = numero di contorni, punti = punti restituiti.
  • Errori: DXF vuoto, illeggibile o senza geometria → 422 {"detail": "DXF illeggibile: ..."} oppure {"detail": "DXF senza geometria"}; worker irraggiungibile → stesso codice e forma che oggi usa la rotta dxf-compare del backend; Flask inoltra codice e corpo come fa api_dxf_compare_recipe (maker.py:674).
  • Limite dimensione: quello già usato per il dxf nella rotta dxf-compare del backend (se c'è); altrimenti 10 MB → 413.

PIANO A — worker + backend (ramo lavoro/daitarn-tmf-101a)

  1. src/vision_worker/main.py: rotta /dxf/preview. Scrive i byte in un file temporaneo, read_xld_dxf(path, sampling_distance=0.05, scale=1.0, flip_y=False), poi y = -y; i contorni li converte in liste [x, y] (verificare i nomi degli attributi di XldCont: righe/colonne o x/y; leggere visionsuite/io/dxf.py nel venv C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal/build/venv313). La parte sincrona va in run_in_threadpool/executor come _decode_and_build_dxf (main.py:229).
  2. src/backend/services/vision_service.py: funzione dxf_preview(dxf_bytes) sul modello di dxf_compare_recipe (riga ~201), riusando il POST multipart generico al worker (riga ~154).
  3. src/backend/api/routers/vision.py: rotta POST /dxf-preview (prefisso del router = /api/vision).
  4. Test: worker (funzione pura con un DXF minimo generato con ezdxf: un rettangolo 10×5 → larghezza 10, altezza 5, y negativa→positiva) e backend (rotta con il servizio sostituito, 422 inoltrato, ruolo Maker richiesto). Comando backend: PYTHONPATH=. C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal-backend/.venv/Scripts/python.exe -m pytest src/backend/tests/<file> -q -p no:cacheprovider. Test del worker: con C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal/build/venv313/Scripts/python.exe (ha visionsuite), cartella test esistente del worker (cercarla: src/vision_worker/tests o tests/).
    NON TOCCARE (A): src/frontend/**, costruttori.py (solo lettura), CompareDxf.

PIANO B — Flask + pagina (ramo lavoro/daitarn-tmf-101b)

  1. blueprints/maker.py: proxy /api/vision/dxf-preview sul modello di api_dxf_compare_recipe (riga 674): 400 «Serve il file DXF» se manca.
  2. static/js/dxf_recipe_wizard.js: stato dxfPreview: null, dxfPreviewBusy: false, dxfPreviewError: ''. onDxf(ev) (riga 90) oltre a oggi chiama loadDxfPreview() → POST multipart con CSRF come run() (riga ~133-141). Con file tolto: tutto azzerato. Una risposta arrivata dopo un cambio di file si scarta (contatore).
    Funzione dxfPreviewPaths() → array di stringhe points per <polyline>; dxfPreviewViewBox() → bbox con margine 5% (minimo 0.5 mm).
  3. templates/maker/task_editor.html, sotto l'input «Disegno (.dxf)» (riga ~715): riquadro SVG (altezza max ~240 px, larghezza piena, preserveAspectRatio="xMidYMid meet", sfondo var(--bg-primary), bordo), <polyline fill="none" vector-effect="non-scaling-stroke" stroke-width="1.5"> col colore primario; sotto: «L × H mm · N entità» con virgola decimale (2 decimali). Durante il caricamento «Lettura DXF…»; errore in rosso col detail. Testi con _(), voci in translations/en/LC_MESSAGES/messages.po (non committare .mo).
  4. Test frontend: proxy (inoltra il file, 400 senza file, ruolo) in src/frontend/flask_app/tests/ con mock_api_client; comando C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal-frontend/.venv/Scripts/python.exe -m pytest tests -q -p no:cacheprovider da src/frontend/flask_app. Base: 9 fallimenti preesistenti (+4 di lingua/.mo nei worktree), nessuno nuovo.
    NON TOCCARE (B): src/backend/, src/vision_worker/, templates/statistics/** (ci lavora #100).

PROVA (Opus): merge A+B su lavoro/daitarn-tmf-dxfcal, riavvio TMF, headless Edge su editor del task 8: scelta di C:/Users/Adriano/Desktop/Sider foto e DXF/DXF profilo/12.3X5.1MEVISS235JR_profilo.dxf → SVG con contorni e circa «12,30 × 5,10 mm»; file non DXF → errore leggibile.

ESITO: richiesta COSA: anteprima del disegno appena si sceglie il .dxf nel wizard «Crea ricetta da DXF» (editor task, tipo dxf_compare), con dimensioni L × H in mm, prima di creare la ricetta. Richiesta di Adriano 25/10/2026: «quando carichi un dxf sulla ricetta fai vedere anteprima». Scelta di Opus: il DXF si legge sul vision worker con lo stesso lettore del confronto (`visionsuite.io.dxf.read_xld_dxf`, usato in `src/vision/costruttori.py:227`), così l'anteprima mostra esattamente la geometria che CompareDxf userà. Niente parser DXF in JavaScript. CONTRATTO (identico per piano A e piano B) - Worker: `POST /dxf/preview` (src/vision_worker/main.py), multipart campo `dxf`. - Backend: `POST /api/vision/dxf-preview` (src/backend/api/routers/vision.py), `require_maker`, Form `dxf: UploadFile`; inoltra al worker e restituisce la stessa risposta. - Flask: `POST /maker/api/vision/dxf-preview` (blueprints/maker.py), `@login_required` + `@role_required("Maker")`, campo file `dxf`; inoltra al backend. - Risposta 200 (JSON, identica ai tre livelli): {"contorni": [[[x, y], ...], ...], "bbox": {"xmin": f, "ymin": f, "xmax": f, "ymax": f}, "larghezza_mm": f, "altezza_mm": f, "entita": int, "punti": int} Coordinate in mm del DXF, asse y verso il basso (come immagine e SVG): y = -y_dxf. Arrotondate a 3 decimali. Totale punti ≤ 20000 (decimazione uniforme per contorno se si supera, mantenendo primo e ultimo punto). `entita` = numero di contorni, `punti` = punti restituiti. - Errori: DXF vuoto, illeggibile o senza geometria → 422 {"detail": "DXF illeggibile: ..."} oppure {"detail": "DXF senza geometria"}; worker irraggiungibile → stesso codice e forma che oggi usa la rotta dxf-compare del backend; Flask inoltra codice e corpo come fa `api_dxf_compare_recipe` (maker.py:674). - Limite dimensione: quello già usato per il dxf nella rotta dxf-compare del backend (se c'è); altrimenti 10 MB → 413. PIANO A — worker + backend (ramo lavoro/daitarn-tmf-101a) 1. src/vision_worker/main.py: rotta `/dxf/preview`. Scrive i byte in un file temporaneo, `read_xld_dxf(path, sampling_distance=0.05, scale=1.0, flip_y=False)`, poi y = -y; i contorni li converte in liste [x, y] (verificare i nomi degli attributi di XldCont: righe/colonne o x/y; leggere `visionsuite/io/dxf.py` nel venv `C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal/build/venv313`). La parte sincrona va in `run_in_threadpool`/executor come `_decode_and_build_dxf` (main.py:229). 2. src/backend/services/vision_service.py: funzione `dxf_preview(dxf_bytes)` sul modello di `dxf_compare_recipe` (riga ~201), riusando il POST multipart generico al worker (riga ~154). 3. src/backend/api/routers/vision.py: rotta `POST /dxf-preview` (prefisso del router = /api/vision). 4. Test: worker (funzione pura con un DXF minimo generato con ezdxf: un rettangolo 10×5 → larghezza 10, altezza 5, y negativa→positiva) e backend (rotta con il servizio sostituito, 422 inoltrato, ruolo Maker richiesto). Comando backend: `PYTHONPATH=. C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal-backend/.venv/Scripts/python.exe -m pytest src/backend/tests/<file> -q -p no:cacheprovider`. Test del worker: con `C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal/build/venv313/Scripts/python.exe` (ha visionsuite), cartella test esistente del worker (cercarla: `src/vision_worker/tests` o `tests/`). NON TOCCARE (A): src/frontend/**, costruttori.py (solo lettura), CompareDxf. PIANO B — Flask + pagina (ramo lavoro/daitarn-tmf-101b) 1. blueprints/maker.py: proxy `/api/vision/dxf-preview` sul modello di `api_dxf_compare_recipe` (riga 674): 400 «Serve il file DXF» se manca. 2. static/js/dxf_recipe_wizard.js: stato `dxfPreview: null`, `dxfPreviewBusy: false`, `dxfPreviewError: ''`. `onDxf(ev)` (riga 90) oltre a oggi chiama `loadDxfPreview()` → POST multipart con CSRF come `run()` (riga ~133-141). Con file tolto: tutto azzerato. Una risposta arrivata dopo un cambio di file si scarta (contatore). Funzione `dxfPreviewPaths()` → array di stringhe `points` per `<polyline>`; `dxfPreviewViewBox()` → bbox con margine 5% (minimo 0.5 mm). 3. templates/maker/task_editor.html, sotto l'input «Disegno (.dxf)» (riga ~715): riquadro SVG (altezza max ~240 px, larghezza piena, `preserveAspectRatio="xMidYMid meet"`, sfondo var(--bg-primary), bordo), `<polyline fill="none" vector-effect="non-scaling-stroke" stroke-width="1.5">` col colore primario; sotto: «L × H mm · N entità» con virgola decimale (2 decimali). Durante il caricamento «Lettura DXF…»; errore in rosso col detail. Testi con `_()`, voci in `translations/en/LC_MESSAGES/messages.po` (non committare .mo). 4. Test frontend: proxy (inoltra il file, 400 senza file, ruolo) in `src/frontend/flask_app/tests/` con `mock_api_client`; comando `C:/Users/Adriano/lavoro/TieMeasureFlow-dxfcal-frontend/.venv/Scripts/python.exe -m pytest tests -q -p no:cacheprovider` da `src/frontend/flask_app`. Base: 9 fallimenti preesistenti (+4 di lingua/.mo nei worktree), nessuno nuovo. NON TOCCARE (B): src/backend/**, src/vision_worker/**, templates/statistics/** (ci lavora #100). PROVA (Opus): merge A+B su lavoro/daitarn-tmf-dxfcal, riavvio TMF, headless Edge su editor del task 8: scelta di `C:/Users/Adriano/Desktop/Sider foto e DXF/DXF profilo/12.3X5.1MEVISS235JR_profilo.dxf` → SVG con contorni e circa «12,30 × 5,10 mm»; file non DXF → errore leggibile.
Author
Owner

Soluzione

ESITO: fatto
FILE: src/vision_worker/main.py — POST /dxf/preview (_dxf_preview: read_xld_dxf sampling 0.05 mm, y=-y, bbox su punti completi, decimazione ≤20000); src/backend/services/vision_service.py dxf_preview; src/backend/api/routers/vision.py POST /api/vision/dxf-preview (Maker, limite max_upload_size_mb → 422); src/frontend/flask_app/blueprints/maker.py proxy /maker/api/vision/dxf-preview; static/js/dxf_recipe_wizard.js loadDxfPreview/dxfPreviewD/dxfPreviewViewBox/dxfPreviewSummary; templates/maker/task_editor.html SVG con un solo e x-bind:view-box.camel (x-for dentro svg non va con Alpine; :viewBox viene abbassato dal parser).
TEST: backend test_vision_dxf_preview.py (5), worker test_dxf_compare.py -k preview (5), frontend TestDxfPreviewProxy (4) + tests/js/test_dxf_preview.js.
COMMIT: ebaafbe, bdb1e8c, e582df7; merge fino a 4791c34 su lavoro/daitarn-tmf-dxfcal (pushato).
PROVA: TMF daitarn riavviato, headless Edge task 8 ricetta 3: MEVIS chiuso → path d 488 car., viewBox 3.68 -21.997 13.53 6.33, «12,30 × 5,10 mm · 1 entità»; PR165 profilo → «15,38 × 5,20 mm · 12 entità»; DXF completo Sider (R12 rotto) → errore rosso «DXF illeggibile: ValueError: Invalid handle 0.»

**Soluzione** ESITO: fatto FILE: src/vision_worker/main.py — POST /dxf/preview (_dxf_preview: read_xld_dxf sampling 0.05 mm, y=-y, bbox su punti completi, decimazione ≤20000); src/backend/services/vision_service.py dxf_preview; src/backend/api/routers/vision.py POST /api/vision/dxf-preview (Maker, limite max_upload_size_mb → 422); src/frontend/flask_app/blueprints/maker.py proxy /maker/api/vision/dxf-preview; static/js/dxf_recipe_wizard.js loadDxfPreview/dxfPreviewD/dxfPreviewViewBox/dxfPreviewSummary; templates/maker/task_editor.html SVG con un solo <path> e x-bind:view-box.camel (x-for dentro svg non va con Alpine; :viewBox viene abbassato dal parser). TEST: backend test_vision_dxf_preview.py (5), worker test_dxf_compare.py -k preview (5), frontend TestDxfPreviewProxy (4) + tests/js/test_dxf_preview.js. COMMIT: ebaafbe, bdb1e8c, e582df7; merge fino a 4791c34 su lavoro/daitarn-tmf-dxfcal (pushato). PROVA: TMF daitarn riavviato, headless Edge task 8 ricetta 3: MEVIS chiuso → path d 488 car., viewBox 3.68 -21.997 13.53 6.33, «12,30 × 5,10 mm · 1 entità»; PR165 profilo → «15,38 × 5,20 mm · 12 entità»; DXF completo Sider (R12 rotto) → errore rosso «DXF illeggibile: ValueError: Invalid handle 0.»
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/TieMeasureFlow#101