feat: GPU rendering + material matching + perf improvements
- GPU: fix Cycles device activation order — set compute_device_type BEFORE engine init, re-set AFTER open_mainfile wipes preferences - GPU: remove _mark_sharp_and_seams edit-mode loop (redundant with Blender 5.0 shade_smooth_by_angle), saves ~200s/render on 175 parts - Material: fix _AFN suffix mismatch — build AF-stripped mat_map keys and add prefix fallback in _apply_material_library (blender_render.py) - Material: production GLB now uses get_material_library_path() which checks active AssetLibrary instead of empty legacy system setting - Admin: RenderTemplateTable multi-select output types (M2M frontend) - Admin: MaterialLibraryPanel replaced with link to Asset Libraries - UX: move Toaster to top-left to avoid dispatch button overlap - SQLAlchemy: add .unique() to all RenderTemplate M2M collection queries - Logging: flush=True on all Blender progress prints, stdout reconfigure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+36
-32
@@ -1,4 +1,4 @@
|
||||
# Review Report: Phase V2-Cleanup + Phase V3
|
||||
# Review Report: Optimized Material Substitution Algorithm
|
||||
Datum: 2026-03-07
|
||||
|
||||
## Ergebnis: ⚠️ Kleinigkeiten
|
||||
@@ -7,49 +7,53 @@ Datum: 2026-03-07
|
||||
|
||||
## Gefundene Probleme
|
||||
|
||||
### [backend/app/domains/media/router.py] Auth fehlt auf GET /{asset_id} und DELETE-Endpunkten
|
||||
**Schwere**: Mittel
|
||||
**Empfehlung**: `get_asset`, `archive_asset`, `delete_asset_permanent` haben kein `get_current_user` Dependency. War nicht im Plan-Scope, sollte aber in einem Folge-Task ergänzt werden. Aktuell könnte jede Person mit einer Asset-UUID das Asset abrufen oder löschen — allerdings sind UUIDs nicht ratbar (V2-C2 ist damit in der Praxis weitgehend erfüllt, aber formal unvollständig).
|
||||
### [products.py:510] Prefix-Matching ohne Mindestlänge-Guard
|
||||
|
||||
### [backend/app/domains/rendering/tasks.py] asyncio.get_event_loop() in Celery-Kontext
|
||||
**Schwere**: Gering
|
||||
**Empfehlung**: `asyncio.get_event_loop().run_until_complete()` in `_update_workflow_run_status()` ist ein Anti-Pattern in neueren Python-Versionen (3.10+). Deprecation-Warning möglich wenn kein laufender Loop existiert. Besser: `asyncio.run()` oder sync SQLAlchemy-Session wie in `dispatch_service.py`. Kein Blocker.
|
||||
|
||||
### [render-worker/scripts/export_gltf.py] O(N×M) Proximity-Loop
|
||||
**Schwere**: Gering
|
||||
**Empfehlung**: `mark_sharp_edges_by_proximity()` iteriert alle Blender-Mesh-Edges gegen alle OCC-Kantenmittelpunkte. Bei großen STEP-Dateien (10k+ Edges, 500+ OCC-Hinweispunkte) kann das spürbar langsam sein. Nicht kritisch für aktuelle Produktgrößen. Notiz für spätere Optimierung (z.B. KD-Tree mit `scipy.spatial`).
|
||||
Der Prefix-Fallback in `build_materials_from_excel` prüft `cad_norm.startswith(excel_norm)` ohne Mindestlänge für `excel_norm`:
|
||||
|
||||
```python
|
||||
if excel_norm and cad_norm and (
|
||||
cad_norm.startswith(excel_norm) or excel_norm.startswith(cad_norm)
|
||||
):
|
||||
```
|
||||
|
||||
Wenn ein Excel-Eintrag nach Normalisierung sehr kurz wird (z.B. `"f"` aus `f-12345678.prt`), trifft der Präfix-Check auf fast alle CAD-Namen die mit `f_` beginnen. Schaeffler-Teilenamen sind zwar praktisch immer lang genug, aber das Risiko eines Fehlmatches bei atypischen Einträgen existiert.
|
||||
|
||||
**Empfehlung**: Guard hinzufügen: `len(excel_norm) >= 5 and len(cad_norm) >= 5`.
|
||||
|
||||
---
|
||||
|
||||
### [export_gltf.py:122] Prefix-Fallback iteriert über unsortierte dict-Keys
|
||||
|
||||
### [backend/app/services/step_processor.py] bbox-Extraktion ohne Shape-Guard (OCC-Pfad)
|
||||
**Schwere**: Gering
|
||||
**Empfehlung**: `brepbndlib.Add(shape, bbox)` kann bei degenerierten STEP-Geometrien eine leere BBox zurückgeben. Ein Guard `if not bbox.IsVoid():` vor dem `bbox.Get()` wäre robuster. Dieser OCC-Pfad ist in der aktuellen Container-Konfiguration nicht aktiv (kein OCC installiert), aber beim nächsten Container-Upgrade relevant.
|
||||
|
||||
```python
|
||||
for key, val in mat_map_lower.items():
|
||||
if lower_base.startswith(key) or key.startswith(lower_base):
|
||||
mat_name = val
|
||||
break
|
||||
```
|
||||
|
||||
`mat_map_lower` hat keine garantierte Sortierung nach Schlüssellänge. Wenn ein kurzer Key (`"ring"`) und ein langer Key (`"ring_inner_seal"`) beide die Präfix-Bedingung erfüllen, gewinnt der erste in dict-Reihenfolge — nicht zwangsläufig der spezifischste Match.
|
||||
|
||||
**Empfehlung**: Keys nach Länge absteigend sortieren: `sorted(mat_map_lower.items(), key=lambda x: len(x[0]), reverse=True)` — längster Match gewinnt = spezifischster Match.
|
||||
|
||||
---
|
||||
|
||||
## Positiv aufgefallen
|
||||
|
||||
- **V2-C1 (asset_type-Klassifizierung)**: Korrektur in beiden Stellen (`admin.py` + `step_tasks.py`) konsistent auf Extension-Basis umgestellt.
|
||||
- **V2-C2 (Tenant Isolation)**: `get_current_user` Dependency korrekt auf `list_assets`, `download_asset`, `zip_download` ergänzt. Pattern konsistent mit anderen Routers.
|
||||
- **V2-C3 (storage_key Normalisierung)**: `_normalize_key()` Helper in `admin.py` sauber definiert. In `step_tasks.py` inline normalisiert.
|
||||
- **V2-C4 (Cache-Control)**: Header auf beiden Endpoints korrekt gesetzt.
|
||||
- **V3-A1 (OCC Bounding Box in step_processor.py)**: Code korrekt, aber nur wirksam wenn OCC installiert ist — in der Produktionskonfiguration nicht aktiv.
|
||||
- **V3-A2 (Frontend Dimensionen)**: `cad_mesh_attributes` im `ProductOut`-Schema sauber ergänzt. `selectinload(Product.cad_file)` war bereits in allen Queries vorhanden — kein N+1-Problem.
|
||||
- **V3-B (Mark Sharp Edges)**: Proximity-basiertes Marking mit konfigurierbarem Threshold (1mm default) ist ein pragmatischer Ansatz.
|
||||
- **V3-C1/C2/C3 (Workflow-Integration)**: `still_with_exports` korrekt ergänzt, Turntable-Params werden zur Laufzeit aufgelöst, WorkflowRun-Status wird nach Task-Abschluss aktualisiert.
|
||||
- **bbox via STL (nachträglicher Fix)**: `_bbox_from_stl()` mit numpy min/max ist die effizienteste Methode — nutzt bereits gecachte STL-Dateien, kein STEP-Re-Parse nötig. Cadquery-Fallback für Dateien ohne STL-Cache ist korrekt implementiert.
|
||||
- **`render_step_thumbnail` Patch**: Nur ausgeführt wenn `dimensions_mm` noch nicht gesetzt — vermeidet redundante Berechnungen bei Re-Renders.
|
||||
- **TypeScript**: `tsc --noEmit` läuft ohne Fehler. Neue `cad_mesh_attributes`-Interface-Felder korrekt typisiert.
|
||||
- **LEARNINGS.md**: 5 neue Learnings mit korrektem Format eingetragen.
|
||||
- **Task 1 korrekt und robust**: `while prev != base_name` Loop für nested Suffixe terminiert sicher und deckt `_AF0_AF1`-Fälle ab.
|
||||
- **`_re.IGNORECASE` korrekt gesetzt** in `export_gltf.py` — deckt `_AF0` wie auch `_af0`.
|
||||
- **`_normalize_part_token_name` Reihenfolge stimmt**: `_af\d+` wird VOR Hyphen→Underscore-Konvertierung gestrippt, Regex funktioniert zuverlässig.
|
||||
- **Hash-Suffix-Stripping `\d{4,}`**: Mindestlänge 4 verhindert False-Positives bei legitimen kurzen Nummern in Teilenamen.
|
||||
- **`print()` in Blender-Script korrekt**: Blender-Scripts laufen als Subprocess, stdout wird vom Caller geloggt — `logging` wäre hier falsch.
|
||||
- **Kein DB-Schema geändert**: Keine Migration nötig, korrekt erkannt und ausgelassen.
|
||||
- **Tuple-Erweiterung auf 4 Elemente**: `excel_entries` korrekt auf `(tokens, raw, material, excel_norm)` erweitert, keine alten Stellen übersehen.
|
||||
|
||||
---
|
||||
|
||||
## Empfehlung
|
||||
|
||||
Freigabe mit folgenden Nacharbeiten im nächsten Cleanup-Cycle:
|
||||
|
||||
1. Auth auf `get_asset` + `archive_asset` + `delete_asset_permanent` in `media/router.py` ergänzen
|
||||
2. `_update_workflow_run_status()` auf `asyncio.run()` oder sync-SQLAlchemy umstellen
|
||||
3. `if not bbox.IsVoid():` Guard in `step_processor.py` vor `bbox.Get()` einfügen
|
||||
|
||||
Keiner dieser Punkte blockiert den aktuellen Stand — alle Core-Features sind korrekt implementiert.
|
||||
|
||||
Review abgeschlossen. Ergebnis: ⚠️
|
||||
Zwei geringe Probleme (Mindestlänge-Guard + Sortierung nach Key-Länge). Beide sind je eine Zeile Fix und verhindern Fehlmatches bei atypischen Eingaben. Können direkt inline gepatcht werden, kein erneutes Review nötig.
|
||||
|
||||
Reference in New Issue
Block a user