from pydantic import BaseModel, Field from typing import Optional, List from datetime import datetime # ============================================================ # CLIENTS # ============================================================ class ClientBase(BaseModel): full_name: str birth_date: Optional[str] = None goal: Optional[str] = "" notes: Optional[str] = "" class ClientCreate(ClientBase): sync_id: str class ClientUpdate(BaseModel): full_name: Optional[str] = None birth_date: Optional[str] = None goal: Optional[str] = None notes: Optional[str] = None is_active: Optional[int] = None class Client(ClientBase): id: int sync_id: str is_active: int created_at: str updated_at: str class Config: from_attributes = True # ============================================================ # WORKOUT TYPES # ============================================================ class WorkoutTypeBase(BaseModel): name: str sort_order: Optional[int] = 0 class WorkoutTypeCreate(WorkoutTypeBase): sync_id: str class WorkoutType(WorkoutTypeBase): id: int sync_id: str created_at: str class Config: from_attributes = True # ============================================================ # EXERCISES # ============================================================ class ExerciseBase(BaseModel): name: str workout_type_id: Optional[int] = None class ExerciseCreate(ExerciseBase): sync_id: str class ExerciseUpdate(BaseModel): name: Optional[str] = None workout_type_id: Optional[int] = None class Exercise(ExerciseBase): id: int sync_id: str created_at: str updated_at: str class Config: from_attributes = True # ============================================================ # DROPSET ITEMS # ============================================================ class DropsetItemBase(BaseModel): weight: float = Field(gt=0) reps: int = Field(gt=0) sort_order: int = 0 class DropsetItemCreate(DropsetItemBase): pass class DropsetItem(DropsetItemBase): id: int class Config: from_attributes = True # ============================================================ # SETS # ============================================================ class SetBase(BaseModel): is_dropset: int = 0 weight: Optional[float] = None reps: Optional[int] = None rating: Optional[int] = Field(None, ge=1, le=5) notes: Optional[str] = "" sort_order: int = 0 timestamp: str class SetCreate(SetBase): sync_id: str dropset_items: Optional[List[DropsetItemCreate]] = [] class Set(SetBase): id: int sync_id: str created_at: str dropset_items: List[DropsetItem] = [] class Config: from_attributes = True # ============================================================ # WORKOUT EXERCISES # ============================================================ class WorkoutExerciseBase(BaseModel): exercise_id: Optional[int] = None exercise_name: str sort_order: int = 0 class WorkoutExerciseCreate(WorkoutExerciseBase): sync_id: str sets: Optional[List[SetCreate]] = [] class WorkoutExercise(WorkoutExerciseBase): id: int sync_id: str created_at: str sets: List[Set] = [] class Config: from_attributes = True # ============================================================ # CARDIO # ============================================================ class CardioBase(BaseModel): cardio_type: str purpose: str duration: Optional[int] = None distance: Optional[float] = None speed: Optional[float] = None incline: Optional[float] = None resistance: Optional[int] = None notes: Optional[str] = "" sort_order: int = 0 class CardioCreate(CardioBase): sync_id: str class Cardio(CardioBase): id: int sync_id: str created_at: str class Config: from_attributes = True # ============================================================ # WORKOUTS # ============================================================ class WorkoutBase(BaseModel): client_id: Optional[int] = None workout_type_id: Optional[int] = None start_time: str end_time: Optional[str] = None notes: Optional[str] = "" class WorkoutCreate(WorkoutBase): sync_id: str workout_exercises: Optional[List[WorkoutExerciseCreate]] = [] cardio_sessions: Optional[List[CardioCreate]] = [] class WorkoutUpdate(BaseModel): workout_type_id: Optional[int] = None end_time: Optional[str] = None notes: Optional[str] = None synced: Optional[int] = None class Workout(WorkoutBase): id: int sync_id: str synced: int created_at: str updated_at: str workout_exercises: List[WorkoutExercise] = [] cardio_sessions: List[Cardio] = [] class Config: from_attributes = True # ============================================================ # METRIC TYPES # ============================================================ class MetricTypeBase(BaseModel): name: str unit: Optional[str] = "см" category: Optional[str] = "body" sort_order: Optional[int] = 0 class MetricTypeCreate(MetricTypeBase): sync_id: str class MetricType(MetricTypeBase): id: int sync_id: str created_at: str class Config: from_attributes = True # ============================================================ # BODY METRIC VALUES # ============================================================ class BodyMetricValueBase(BaseModel): metric_type_id: int value: float class BodyMetricValueCreate(BodyMetricValueBase): pass class BodyMetricValue(BodyMetricValueBase): id: int class Config: from_attributes = True # ============================================================ # BODY METRICS # ============================================================ class BodyMetricBase(BaseModel): client_id: Optional[int] = None metric_date: str notes: Optional[str] = "" class BodyMetricCreate(BodyMetricBase): sync_id: str values: Optional[List[BodyMetricValueCreate]] = [] class BodyMetric(BodyMetricBase): id: int sync_id: str created_at: str values: List[BodyMetricValue] = [] class Config: from_attributes = True # ============================================================ # SYNC # ============================================================ class SyncPayload(BaseModel): clients: Optional[List[ClientCreate]] = [] workout_types: Optional[List[WorkoutTypeCreate]] = [] exercises: Optional[List[ExerciseCreate]] = [] workouts: Optional[List[WorkoutCreate]] = [] metric_types: Optional[List[MetricTypeCreate]] = [] body_metrics: Optional[List[BodyMetricCreate]] = [] class SyncResponse(BaseModel): status: str created: dict updated: dict