Sprint 6: report review workflow, CI/K8s, and client tabs.
CI / api-test (push) Has been cancelled
CI / web-build (push) Has been cancelled

Add coach review APIs, pose calibration thresholds, Gitea CI, Kubernetes skeleton, H5 practice page, and mini program tab bar.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-23 11:44:11 +08:00
parent 1aaef71f52
commit a400130c67
27 changed files with 636 additions and 27 deletions
+13
View File
@@ -0,0 +1,13 @@
"""Configurable thresholds for pose metric calibration."""
CALIBRATION = {
"head_forward_scale": 180.0,
"shoulder_asymmetry_scale": 220.0,
"pelvic_tilt_scale": 4.5,
"normal_max": 35,
"low_max": 55,
"medium_max": 75,
"review_required_min": 75,
}
METRIC_NAMES = ("头前伸", "高低肩", "骨盆倾斜")
+11 -7
View File
@@ -15,7 +15,7 @@ RIGHT_HIP = 24
Landmark = dict[str, float]
FrameLandmarks = dict[int, Landmark]
METRIC_NAMES = ("头前伸", "高低肩", "骨盆倾斜")
from app.ai.calibration import CALIBRATION, METRIC_NAMES
def _point(frame: FrameLandmarks, idx: int) -> Landmark | None:
@@ -43,7 +43,7 @@ def score_head_forward(frame: FrameLandmarks) -> float | None:
return None
mid_x = (left["x"] + right["x"]) / 2
offset = abs(nose["x"] - mid_x) / width
return min(100.0, max(0.0, offset * 180))
return min(100.0, max(0.0, offset * CALIBRATION["head_forward_scale"]))
def score_shoulder_asymmetry(frame: FrameLandmarks) -> float | None:
@@ -53,7 +53,7 @@ def score_shoulder_asymmetry(frame: FrameLandmarks) -> float | None:
if not left or not right or not width:
return None
diff = abs(left["y"] - right["y"]) / width
return min(100.0, max(0.0, diff * 220))
return min(100.0, max(0.0, diff * CALIBRATION["shoulder_asymmetry_scale"]))
def score_pelvic_tilt(frame: FrameLandmarks) -> float | None:
@@ -63,15 +63,15 @@ def score_pelvic_tilt(frame: FrameLandmarks) -> float | None:
return None
angle = abs(math.degrees(math.atan2(right["y"] - left["y"], right["x"] - left["x"])))
tilt = min(angle, 180 - angle)
return min(100.0, max(0.0, tilt * 4.5))
return min(100.0, max(0.0, tilt * CALIBRATION["pelvic_tilt_scale"]))
def value_to_level(value: float) -> str:
if value < 35:
if value < CALIBRATION["normal_max"]:
return "normal"
if value < 55:
if value < CALIBRATION["low_max"]:
return "low"
if value < 75:
if value < CALIBRATION["medium_max"]:
return "medium"
return "high"
@@ -111,12 +111,16 @@ def build_screening_report(frames: list[FrameLandmarks]) -> dict:
worst = max((m["value"] for m in metrics), default=0)
risk_level = value_to_level(worst)
if worst >= CALIBRATION["review_required_min"]:
risk_level = "review_required"
if risk_level == "normal":
summary = "体态指标整体正常,建议保持日常活动与姿势习惯"
elif risk_level == "low":
summary = "存在轻度体态偏差,建议开始基础纠正训练"
elif risk_level == "medium":
summary = "建议关注头前伸与高低肩,开始针对性训练"
elif risk_level == "review_required":
summary = "指标偏高,已提交机构教练复核"
else:
summary = "多项指标偏高,建议尽快安排专业评估与干预"