Files
john a400130c67
CI / api-test (push) Has been cancelled
CI / web-build (push) Has been cancelled
Sprint 6: report review workflow, CI/K8s, and client tabs.
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>
2026-07-23 11:44:11 +08:00

171 lines
3.9 KiB
Python

from datetime import date, datetime
from enum import Enum
from typing import Any
from pydantic import BaseModel, Field
class LoginType(str, Enum):
phone_code = "phone_code"
wechat = "wechat"
class UserRole(str, Enum):
parent = "parent"
coach = "coach"
org_admin = "org_admin"
platform_admin = "platform_admin"
class Gender(str, Enum):
male = "male"
female = "female"
unknown = "unknown"
class VideoScene(str, Enum):
front_posture = "front_posture"
side_posture = "side_posture"
squat = "squat"
balance = "balance"
gait = "gait"
class TaskType(str, Enum):
posture_screening = "posture_screening"
movement_scoring = "movement_scoring"
reassessment = "reassessment"
class TaskStatus(str, Enum):
CREATED = "CREATED"
QUEUED = "QUEUED"
PROCESSING = "PROCESSING"
SUCCEEDED = "SUCCEEDED"
FAILED = "FAILED"
CANCELLED = "CANCELLED"
class RiskLevel(str, Enum):
low = "low"
medium = "medium"
high = "high"
review_required = "review_required"
class PlanStatus(str, Enum):
draft = "draft"
active = "active"
completed = "completed"
paused = "paused"
class LoginRequest(BaseModel):
login_type: LoginType = Field(alias="loginType")
credential: str
code: str | None = None
model_config = {"populate_by_name": True}
class User(BaseModel):
id: int
phone_masked: str = Field(alias="phoneMasked")
role: UserRole
model_config = {"populate_by_name": True}
class ChildCreateRequest(BaseModel):
name: str
birthday: date
gender: Gender = Gender.unknown
height: float | None = None
weight: float | None = None
contraindications: str | None = None
class ChildUpdateRequest(ChildCreateRequest):
pass
class Child(BaseModel):
id: int
name: str
birthday: date
age: int
height: float | None = None
weight: float | None = None
status: str = "active"
class UploadTokenRequest(BaseModel):
child_id: int = Field(alias="childId")
file_name: str = Field(alias="fileName")
content_type: str = Field(alias="contentType")
size: int
model_config = {"populate_by_name": True}
class VideoCreateRequest(BaseModel):
child_id: int = Field(alias="childId")
object_key: str = Field(alias="objectKey")
scene: VideoScene
capture_hint: str | None = Field(default=None, alias="captureHint")
model_config = {"populate_by_name": True}
class AnalysisTaskCreateRequest(BaseModel):
child_id: int = Field(alias="childId")
video_id: int = Field(alias="videoId")
task_type: TaskType = Field(alias="taskType")
model_config = {"populate_by_name": True}
class AnalysisTaskWebhook(BaseModel):
task_id: int = Field(alias="taskId")
status: TaskStatus
progress: int | None = None
error_code: str | None = Field(default=None, alias="errorCode")
result: dict[str, Any] | None = None
signature: str | None = None
model_config = {"populate_by_name": True}
class TrainingPlanCreateRequest(BaseModel):
child_id: int = Field(alias="childId")
report_id: int | None = Field(default=None, alias="reportId")
goal: str
cycle_days: int = Field(alias="cycleDays", ge=7)
exercise_ids: list[int] | None = Field(default=None, alias="exerciseIds")
constraints: dict[str, Any] | None = None
model_config = {"populate_by_name": True}
class TrainingRecordCreateRequest(BaseModel):
exercise_id: int = Field(alias="exerciseId")
completed: bool
score: int | None = Field(default=None, ge=0, le=100)
duration_seconds: int | None = Field(default=None, alias="durationSeconds")
note: str | None = None
media: list[str] | None = None
model_config = {"populate_by_name": True}
class ReportMetric(BaseModel):
name: str
value: float
level: str
confidence: float
class ReportReviewRequest(BaseModel):
action: str = Field(description="approve or reject")
note: str | None = None