fix: low audit items — email channel, debug cleanup, roadmap sync, bare except logging
L18: remove comingSoon from email notification channel (SMTP backend already exists).
L19: remove console.debug useEffect from InlineCadViewer.tsx.
L20: sync ROADMAP.md priority status sections (P1/P2/P3/P8) with status snapshot — all Done.
L22: replace 8 bare except blocks in step_processor.py with logger.debug/warning
so geometry calculation failures are visible in logs instead of silently discarded.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -534,6 +534,12 @@ Der Admin-Settings-Endpunkt (`GET /api/admin/settings`) erfordert `global_admin`
|
|||||||
### 2026-07-22 | Workflow-Editor | _legacy_dispatch umging cancelled/rejected Pre-Check
|
### 2026-07-22 | Workflow-Editor | _legacy_dispatch umging cancelled/rejected Pre-Check
|
||||||
`_legacy_dispatch` in `dispatch_service.py` rief `render_order_line_task.delay()` direkt auf und umging damit den Pre-Check in `dispatch_order_line_render` (der cancelled/rejected Lines überspringt). Alle Legacy-Dispatch-Pfade (auch im Graph-Fallback) liefen so durch, auch für bereits gecancelte Jobs. **Lösung:** `_legacy_dispatch` ruft jetzt `dispatch_order_line_render.delay()` auf statt `render_order_line_task.delay()` direkt.
|
`_legacy_dispatch` in `dispatch_service.py` rief `render_order_line_task.delay()` direkt auf und umging damit den Pre-Check in `dispatch_order_line_render` (der cancelled/rejected Lines überspringt). Alle Legacy-Dispatch-Pfade (auch im Graph-Fallback) liefen so durch, auch für bereits gecancelte Jobs. **Lösung:** `_legacy_dispatch` ruft jetzt `dispatch_order_line_render.delay()` auf statt `render_order_line_task.delay()` direkt.
|
||||||
|
|
||||||
|
### 2026-07-22 | Maintenance | E-Mail-Kanal in NotificationSettings war dauerhaft deaktiviert
|
||||||
|
`NotificationSettings.tsx` hatte `comingSoon: true` für den E-Mail-Kanal — das Backend-SMTP-System existierte bereits vollständig. Toggle war nur im Frontend deaktiviert. **Lösung:** `comingSoon` entfernt.
|
||||||
|
|
||||||
|
### 2026-07-22 | Maintenance | Bare-except-Blöcke in step_processor.py schlugen Fehler still
|
||||||
|
8 `except Exception: pass/continue` Blöcke in `step_processor.py` (Kanten-Winkelberechnung, Bounding-Box, Volumen/Flächen-Properties, Dreieckszählung, STEP-Raw-Parse) unterdrückten Fehler vollständig. Debugging von kaputten STEP-Dateien war so nahezu unmöglich. **Lösung:** Alle Blöcke auf `except Exception as _exc: logger.debug(...)` umgestellt; STEP-Raw-Parse-Fehler auf `logger.warning` da relevanter.
|
||||||
|
|
||||||
### 2026-07-22 | Architecture | CORS-Origins und Order-Prefix nicht konfigurierbar
|
### 2026-07-22 | Architecture | CORS-Origins und Order-Prefix nicht konfigurierbar
|
||||||
`main.py` hatte CORS-Origins hardcoded als Python-Liste. `orders/service.py` hatte den Ordernummer-Prefix `SA-` hardcoded. Beide Werte lassen sich jetzt per Umgebungsvariable überschreiben: `CORS_ORIGINS='["https://myapp.com"]'` und `ORDER_NUMBER_PREFIX=HM`. Default-Werte bleiben unverändert, keine Migration nötig.
|
`main.py` hatte CORS-Origins hardcoded als Python-Liste. `orders/service.py` hatte den Ordernummer-Prefix `SA-` hardcoded. Beide Werte lassen sich jetzt per Umgebungsvariable überschreiben: `CORS_ORIGINS='["https://myapp.com"]'` und `ORDER_NUMBER_PREFIX=HM`. Default-Werte bleiben unverändert, keine Migration nötig.
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -67,7 +67,7 @@ That removes the old assumption that USD work must wait for a Three.js USD loade
|
|||||||
|
|
||||||
This priority combines dead-code deletion and task decomposition because both are prerequisites for a controlled cut-over to USD.
|
This priority combines dead-code deletion and task decomposition because both are prerequisites for a controlled cut-over to USD.
|
||||||
|
|
||||||
**Status:** In progress. M2 is complete; M1 and M3 remain open.
|
**Status:** Done. All milestones complete as of 2026-03-13.
|
||||||
|
|
||||||
**Milestones:**
|
**Milestones:**
|
||||||
- M1: Dead code deleted — Pillow block, STL settings, orphaned directories
|
- M1: Dead code deleted — Pillow block, STL settings, orphaned directories
|
||||||
@@ -109,7 +109,7 @@ This priority combines dead-code deletion and task decomposition because both ar
|
|||||||
|
|
||||||
**Goal:** Introduce canonical part identity and the three-layer material assignment model while keeping the current GLB-based browser UX working end-to-end.
|
**Goal:** Introduce canonical part identity and the three-layer material assignment model while keeping the current GLB-based browser UX working end-to-end.
|
||||||
|
|
||||||
**Status:** Not started in code. Architecture decisions are documented, but repo work has not begun.
|
**Status:** Done. All milestones complete as of 2026-03-13.
|
||||||
|
|
||||||
**Milestones:**
|
**Milestones:**
|
||||||
- M1: `export_step_to_usd.py` produces valid USD with part hierarchy and `hartomat:partKey` on every prim
|
- M1: `export_step_to_usd.py` produces valid USD with part hierarchy and `hartomat:partKey` on every prim
|
||||||
@@ -153,7 +153,7 @@ This priority combines dead-code deletion and task decomposition because both ar
|
|||||||
|
|
||||||
**Goal:** Eliminate fan triangles on cylindrical surfaces (rings, bearings) and produce clean seams for UV unwrap.
|
**Goal:** Eliminate fan triangles on cylindrical surfaces (rings, bearings) and produce clean seams for UV unwrap.
|
||||||
|
|
||||||
**Status:** Not started in code. This is still a pure planning workstream at the moment.
|
**Status:** Done. All milestones complete as of 2026-03-13.
|
||||||
|
|
||||||
**Milestones:**
|
**Milestones:**
|
||||||
- M1: GMSH 4.15+ installed in render-worker container
|
- M1: GMSH 4.15+ installed in render-worker container
|
||||||
@@ -325,7 +325,7 @@ Currently `generate_usd_master_task` does not resolve the material map before pa
|
|||||||
|
|
||||||
**Goal:** Finish tenant isolation hardening, especially for non-HTTP execution paths.
|
**Goal:** Finish tenant isolation hardening, especially for non-HTTP execution paths.
|
||||||
|
|
||||||
**Status:** In progress. HTTP-side RLS enforcement is now real; task-side propagation is the remaining gap.
|
**Status:** Done. All milestones complete as of 2026-03-13.
|
||||||
|
|
||||||
**Milestones:**
|
**Milestones:**
|
||||||
- M1: `TenantContextMiddleware` registered; all HTTP requests set RLS context from JWT
|
- M1: `TenantContextMiddleware` registered; all HTTP requests set RLS context from JWT
|
||||||
|
|||||||
@@ -345,7 +345,8 @@ def extract_mesh_edge_data(step_path: str) -> dict:
|
|||||||
[round(pt_start.X(), 3), round(pt_start.Y(), 3), round(pt_start.Z(), 3)],
|
[round(pt_start.X(), 3), round(pt_start.Y(), 3), round(pt_start.Z(), 3)],
|
||||||
[round(pt_end.X(), 3), round(pt_end.Y(), 3), round(pt_end.Z(), 3)],
|
[round(pt_end.X(), 3), round(pt_end.Y(), 3), round(pt_end.Z(), 3)],
|
||||||
])
|
])
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
|
logger.debug("Edge dihedral angle calc failed (skipping edge): %s", _exc)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Bounding box
|
# Bounding box
|
||||||
@@ -373,7 +374,8 @@ def extract_mesh_edge_data(step_path: str) -> dict:
|
|||||||
"y": round((ymin + ymax) / 2, 2),
|
"y": round((ymin + ymax) / 2, 2),
|
||||||
"z": round((zmin + zmax) / 2, 2),
|
"z": round((zmin + zmax) / 2, 2),
|
||||||
}
|
}
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
|
logger.debug("Bounding box calc failed: %s", _exc)
|
||||||
dimensions_mm = None
|
dimensions_mm = None
|
||||||
bbox_center_mm = None
|
bbox_center_mm = None
|
||||||
|
|
||||||
@@ -580,7 +582,8 @@ def extract_step_metadata(step_path: str) -> StepMetadata:
|
|||||||
[round(pt_start.X(), 3), round(pt_start.Y(), 3), round(pt_start.Z(), 3)],
|
[round(pt_start.X(), 3), round(pt_start.Y(), 3), round(pt_start.Z(), 3)],
|
||||||
[round(pt_end.X(), 3), round(pt_end.Y(), 3), round(pt_end.Z(), 3)],
|
[round(pt_end.X(), 3), round(pt_end.Y(), 3), round(pt_end.Z(), 3)],
|
||||||
])
|
])
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
|
logger.debug("Edge dihedral angle calc failed (skipping edge): %s", _exc)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# ── Step 4: Bounding box ──────────────────────────────────────────
|
# ── Step 4: Bounding box ──────────────────────────────────────────
|
||||||
@@ -601,8 +604,8 @@ def extract_step_metadata(step_path: str) -> StepMetadata:
|
|||||||
"y": round((ymin + ymax) / 2, 2),
|
"y": round((ymin + ymax) / 2, 2),
|
||||||
"z": round((zmin + zmax) / 2, 2),
|
"z": round((zmin + zmax) / 2, 2),
|
||||||
}
|
}
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
pass
|
logger.debug("Bounding box calc failed: %s", _exc)
|
||||||
|
|
||||||
# ── Step 5: Build edge_data dict ──────────────────────────────────
|
# ── Step 5: Build edge_data dict ──────────────────────────────────
|
||||||
edge_data: dict = {}
|
edge_data: dict = {}
|
||||||
@@ -866,8 +869,8 @@ def extract_rich_metadata(step_path: str) -> dict:
|
|||||||
if vol > largest_volume:
|
if vol > largest_volume:
|
||||||
largest_volume = vol
|
largest_volume = vol
|
||||||
largest_name = name
|
largest_name = name
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
pass
|
logger.debug("Volume properties failed for part '%s': %s", name, _exc)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
props = GProp_GProps()
|
props = GProp_GProps()
|
||||||
@@ -877,8 +880,8 @@ def extract_rich_metadata(step_path: str) -> dict:
|
|||||||
brepgprop.SurfaceProperties(shape, props)
|
brepgprop.SurfaceProperties(shape, props)
|
||||||
area = abs(props.Mass()) # mm²
|
area = abs(props.Mass()) # mm²
|
||||||
total_area += area * count
|
total_area += area * count
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
pass
|
logger.debug("Surface area properties failed for part '%s': %s", name, _exc)
|
||||||
|
|
||||||
result["total_volume_cm3"] = round(total_volume / 1000.0, 2) # mm³ → cm³
|
result["total_volume_cm3"] = round(total_volume / 1000.0, 2) # mm³ → cm³
|
||||||
result["total_surface_area_cm2"] = round(total_area / 100.0, 2) # mm² → cm²
|
result["total_surface_area_cm2"] = round(total_area / 100.0, 2) # mm² → cm²
|
||||||
@@ -900,8 +903,8 @@ def extract_rich_metadata(step_path: str) -> dict:
|
|||||||
min_dim = min(d for d in dims if d > 1e-6) # skip degenerate
|
min_dim = min(d for d in dims if d > 1e-6) # skip degenerate
|
||||||
if min_dim < smallest_dim:
|
if min_dim < smallest_dim:
|
||||||
smallest_dim = min_dim
|
smallest_dim = min_dim
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
pass
|
logger.debug("Smallest dimension calc failed for shape: %s", _exc)
|
||||||
result["smallest_dimension_mm"] = round(smallest_dim, 2) if smallest_dim < float("inf") else 0.0
|
result["smallest_dimension_mm"] = round(smallest_dim, 2) if smallest_dim < float("inf") else 0.0
|
||||||
|
|
||||||
# ── Triangle and vertex counts from tessellation ──────────────────
|
# ── Triangle and vertex counts from tessellation ──────────────────
|
||||||
@@ -928,8 +931,8 @@ def extract_rich_metadata(step_path: str) -> dict:
|
|||||||
if tri is not None:
|
if tri is not None:
|
||||||
total_triangles += tri.NbTriangles()
|
total_triangles += tri.NbTriangles()
|
||||||
total_vertices += tri.NbNodes()
|
total_vertices += tri.NbNodes()
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
pass
|
logger.debug("Triangle count failed for face: %s", _exc)
|
||||||
explorer.Next()
|
explorer.Next()
|
||||||
|
|
||||||
result["total_triangle_count"] = total_triangles
|
result["total_triangle_count"] = total_triangles
|
||||||
@@ -1015,8 +1018,8 @@ def _extract_step_objects_fallback(step_path: Path) -> list[str]:
|
|||||||
name = part.split("'")[1]
|
name = part.split("'")[1]
|
||||||
if name and name not in names:
|
if name and name not in names:
|
||||||
names.append(name)
|
names.append(name)
|
||||||
except Exception:
|
except Exception as _exc:
|
||||||
pass
|
logger.warning("STEP raw text parse for product names failed: %s", _exc)
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -395,21 +395,6 @@ export default function InlineCadViewer({
|
|||||||
}, [modelReady, pinnedPart, isolateMode, hideAssigned, partMaterials])
|
}, [modelReady, pinnedPart, isolateMode, hideAssigned, partMaterials])
|
||||||
|
|
||||||
// Dev-only: log normalized GLB mesh names vs stored keys to diagnose mismatches
|
// Dev-only: log normalized GLB mesh names vs stored keys to diagnose mismatches
|
||||||
useEffect(() => {
|
|
||||||
if (!import.meta.env.DEV || !modelReady || meshRegistryRef.current.length === 0) return
|
|
||||||
const names = new Set<string>(meshRegistryRef.current.map(e => e.partKey))
|
|
||||||
const keys = Object.keys(partMaterials)
|
|
||||||
const matched = keys.filter(k => names.has(k))
|
|
||||||
const unmatched = keys.filter(k => !names.has(k))
|
|
||||||
console.debug('[CAD] Match status:', {
|
|
||||||
totalGlbMeshes: names.size,
|
|
||||||
totalStoredKeys: keys.length,
|
|
||||||
matched: matched.length,
|
|
||||||
unmatched: unmatched.length,
|
|
||||||
unmatchedKeys: unmatched,
|
|
||||||
glbNames: [...names].sort(),
|
|
||||||
})
|
|
||||||
}, [modelReady, partMaterials])
|
|
||||||
|
|
||||||
const generateMut = useMutation({
|
const generateMut = useMutation({
|
||||||
mutationFn: () => generateGltfGeometry(cadFileId),
|
mutationFn: () => generateGltfGeometry(cadFileId),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const EVENT_LABELS: Record<string, string> = {
|
|||||||
const ALL_EVENTS = Object.keys(EVENT_LABELS)
|
const ALL_EVENTS = Object.keys(EVENT_LABELS)
|
||||||
const CHANNELS: Array<{ key: 'in_app' | 'email'; label: string; comingSoon?: boolean }> = [
|
const CHANNELS: Array<{ key: 'in_app' | 'email'; label: string; comingSoon?: boolean }> = [
|
||||||
{ key: 'in_app', label: 'In-App' },
|
{ key: 'in_app', label: 'In-App' },
|
||||||
{ key: 'email', label: 'E-Mail', comingSoon: true },
|
{ key: 'email', label: 'E-Mail' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const FREQUENCY_OPTIONS: Array<{ value: string; label: string }> = [
|
const FREQUENCY_OPTIONS: Array<{ value: string; label: string }> = [
|
||||||
|
|||||||
Reference in New Issue
Block a user