Hartmut
95cfe0aa93
refactor: replace STL intermediary with OCC-native STEP→GLB pipeline
...
- export_step_to_gltf.py: STEP→GLB via RWGltf_CafWriter + BRepBuilderAPI_Transform
(mm→m pre-scaling, XCAFDoc_ShapeTool.GetComponents_s static method)
- Blender scripts (blender_render.py, still_render.py, turntable_render.py,
export_gltf.py, export_blend.py): import GLB instead of STL, remove _scale_mm_to_m
- step_tasks.py: add generate_gltf_production_task, remove generate_stl_cache,
replace _bbox_from_stl with _bbox_from_glb (trimesh), auto-queue geometry GLB
after thumbnail render
- render_blender.py: replace _stl_from_cache_or_convert with _glb_from_step,
remove convert_step_to_stl and export_per_part_stls
- domains/rendering/tasks.py: update render_turntable_task, export_gltf/blend tasks
to use GLB instead of STL
- cad.py: remove STL download/generate endpoints, add generate-gltf-production
- admin.py: generate-missing-stls → generate-missing-geometry-glbs
- Frontend: replace STL cache UI with GLB generate buttons, remove stl_cached field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-07 16:49:18 +01:00
Hartmut
2377cb192a
fix: smooth normals on non-indexed geometry + sync DB in gltf task
...
InlineCadViewer: STL-derived GLBs have non-indexed geometry (unique vertex
per triangle face). computeVertexNormals() on non-indexed geometry produces
per-face normals (faceted shading). Fix: mergeVertices() first to create
shared/indexed geometry, then computeVertexNormals() averages across
adjacent faces → smooth shading. Indexed Blender GLBs are unaffected.
generate_gltf_geometry_task: asyncio.run() inside a Celery worker that
already runs asyncpg causes 'Future attached to a different loop'. Replace
async _store() with sync SQLAlchemy session (matching the rest of the task).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-07 15:17:20 +01:00
Hartmut
c1e9a86996
fix(gltf): route generate_gltf_geometry_task through Blender for materials + sharp edges
...
Replace trimesh-only GLB export with Blender headless pipeline using
export_gltf.py. The viewer GLB and downloadable GLB now receive:
- Material substitution via the Schaeffler asset library (.blend)
- OCC-derived sharp edge data (sharp_edge_midpoints from mesh_attributes)
marked as Blender sharp edges before export
Material map is resolved via resolve_material_map() to convert aliases
(e.g. "Steel--Stahl" → "SCHAEFFLER_010101_Steel-Bare") before passing
to Blender. Old gltf_geometry MediaAsset is replaced on each run to
avoid stale records accumulating.
Trimesh kept as fallback if Blender binary unavailable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-07 14:10:45 +01:00
Hartmut
bfd58e3419
fix: media thumbnails, product dimensions, inline 3D viewer, GLB export
...
Bug A: Media Library thumbnails were gray because <img src> cannot send
JWT auth headers. Added useAuthBlob() hook (fetch + createObjectURL) in
MediaBrowser.tsx. Also fixed publish_asset Celery task to populate
product_id + cad_file_id on MediaAsset for thumbnail fallback resolution.
Bug B: Product dimensions now shown in Product Details card with Ruler
icon and "from CAD" label when cad_mesh_attributes.dimensions_mm exists.
Bug C: Replaced 128×128 CAD thumbnail with InlineCadViewer component.
Queries gltf_geometry MediaAssets, fetches GLB via auth fetch → blob URL
→ Three.js Canvas with OrbitControls. Falls back to thumbnail + "Load 3D
Model" button. Polling when GLB generation is in progress.
Bug D: trimesh was in [cad] optional extra but Dockerfile only installed
[dev]. Changed to pip install -e ".[dev,cad]" — trimesh now available in
backend container, GLB + Colors export works.
Also added bbox extraction (STL-first numpy parsing) in render_step_thumbnail
and admin "Re-extract CAD Metadata" bulk endpoint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-07 13:27:46 +01:00
Hartmut
5029a94608
fix: full-width content area + auto-create MediaAssets on render complete
...
- Remove max-w-* constraints from all data/table pages so content fills available width after sidebar (Billing, MediaBrowser, OrderDetail, WorkerManagement, WorkerActivity, Materials, Tenants, AssetLibrary, NewProductOrder, ProductDetail)
- Narrow form/settings pages keep their max-width (NotificationSettings, Preferences, NewOrder, Notifications)
- render_order_line_task: create MediaAsset record on render success so results immediately appear in Media Browser without requiring the retroactive import button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-07 00:17:17 +01:00
Hartmut
f5ca91ee02
feat: layout hamburger, media browser filters+previews, billing fixes
...
- Layout: mobile hamburger menu + overlay backdrop + close button; content area always full-width
- Media browser: filter chips (default still+turntable); advanced toggle for GLB/STL; thumbnail_url previews for non-image types; video hover-play for turntable
- Backend: asset_types multi-filter, thumbnail_url in MediaAssetOut, download proxy endpoint for MinIO/local files
- Admin: "Import Existing Media" button → POST /api/admin/import-media-assets
- Billing: fix invoice create 500 (MissingGreenlet — use selectinload after commit); PDF download uses axios blob instead of bare <a href> (auth header missing); fix storage.upload() accepting str|Path
- SSE task logs: task_logs.py core + router, LiveRenderLog component
- CadPreview: fix infinite loop when no gltf_geometry assets; loading screen before ThreeDViewer render
- render-worker: add trimesh layer to Dockerfile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-07 00:09:27 +01:00
Hartmut
a70cb55d01
feat(N): workflow pipeline, 3D viewer, worker management, QC tests
...
- workflow_builder.py: fix broken stubs, add render_order_line_still_task
(resolves step_path from DB instead of passing order_line_id as step_path)
- domains/rendering/tasks.py: add render_order_line_still_task,
export_gltf_for_order_line_task, export_blend_for_order_line_task,
generate_gltf_geometry_task (trimesh STL→GLB, no Blender needed)
- tasks/step_tasks.py: add generate_gltf_geometry_task for CadFile GLB export
- cad router: POST /{id}/generate-gltf-geometry endpoint (admin/PM)
- worker router: GET /celery-workers + POST /scale (docker compose subprocess)
- Dockerfile: pip install -e "[dev]" to enable pytest
- docker-compose.yml: docker socket + compose file mount on backend
- ThreeDViewer.tsx: mode toggle (geometry/production), wireframe, env presets,
download buttons (GLB + .blend)
- CadPreview.tsx: load gltf_geometry/gltf_production/blend_production assets
from MediaAsset table and pass URLs to ThreeDViewer
- ProductDetail.tsx: "View 3D" button → /cad/:id, "Generate GLB" button
- media router/service: cad_file_id filter on GET /api/media
- WorkerManagement.tsx: new page with worker status, queue depth, scale controls
- App.tsx + Layout.tsx: /workers route + sidebar link (admin/PM)
- tests: test_rendering_service.py, test_orders_service.py (backend)
- tests: WorkerActivity.test.tsx, WorkerManagement.test.tsx (frontend)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 22:56:53 +01:00
Hartmut
a18d4c23ec
feat(K): Blender Asset Library + production exports (GLB + .blend)
...
- feat(migration): 045_asset_libraries — new asset_libraries table (blend_file_path, catalog JSONB)
- feat(model): AssetLibrary SQLAlchemy model in domains/materials/models.py
- feat(api): POST/GET/PATCH/DELETE /api/asset-libraries + /upload-blend + /refresh-catalog endpoints
- feat(celery): refresh_asset_library_catalog task on thumbnail_rendering queue — runs Blender headless
- feat(blender): catalog_assets.py — extracts asset-marked materials + node_groups from .blend
- feat(blender): asset_library.py — apply_asset_library_materials + apply_asset_library_node_groups helpers
- feat(blender): export_gltf.py — STEP→STL→GLB production export with optional asset library
- feat(blender): export_blend.py — STEP→STL→.blend production export with pack_all()
- feat(frontend): api/assetLibraries.ts — full CRUD API client
- feat(frontend): AssetLibraryPanel in Admin.tsx — upload, refresh, expand catalog view
- docs: Blender asset_data marking requirement learning in LEARNINGS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 20:56:26 +01:00
Hartmut
7a1329958d
feat(J): WebSocket live-events + replace polling + fix ffmpeg turntable timeout
...
- fix(render): ffmpeg overlay=0:0 -> overlay=0:0:shortest=1 to prevent hang on finite PNG sequences
- feat(ws): add core/websocket.py ConnectionManager + Redis Pub/Sub subscriber loop
- feat(ws): add /api/ws WebSocket endpoint with JWT query-param auth in main.py
- feat(ws): emit render_complete/failed + cad_processing_complete events from step_tasks.py
- feat(ws): emit order_status_change events from orders router
- feat(ws): add beat_tasks.py broadcast_queue_status task (every 10s via Redis __broadcast__)
- feat(frontend): add useWebSocket hook with auto-reconnect (exponential backoff, 25s ping)
- feat(frontend): add WebSocketContext + WebSocketProvider wrapping App
- refactor(frontend): remove polling from WorkerActivity (was 5s/3s) + OrderDetail (was 5s)
- refactor(frontend): reduce polling in Layout (8s->60s) + NotificationCenter (15s->60s)
- docs: add ffmpeg shortest=1 + WebSocket JWT auth learnings to LEARNINGS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 20:49:34 +01:00
Hartmut
ceb0143cb6
fix: animation output types now render turntable mp4 via Celery
...
- render_order_line_task: detect is_animation flag, branch to turntable
pipeline instead of still-image pipeline
- render_blender: add render_turntable_to_file() — STL conversion,
blender turntable_render.py for frames, ffmpeg compose to mp4
- render-worker/Dockerfile: add ffmpeg package
- Fix: Turntable Video_White was producing a .jpg still instead of .mp4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 20:10:33 +01:00
Hartmut
ab3f9c734a
fix: render pipeline + multi-tenancy bugs (B-Fix-1 through B-Fix-9)
...
- Remove worker-thumbnail (no Blender, was competing on thumbnail_rendering)
- Move render_order_line_task to thumbnail_rendering queue (render-worker)
- Restore template_service.py real implementation (fix circular import shim)
- Thread tenant_id through STEP upload, Excel import, product create
- Make system tables (output_types, materials, etc.) tenant_id nullable
- Fix tenants frontend 307-redirect: use trailing slash /tenants/
- Remove Flamenco + Three.js from Admin UI (unsupported)
- Set all output_types render_backend to celery (was flamenco)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 19:34:20 +01:00
Hartmut
f19a6ccde8
feat(F-G-H-I): STL cache, invoices, import validation, notification settings
...
Phase F — STL Hash Cache:
- Migration 041: step_file_hash column on cad_files
- cache_service.py: SHA256 hash + MinIO-backed STL cache (check/store)
- render_step_thumbnail: compute+persist hash before render
- generate_stl_cache: check MinIO cache before cadquery conversion, store after
Phase G — Invoices:
- Migration 042: invoices + invoice_lines tables with RLS
- Invoice/InvoiceLine models + schemas
- billing service: generate_invoice_number (INV-YYYY-NNNN), create/list/get/delete/PDF
- WeasyPrint PDF generation; backend Dockerfile + pyproject.toml deps
- invoice_router with 6 endpoints; registered in main.py
- frontend: Billing.tsx page + api/billing.ts; route + nav link
Phase H — Import Sanity Check:
- Migration 043: import_validations table
- ImportValidation model + schemas
- run_sanity_check: material fuzzy-match (cutoff=0.8), STEP availability, duplicate detection
- validate_excel_import Celery task (queue: step_processing)
- uploads.py: create ImportValidation on /excel, fire task, expose GET /validations/{id}
- frontend: Upload.tsx polling ValidationDialog with Ampel status indicators
Phase I — Notification Settings:
- Migration 044: notification_configs table (user×event×channel toggles)
- NotificationConfig model + seeds (in_app=true, email=false)
- get/upsert/reset config endpoints on /notifications/config
- frontend: NotificationSettings.tsx page + api/notifications.ts extensions
Infrastructure:
- docker-compose.yml: add worker-thumbnail service (concurrency=1, Q=thumbnail_rendering)
- Fix Dockerfile: libgdk-pixbuf-2.0-0 (correct Debian bookworm package name)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 18:05:01 +01:00
Hartmut
716451ff76
feat(D): OCC mesh attribute extraction + Blender smooth shading integration
...
Migration 039: cad_files.mesh_attributes JSONB column.
domains/products/tasks.py: extract_mesh_attributes Celery task using pythonOCC.
still_render.py + turntable_render.py: _apply_mesh_attributes() sets auto-smooth
based on curved_ratio and topology threshold from OCC analysis.
render_blender.py: passes --mesh-attributes JSON arg to Blender subprocess.
render_still_task: loads mesh_attributes from DB and passes to renderer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 17:07:55 +01:00
Hartmut
9d1a820295
refactor(A2): replace blender-renderer HTTP service with render-worker Celery container
...
- Create render-worker/ with Dockerfile (Ubuntu + cadquery + Blender via host mount)
- Add render-worker/check_version.py: verifies Blender >= 5.0.1 at startup, Exit 1 on failure
- Add render-worker/scripts/: blender_render.py, still_render.py, turntable_render.py
- Create backend/app/services/render_blender.py: direct subprocess rendering
- convert_step_to_stl() and export_per_part_stls() using cadquery
- render_still(): STEP → STL → PNG via Blender subprocess
- is_blender_available(): detects BLENDER_BIN env for render-worker context
- Create backend/app/domains/rendering/tasks.py: render_still_task + render_turntable_task
- Update step_processor.py: use subprocess path when BLENDER_BIN env is set (render-worker)
- Update step_tasks.py: generate_stl_cache uses direct cadquery instead of HTTP
- Remove blender-renderer and threejs-renderer from docker-compose.yml
- Replace worker-thumbnail with render-worker (Ubuntu + cadquery + Blender mount)
- Remove Docker SDK from backend Dockerfile (was only for flamenco scaling)
- Update .env.example: BLENDER_VERSION=5.0.1 documented
- Update celery_app.py: include domains.rendering.tasks in autodiscover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 15:48:46 +01:00
Hartmut
1d6864fb64
refactor(A1): remove Flamenco, simplify render pipeline to Celery-only
...
- Remove flamenco-manager and flamenco-worker from docker-compose.yml
- Delete flamenco_client.py, flamenco_tasks.py, docker_scaler.py
- Simplify render_dispatcher.py to Celery-only (removes ~300 lines)
- Remove Flamenco beat schedule from celery_app.py
- Clean admin.py: remove flamenco settings, endpoints, threejs validation
- Clean orders.py cancel-render: Celery revoke only
- Clean worker.py: remove flamenco_job_id from activity response
- Migration 032: cancel lingering flamenco jobs, remove flamenco settings
- PLAN.md: mark all decisions confirmed, status IN UMSETZUNG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-03-06 15:38:37 +01:00
Hartmut
552922eb8a
chore: remove build artifacts from tracking, extend .gitignore
2026-03-05 22:13:21 +01:00
Hartmut
bce762a783
feat: initial commit
2026-03-05 22:12:38 +01:00