mitai-jinkendo/backend/models.py
Lars 48508c164e
All checks were successful
Deploy Development / deploy (push) Successful in 59s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
feat: Add Activity Session Metrics functionality
- Introduced Activity Session Metrics for enhanced tracking of session data.
- Updated backend to support new API endpoints for managing session metrics.
- Added new Pydantic models for activity metrics and replaced metrics functionality.
- Enhanced data layer to include session metrics in recent training session data.
- Updated documentation to reflect changes in session metrics handling.
2026-04-14 11:49:14 +02:00

260 lines
8.4 KiB
Python

"""
Pydantic Models for Mitai Jinkendo API
Data validation schemas for request/response bodies.
"""
from typing import Any, List, Optional
from pydantic import BaseModel, Field
# ── Profile Models ────────────────────────────────────────────────────────────
class ProfileCreate(BaseModel):
name: str
avatar_color: Optional[str] = '#1D9E75'
sex: Optional[str] = 'm'
dob: Optional[str] = None
height: Optional[float] = 178
goal_weight: Optional[float] = None
goal_bf_pct: Optional[float] = None
class ProfileUpdate(BaseModel):
name: Optional[str] = None
avatar_color: Optional[str] = None
sex: Optional[str] = None
dob: Optional[str] = None
height: Optional[float] = None
goal_weight: Optional[float] = None
goal_bf_pct: Optional[float] = None
quality_filter_level: Optional[str] = None # Issue #31: Global quality filter
email: Optional[str] = None # Self-service; leer = entfernen; Änderung setzt Verifikation zurück
# ── Tracking Models ───────────────────────────────────────────────────────────
class WeightEntry(BaseModel):
date: str
weight: float
note: Optional[str] = None
class CircumferenceEntry(BaseModel):
date: str
c_neck: Optional[float] = None
c_chest: Optional[float] = None
c_waist: Optional[float] = None
c_belly: Optional[float] = None
c_hip: Optional[float] = None
c_thigh: Optional[float] = None
c_calf: Optional[float] = None
c_arm: Optional[float] = None
notes: Optional[str] = None
photo_id: Optional[str] = None
class CaliperEntry(BaseModel):
date: str
sf_method: Optional[str] = 'jackson3'
sf_chest: Optional[float] = None
sf_axilla: Optional[float] = None
sf_triceps: Optional[float] = None
sf_subscap: Optional[float] = None
sf_suprailiac: Optional[float] = None
sf_abdomen: Optional[float] = None
sf_thigh: Optional[float] = None
sf_calf_med: Optional[float] = None
sf_lowerback: Optional[float] = None
sf_biceps: Optional[float] = None
body_fat_pct: Optional[float] = None
lean_mass: Optional[float] = None
fat_mass: Optional[float] = None
notes: Optional[str] = None
class ActivityEntry(BaseModel):
date: str
start_time: Optional[str] = None
end_time: Optional[str] = None
activity_type: str
duration_min: Optional[float] = None
kcal_active: Optional[float] = None
kcal_resting: Optional[float] = None
hr_avg: Optional[float] = None
hr_max: Optional[float] = None
distance_km: Optional[float] = None
rpe: Optional[int] = None
source: Optional[str] = 'manual'
notes: Optional[str] = None
training_type_id: Optional[int] = None # v9d: Training type categorization
training_category: Optional[str] = None # v9d: Denormalized category
training_subcategory: Optional[str] = None # v9d: Denormalized subcategory
class ActivityMetricValue(BaseModel):
parameter_key: str
value: Any
class ActivityMetricsReplace(BaseModel):
"""Voller Ersatz der EAV-Metriken für eine Session (siehe Agent-Guide)."""
metrics: List[ActivityMetricValue] = Field(default_factory=list)
class NutritionDay(BaseModel):
date: str
kcal: Optional[float] = None
protein_g: Optional[float] = None
fat_g: Optional[float] = None
carbs_g: Optional[float] = None
# ── Auth Models ───────────────────────────────────────────────────────────────
class LoginRequest(BaseModel):
email: str
password: str
class PasswordResetRequest(BaseModel):
email: str
class PasswordResetConfirm(BaseModel):
token: str
new_password: str
class RegisterRequest(BaseModel):
name: str
email: str
password: str
# ── Admin Models ──────────────────────────────────────────────────────────────
class AdminProfileUpdate(BaseModel):
role: Optional[str] = None
ai_enabled: Optional[int] = None
ai_limit_day: Optional[int] = None
export_enabled: Optional[int] = None
# ── Prompt Models (Issue #28) ────────────────────────────────────────────────
class PromptCreate(BaseModel):
name: str
slug: str
display_name: Optional[str] = None
description: Optional[str] = None
template: str
category: str = 'ganzheitlich'
active: bool = True
sort_order: int = 0
class PromptUpdate(BaseModel):
name: Optional[str] = None
display_name: Optional[str] = None
description: Optional[str] = None
template: Optional[str] = None
category: Optional[str] = None
active: Optional[bool] = None
sort_order: Optional[int] = None
class PromptGenerateRequest(BaseModel):
goal: str
data_categories: list[str]
example_output: Optional[str] = None
# ── Unified Prompt System Models (Issue #28 Phase 2) ───────────────────────
class StagePromptCreate(BaseModel):
"""Single prompt within a stage"""
source: str # 'inline' or 'reference'
slug: Optional[str] = None # Required if source='reference'
template: Optional[str] = None # Required if source='inline'
output_key: str # Key for storing result (e.g., 'nutrition', 'stage1_body')
output_format: str = 'text' # 'text' or 'json'
output_schema: Optional[dict] = None # JSON schema if output_format='json'
class StageCreate(BaseModel):
"""Single stage with multiple prompts"""
stage: int # Stage number (1, 2, 3, ...)
prompts: list[StagePromptCreate]
class UnifiedPromptCreate(BaseModel):
"""Create a new unified prompt (base, pipeline, or workflow type)"""
name: str
slug: Optional[str] = None # Auto-generated from name if not provided
display_name: Optional[str] = None
description: Optional[str] = None
type: str # 'base' | 'pipeline' | 'workflow'
category: str = 'ganzheitlich'
active: bool = True
sort_order: int = 0
# For base prompts (single reusable template)
template: Optional[str] = None # Required if type='base'
output_format: str = 'text'
output_schema: Optional[dict] = None
# For pipeline prompts (multi-stage workflow)
stages: Optional[list[StageCreate]] = None # Required if type='pipeline'
# For workflow prompts (visual graph editor)
graph_data: Optional[dict] = None # Required if type='workflow'
class UnifiedPromptUpdate(BaseModel):
"""Update an existing unified prompt"""
name: Optional[str] = None
display_name: Optional[str] = None
description: Optional[str] = None
type: Optional[str] = None
category: Optional[str] = None
active: Optional[bool] = None
sort_order: Optional[int] = None
template: Optional[str] = None
output_format: Optional[str] = None
output_schema: Optional[dict] = None
stages: Optional[list[StageCreate]] = None
graph_data: Optional[dict] = None # For workflow type
# ── Pipeline Config Models (Issue #28) ─────────────────────────────────────
# NOTE: These will be deprecated in favor of UnifiedPrompt models above
class PipelineConfigCreate(BaseModel):
name: str
description: Optional[str] = None
is_default: bool = False
active: bool = True
modules: dict # {"körper": true, "ernährung": true, ...}
timeframes: dict # {"körper": 30, "ernährung": 30, ...}
stage1_prompts: list[str] # Array of slugs
stage2_prompt: str # slug
stage3_prompt: Optional[str] = None # slug
class PipelineConfigUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
is_default: Optional[bool] = None
active: Optional[bool] = None
modules: Optional[dict] = None
timeframes: Optional[dict] = None
stage1_prompts: Optional[list[str]] = None
stage2_prompt: Optional[str] = None
stage3_prompt: Optional[str] = None
class PipelineExecuteRequest(BaseModel):
config_id: Optional[str] = None # None = use default config