瀏覽代碼

fix(analytics): derive metric defs from single source of truth (add thermal/structural metrics)

analytics.py kept a hardcoded 11-metric electromagnetic-only METRIC_DEFS, silently dropping the 7 thermal + 4 structural metrics from /analytics/metrics. Now imports METRIC_DEFINITIONS from afmcore.metrics (35 metrics) and derives the display dict. test_analytics.py passes.
carlin 21 小時之前
父節點
當前提交
d326b5df30
共有 1 個文件被更改,包括 26 次插入11 次删除
  1. 26 11
      web/backend/app/services/analytics.py

+ 26 - 11
web/backend/app/services/analytics.py

@@ -13,25 +13,40 @@ All source is ASCII.
 from __future__ import annotations
 
 import math
+import os
+import sys
 from typing import Any
 
 
 # ---------------------------------------------------------------------------
 # Metric definitions (for display and analysis)
 # ---------------------------------------------------------------------------
+# Single source of truth: src/afmcore/metrics.py METRIC_DEFINITIONS. This
+# module must NOT keep its own copy (historical drift: an 11-metric
+# electromagnetic-only list silently dropped thermal + structural metrics).
+# Make src/afmcore importable, then derive the display dict below.
+
+_ANALYTICS_DIR = os.path.dirname(
+    os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+)
+# web/backend/app/services/analytics.py -> <repo>/src
+_SRC_DIR = os.path.normpath(os.path.join(_ANALYTICS_DIR, "..", "..", "src"))
+if _SRC_DIR not in sys.path and os.path.isdir(_SRC_DIR):
+    sys.path.insert(0, _SRC_DIR)
+
+from afmcore.metrics import METRIC_DEFINITIONS  # noqa: E402
+
+# direction "neutral" maps to True to preserve the historical pareto behavior
+# (neutral metrics are not optimization targets but were never excluded here).
+_DIRECTION_TO_BETTER = {"higher": True, "lower": False, "neutral": True}
 
 METRIC_DEFS = {
-    "tavg_nm": {"label": "Average Torque", "unit": "Nm", "higher_is_better": True},
-    "ripple_pct": {"label": "Torque Ripple", "unit": "%", "higher_is_better": False},
-    "efficiency_pct": {"label": "Efficiency", "unit": "%", "higher_is_better": True},
-    "total_losses_w": {"label": "Total Losses", "unit": "W", "higher_is_better": False},
-    "copper_loss_w": {"label": "Copper Loss", "unit": "W", "higher_is_better": False},
-    "iron_loss_w": {"label": "Iron Loss", "unit": "W", "higher_is_better": False},
-    "magnet_loss_w": {"label": "Magnet Loss", "unit": "W", "higher_is_better": False},
-    "back_emf_v": {"label": "Back EMF", "unit": "V", "higher_is_better": True},
-    "output_power_w": {"label": "Output Power", "unit": "W", "higher_is_better": True},
-    "input_power_w": {"label": "Input Power", "unit": "W", "higher_is_better": True},
-    "no_load_speed_rpm": {"label": "No-Load Speed", "unit": "rpm", "higher_is_better": True},
+    m["key"]: {
+        "label": m["label"],
+        "unit": m["unit"],
+        "higher_is_better": _DIRECTION_TO_BETTER.get(m.get("direction"), True),
+    }
+    for m in METRIC_DEFINITIONS
 }