Files
HartOMat/plan.md
T
Hartmut 382a18fd02 feat(O): UI-Vollständigkeit + v3-Workflows + OCC-Kantenanalyse
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>
2026-03-06 23:20:55 +01:00

13 KiB
Raw Blame History

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

  • Datei: frontend/src/components/layout/Layout.tsx
  • Was: Upload-Icon + NavLink zu /upload in 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_configs Tabelle (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/false
    • POST /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 | None hinzufügen
    • PATCH-Handler: workflow_definition_id setzen wenn in body
    • OutputTypeOut soll workflow_name: str | None als 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_workflow
    • dispatch_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.
  • 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_activeupdateAssetLibrary()
    • Delete-Button → deleteAssetLibrary()
    • Leer-Zustand: "No asset libraries yet — upload a .blend file"
  • Akzeptanzkriterium: Libraries hochladen, Katalog anzeigen, löschen
  • 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)
  • 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
  • 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_modell eine CadFile zugeordnet hat (cad_files.original_name ILIKE)
      • Prüft ob cad_part_materials alle Materialien in materials-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) Queue step_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)
  • Akzeptanzkriterium: Nach Excel-Upload wird Import-Validierung automatisch gequeuet; summary liefert 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}/validation pollen (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.ts mit getImportValidation(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_attributes vorhanden: kleine Info-Karte
    • Felder: volume_cm3 (aus mesh_attributes.volume_mm3 / 1000 → "12.5 cm³"), surface_area_cm2, bounding_box ("W×H×D mm"), sharp_angle_deg (aus suggested_smooth_angle)
    • Label "Geometry" mit Ruler-Icon
  • API-Änderung: Product-API gibt cad_file.mesh_attributes zurü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 3060°)
      • 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 in mesh_attributes mergen
    • Fallback: bei OCC-Fehler gracefully {} zurückgeben
  • Akzeptanzkriterium: cad_files.mesh_attributes enthält suggested_smooth_angle nach 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:
    1. Wenn mesh_attributes.suggested_smooth_angle vorhanden: diesen Winkel statt globalem smooth_angle nutzen
    2. 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_midpoints vorhanden: KD-Tree matching → zusätzliche Kanten markieren
    3. 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
    4. Beide Funktionen nach _import_stl() aufrufen (Mode A + Mode B)
  • 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:
    @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}
    
    Skript render-worker/scripts/asset_library.py existiert 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_task und export_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_production type
  • 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.