382a18fd02
Backend:
- Phase I: notification_configs router (GET/PUT/{event}/{channel}/POST reset)
war bereits in notifications.py — add-alias endpoint in uploads.py ergänzt
- OutputType schema: workflow_definition_id + workflow_name fields;
PATCH unterstützt Workflow-Zuweisung; _enrich_workflow_names() batch query
- Dispatch-Integration: orders.py dispatch_renders() → dispatch_render_with_workflow()
mit Legacy-Fallback; neues Logging
- uploads.py: POST /validations/{id}/add-alias für Material-Lücken
Pipeline:
- step_processor.py: extract_mesh_edge_data() via OCC — berechnet Dihedralwinkel
aller Kanten, liefert suggested_smooth_angle + sharp_edge_midpoints
Integriert in extract_cad_metadata() und process_cad_file()
- domains/rendering/tasks.py: apply_asset_library_materials_task (K3),
export_gltf_for_order_line_task → Blender export_gltf.py (K4),
export_blend_for_order_line_task → export_blend.py fix (K5)
- render-worker/scripts/still_render.py: _mark_sharp_and_seams() mit
OCC midpoint KD-tree matching + UV-Seam-Markierung
- render-worker/scripts/blender_render.py: identische Funktion + mesh_attributes parsing
Frontend:
- Layout.tsx: Upload-Link in Sidebar (alle User); Asset Libraries Link (admin/PM)
- App.tsx: /asset-libraries Route
- AssetLibrary.tsx: neue Seite (Upload, Catalog-Anzeige, Refresh, Toggle, Delete)
- OutputTypeTable.tsx: Workflow-Dropdown + Legacy/Workflow Badge
- ProductDetail.tsx: Geometry-Karte (Volumen, Surface, BBox, Sharp-Winkel)
- api/outputTypes.ts + api/products.ts: neue Felder
- api/imports.ts: ImportValidation API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
13 KiB
Plan: UI-Vollständigkeit + Workflows — Phase O
Ziel: Alle implementierten Backend-Features im UI zugänglich machen + v3-Workflows vollständig verdrahten.
Betroffene Dateien
| Datei | Änderung |
|---|---|
frontend/src/components/layout/Layout.tsx |
Upload-Link hinzufügen |
frontend/src/pages/Admin.tsx |
OutputType-Tabelle: Workflow-Dropdown |
frontend/src/pages/AssetLibrary.tsx |
NEU: Asset Library Management UI |
frontend/src/api/asset_libraries.ts |
NEU: API-Client |
frontend/src/pages/ProductDetail.tsx |
Mesh-Attribute-Anzeige |
frontend/src/pages/Upload.tsx |
Sanity-Check-Dialog nach Import |
frontend/src/api/imports.ts |
NEU: import_validation API |
frontend/src/App.tsx |
Route /asset-libraries |
backend/app/api/routers/notification_configs.py |
NEU: notification_configs CRUD |
backend/app/main.py |
notification_configs router registrieren |
backend/app/api/routers/orders.py |
dispatch_renders → dispatch_render_with_workflow |
backend/app/api/routers/output_types.py |
workflow_definition_id im PATCH |
backend/app/schemas/output_type.py |
workflow_definition_id im Schema |
backend/app/domains/rendering/tasks.py |
K3: apply_asset_library_materials_task |
backend/app/tasks/step_tasks.py |
OCC sharp edge extraction in render_step_thumbnail |
render-worker/scripts/still_render.py |
mark_sharp / UV seams support |
render-worker/scripts/blender_render.py |
mark_sharp / UV seams support |
backend/app/services/step_processor.py |
extract_mesh_edge_data() für sharp edges |
Tasks
Task 1: Upload-Link in Sidebar [QUICK WIN]
- Datei:
frontend/src/components/layout/Layout.tsx - Was:
Upload-Icon + NavLink zu/uploadin der Sidebar für alle eingeloggten User - Akzeptanzkriterium: Upload-Link sichtbar in Sidebar
Task 2: notification_configs Backend-Router [Phase I]
- Datei:
backend/app/api/routers/notification_configs.py(NEU),backend/app/main.py - Was: REST-Endpoints für
notification_configsTabelle (044 bereits migriert):GET /api/notification-configs— gibt configs für aktuellen User zurück (mit Defaults falls keine Zeilen)PUT /api/notification-configs/{event_type}/{channel}— setzt enabled=true/falsePOST /api/notification-configs/reset— löscht alle configs des Users → Defaults gelten wieder- Response:
[{event_type, channel, enabled}] - Auth:
get_current_user(jeder kann seine eigenen Configs verwalten)
- Akzeptanzkriterium: NotificationSettings.tsx zeigt Toggle-Matrix und speichert korrekt
Task 3: OutputType → WorkflowDefinition — Schema + API
- Datei:
backend/app/schemas/output_type.py,backend/app/api/routers/output_types.py - Was:
OutputTypeOut+OutputTypePatch:workflow_definition_id: uuid.UUID | Nonehinzufügen- PATCH-Handler:
workflow_definition_idsetzen wenn in body OutputTypeOutsollworkflow_name: str | Noneals convenience field enthalten
- Akzeptanzkriterium:
PATCH /api/output-types/{id}mit{"workflow_definition_id": "..."}funktioniert
Task 4: Workflow-Dispatch Integration
- Datei:
backend/app/api/routers/orders.py - Was: In
dispatch_renders()(Zeile 910):- Statt
dispatch_order_line_render.delay(str(line.id))aufrufen: from app.domains.rendering.dispatch_service import dispatch_render_with_workflowdispatch_render_with_workflow(str(line.id))aufrufen- Das dispatch_service lädt OutputType.workflow_definition_id und nutzt Celery Canvas falls verknüpft; fällt auf Legacy zurück wenn nicht.
- Statt
- Akzeptanzkriterium: Dispatch nutzt neuen Pfad; Legacy-Fallback bleibt erhalten
Task 5: Asset Library API-Client (Frontend)
- Datei:
frontend/src/api/asset_libraries.ts(NEU) - Was:
export interface AssetLibrary { id, name, description, original_filename, catalog: {materials: string[], node_groups: string[]}, is_active, created_at } export async function listAssetLibraries(): Promise<AssetLibrary[]> export async function uploadAssetLibrary(name: string, file: File, description?: string): Promise<AssetLibrary> export async function refreshLibraryCatalog(id: string): Promise<AssetLibrary> export async function deleteAssetLibrary(id: string): Promise<void> export async function updateAssetLibrary(id: string, data: Partial<AssetLibrary>): Promise<AssetLibrary> - Akzeptanzkriterium: TypeScript kompiliert fehlerfrei
Task 6: Asset Library Management Page (K2)
- Datei:
frontend/src/pages/AssetLibrary.tsx(NEU) - Was: Seite
/asset-libraries(admin/PM):- Liste der Asset Libraries als Karten: Name, Filename, Badge-Grid mit Materialien/Node-Groups aus
catalog - Upload-Button: Datei-Input für
.blend+ Name-Feld →uploadAssetLibrary() - "Refresh Catalog" Button je Library →
refreshLibraryCatalog(id)→ Toast - Toggle
is_active→updateAssetLibrary() - Delete-Button →
deleteAssetLibrary() - Leer-Zustand: "No asset libraries yet — upload a .blend file"
- Liste der Asset Libraries als Karten: Name, Filename, Badge-Grid mit Materialien/Node-Groups aus
- Akzeptanzkriterium: Libraries hochladen, Katalog anzeigen, löschen
Task 7: Asset Library Route + Sidebar-Link
- Datei:
frontend/src/App.tsx,frontend/src/components/layout/Layout.tsx - Was:
- App.tsx: Route
/asset-libraries→<AssetLibraryPage />(AdminRoute) - Layout.tsx: Sidebar-Link "Asset Libraries" mit
Library-Icon (admin/PM)
- App.tsx: Route
- Abhängigkeiten: Task 6
Task 8: OutputType Workflow-Dropdown (Frontend)
- Datei:
frontend/src/pages/Admin.tsx(OutputTypeTable-Bereich) - Was: In der OutputType-Tabelle eine neue Spalte "Workflow":
- Dropdown mit allen WorkflowDefinitions (aus
GET /api/workflows) + "— None —" - Bei Änderung:
PATCH /api/output-types/{id}mit{workflow_definition_id: ...} - Wenn kein Workflow: zeige "Legacy" Badge; wenn Workflow: zeige Workflow-Name als grünes Badge
- Dropdown mit allen WorkflowDefinitions (aus
- Akzeptanzkriterium: Workflow kann pro OutputType zugewiesen werden
Task 9: Excel Sanity-Check Backend (Phase H)
- Datei:
backend/app/domains/imports/sanity_check.py(NEU),backend/app/domains/imports/router.py - Was:
- Sync-Funktion
run_sanity_check(import_validation_id: str):- Lädt ImportValidation-Record
- Iteriert über
rows(ParsedRows aus Excel) - Für jede Zeile: prüft ob
name_cad_modelleine CadFile zugeordnet hat (cad_files.original_name ILIKE) - Prüft ob
cad_part_materialsalle Materialien inmaterials-Tabelle (via Alias-Lookup) auflösbar sind - Erstellt
summary: {total_rows, rows_with_cad, rows_without_cad, material_gaps: [{product, missing_material}]} - Status → 'completed'
- Celery-Task
validate_excel_import_task(import_validation_id)Queuestep_processing - Endpoint
GET /api/imports/{id}/validation— gibt ImportValidation zurück - Endpoint
POST /api/imports/{id}/add-alias— schnell einen Alias hinzufügen (part_name → material) - ImportValidation DB-Zugriif: sync SQLAlchemy (Celery-kompatibel)
- Sync-Funktion
- Akzeptanzkriterium: Nach Excel-Upload wird Import-Validierung automatisch gequeuet;
summaryliefert Material-Lücken
Task 10: Upload.tsx — Sanity-Check-Dialog (Phase H)
- Datei:
frontend/src/pages/Upload.tsx - Was: Nach erfolgreichem Excel-Upload:
GET /api/imports/{id}/validationpollen (alle 3s, max 30s)- Wenn status='completed': Ampel-Dialog anzeigen:
- Grün-Badge: "X Produkte mit STEP-Datei"
- Gelb-Badge: "Y Produkte ohne STEP-Datei"
- Rote Liste: Material-Lücken (Part-Name → fehlendes Material, mit "Add Alias" Button)
- "Proceed" Button schließt Dialog
- Import API erweitern:
api/imports.tsmitgetImportValidation(id),addMaterialAlias()
- Akzeptanzkriterium: Nach Upload erscheint Dialog mit Produktions-Readiness
Task 11: Mesh-Attribute Anzeige in ProductDetail (Phase D)
- Datei:
frontend/src/pages/ProductDetail.tsx - Was: Im CAD-File-Bereich, nach dem Status-Badge:
- Wenn
product.cad_file.mesh_attributesvorhanden: kleine Info-Karte - Felder:
volume_cm3(ausmesh_attributes.volume_mm3 / 1000→ "12.5 cm³"),surface_area_cm2,bounding_box("W×H×D mm"),sharp_angle_deg(aussuggested_smooth_angle) - Label "Geometry" mit
Ruler-Icon
- Wenn
- API-Änderung: Product-API gibt
cad_file.mesh_attributeszurück (prüfen ob vorhanden) - Akzeptanzkriterium: Volumen, Oberfläche, BBox in ProductDetail sichtbar (wenn vorhanden)
Task 12: OCC Edge-Analyse → mesh_attributes (Sharp/Seam)
- Datei:
backend/app/services/step_processor.py - Was: Neue Funktion
extract_mesh_edge_data(step_path: str) -> dict:- Öffnet STEP via OCC
- Iteriert über alle Faces und deren Edges
- Berechnet Winkel zwischen adjazenten Faces per Edge (Dihedralwinkel)
- Sammelt:
suggested_smooth_angle: Median-Winkel aller Kanten wo Winkel > 5° (typisch 30–60°)has_mechanical_edges: bool (True wenn mehrere Kanten mit Winkel > 60° → Lagerkante)sharp_edge_midpoints: Liste von[x,y,z]mm-Koordinaten der scharfen Kanten-Mittelpunkte (max 500 Stück, für Winkel > 45°)
- Integriert in
extract_cad_metadata(): nach_extract_step_objects()aufrufen, Ergebnis inmesh_attributesmergen - Fallback: bei OCC-Fehler gracefully
{}zurückgeben
- Akzeptanzkriterium:
cad_files.mesh_attributesenthältsuggested_smooth_anglenach Verarbeitung
Task 13: Blender-Scripts — mark_sharp + UV-Seams
- Dateien:
render-worker/scripts/still_render.py,render-worker/scripts/blender_render.py - Was: Nach STL-Import, vor dem Render:
- Wenn
mesh_attributes.suggested_smooth_anglevorhanden: diesen Winkel statt globalemsmooth_anglenutzen - Neue Funktion
_mark_sharp_edges(obj, smooth_angle_deg, sharp_edge_midpoints=None):- Setzt
obj.data.auto_smooth_angle = math.radians(smooth_angle_deg) - Wählt Kanten aus:
bpy.ops.mesh.edges_select_sharp(sharpness=math.radians(smooth_angle_deg)) - Ruft
bpy.ops.mesh.mark_sharp()auf - Wenn
sharp_edge_midpointsvorhanden: KD-Tree matching → zusätzliche Kanten markieren
- Setzt
- Neue Funktion
_create_uv_seams_from_sharps(obj):- Startet Edit-Mode
- Selektiert alle Sharp-Kanten:
[e for e in mesh.edges if e.use_edge_sharp] - Markiert diese als Seams:
edge.use_seam = True - Ruft
bpy.ops.uv.smart_project(angle_limit=math.radians(smooth_angle_deg))auf
- Beide Funktionen nach
_import_stl()aufrufen (Mode A + Mode B)
- Wenn
- Akzeptanzkriterium: Gerenderte Bilder zeigen korrekte Kanten für Lager (30° Winkel scharf sichtbar)
Task 14: K3 — apply_asset_library_materials_task
- Datei:
backend/app/domains/rendering/tasks.py - Was: Neuer Celery-Task:
Skript
@celery_app.task(name="...apply_asset_library_materials_task", queue="thumbnail_rendering") def apply_asset_library_materials_task(order_line_id: str, asset_library_id: str) -> dict: # Lädt OrderLine, CadFile, AssetLibrary # Prüft ob asset_library.blend_file_path existiert # Ruft Blender subprocess auf mit asset_library.py: # blender --background --python asset_library.py -- --stl_path X --asset_library_blend Y --material_map '{...}' # Returns {'status': 'applied', 'materials_count': N}render-worker/scripts/asset_library.pyexistiert bereits. - Akzeptanzkriterium: Task läuft ohne Fehler wenn Blender verfügbar
Task 15: K4/K5 — export_gltf + export_blend via Blender
- Datei:
backend/app/domains/rendering/tasks.py - Was:
export_gltf_for_order_line_taskundexport_blend_for_order_line_tasküberarbeiten:- Statt trimesh: Blender subprocess mit
export_gltf.py/export_blend.py - Asset Library path aus LinkedAssetLibrary (via OutputType) übergeben falls vorhanden
- GLB → MinIO
production-exports/{cad_file_id}/{order_line_id}.glb - .blend → MinIO
production-exports/{cad_file_id}/{order_line_id}.blend - MediaAsset erstellen mit
gltf_production/blend_productiontype
- Statt trimesh: Blender subprocess mit
- Akzeptanzkriterium: Export-Tasks produzieren GLB/BLEND-Dateien in MinIO
Abhängigkeiten
Sofort (parallel):
Task 1 (Upload Link)
Task 2 (Notification Config Backend)
Task 3 (OutputType Schema)
Task 5 (Asset Library API)
Task 9 (Sanity Check Backend)
Task 12 (OCC Edge Analyse)
Nach Task 3:
Task 4 (Dispatch Integration)
Task 8 (OutputType Workflow Dropdown)
Nach Task 5+6:
Task 6 (Asset Library Page) — braucht Task 5
Task 7 (Route + Sidebar) — braucht Task 6
Nach Task 9:
Task 10 (Upload Sanity Dialog)
Nach Task 11:
Task 11 (Mesh Display) — unabhängig
Nach Task 12:
Task 13 (Blender Scripts)
Nach Task 14:
Task 15 (K4/K5 Exports)
Migrations-Check
Alle benötigten Migrationen existieren bereits:
- 043: import_validations ✅
- 044: notification_configs ✅
- 045: asset_libraries ✅
Keine neue Migration nötig.