initial commit
This commit is contained in:
commit
39fdd43d8e
127 files changed
+13919
No files matched your search
@@ -0,0 +1,193 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
from typing import Any, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
Weekday = Literal[0, 1, 2, 3, 4, 5, 6] # Mon=0 .. Sun=6
|
||||
|
||||
|
||||
class SetupPasswordRequest(BaseModel):
|
||||
password: str = Field(min_length=4, max_length=128)
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
password: str
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
needs_setup: bool = False
|
||||
onboarded: bool = False
|
||||
|
||||
|
||||
class StatusResponse(BaseModel):
|
||||
has_password: bool
|
||||
onboarded: bool
|
||||
phase: str = "active"
|
||||
|
||||
|
||||
class OnboardingRequest(BaseModel):
|
||||
one_rm_kg: float = Field(gt=0)
|
||||
training_weekdays: list[int] = Field(min_length=3, max_length=3)
|
||||
start_date: date
|
||||
|
||||
@field_validator("training_weekdays")
|
||||
@classmethod
|
||||
def validate_days(cls, v: list[int]) -> list[int]:
|
||||
if len(set(v)) != 3:
|
||||
raise ValueError("Нужно выбрать 3 разных дня")
|
||||
for d in v:
|
||||
if d < 0 or d > 6:
|
||||
raise ValueError("День недели должен быть 0–6")
|
||||
sorted_days = sorted(v)
|
||||
# check circular gap of at least 1 day between consecutive selected days
|
||||
for i in range(3):
|
||||
a = sorted_days[i]
|
||||
b = sorted_days[(i + 1) % 3]
|
||||
if i < 2:
|
||||
gap = b - a - 1
|
||||
else:
|
||||
gap = (a + 7) - b - 1
|
||||
if gap < 1:
|
||||
raise ValueError("Между выбранными днями нужен минимум 1 день перерыва")
|
||||
return sorted(v)
|
||||
|
||||
|
||||
class ProfileResponse(BaseModel):
|
||||
one_rm_kg: Optional[float]
|
||||
training_weekdays: Optional[list[int]]
|
||||
start_date: Optional[date]
|
||||
onboarded: bool
|
||||
phase: str
|
||||
rest_until: Optional[datetime]
|
||||
probe_available_at: Optional[datetime]
|
||||
cycle_number: int
|
||||
rest_countdown_seconds: Optional[int] = None
|
||||
can_probe: bool = False
|
||||
|
||||
|
||||
class SetResponse(BaseModel):
|
||||
id: int
|
||||
set_index: int
|
||||
target_percent: Optional[float]
|
||||
target_reps: Optional[int]
|
||||
target_weight_kg: Optional[float]
|
||||
is_failure: bool
|
||||
completed: bool
|
||||
actual_reps: Optional[int]
|
||||
actual_weight_kg: Optional[float]
|
||||
|
||||
|
||||
class ExerciseResponse(BaseModel):
|
||||
id: int
|
||||
order_index: int
|
||||
name: str
|
||||
kind: str
|
||||
prescribed_weight_kg: Optional[float]
|
||||
weight_locked: bool
|
||||
needs_weight_input: bool
|
||||
pending_prompt: Optional[str]
|
||||
weight_step_kg: float = 1.25
|
||||
sets: list[SetResponse]
|
||||
|
||||
|
||||
class WorkoutSummary(BaseModel):
|
||||
id: int
|
||||
cycle_number: int
|
||||
week_number: int
|
||||
day_index: int
|
||||
scheduled_date: date
|
||||
status: str
|
||||
exercise_count: int = 0
|
||||
|
||||
|
||||
class WorkoutDetail(BaseModel):
|
||||
id: int
|
||||
cycle_number: int
|
||||
week_number: int
|
||||
day_index: int
|
||||
scheduled_date: date
|
||||
status: str
|
||||
started_at: Optional[datetime]
|
||||
paused_at: Optional[datetime]
|
||||
completed_at: Optional[datetime]
|
||||
current_exercise_index: int
|
||||
current_set_index: int
|
||||
rest_started_at: Optional[datetime]
|
||||
rest_elapsed_seconds: int = 0
|
||||
can_start: bool = False
|
||||
can_resume: bool = False
|
||||
pause_expires_at: Optional[datetime] = None
|
||||
exercises: list[ExerciseResponse]
|
||||
|
||||
|
||||
class CalendarResponse(BaseModel):
|
||||
year: int
|
||||
month: int
|
||||
workouts: list[WorkoutSummary]
|
||||
profile: ProfileResponse
|
||||
|
||||
|
||||
class CompleteSetRequest(BaseModel):
|
||||
actual_reps: Optional[int] = None
|
||||
actual_weight_kg: Optional[float] = None
|
||||
|
||||
|
||||
class SetWeightRequest(BaseModel):
|
||||
weight_kg: float = Field(gt=0)
|
||||
|
||||
|
||||
class AdjustSetRequest(BaseModel):
|
||||
actual_weight_kg: float = Field(gt=0)
|
||||
actual_reps: int = Field(ge=0)
|
||||
|
||||
|
||||
class PromptChoiceRequest(BaseModel):
|
||||
choice: Literal["easy_yes", "easy_no"]
|
||||
weight_kg: Optional[float] = None
|
||||
increase_kg: Optional[float] = None
|
||||
decrease_kg: Optional[float] = None
|
||||
|
||||
|
||||
class StartRestRequest(BaseModel):
|
||||
rest_days: int = Field(ge=3, le=5)
|
||||
|
||||
|
||||
class ProbeRequest(BaseModel):
|
||||
one_rm_kg: float = Field(gt=0)
|
||||
start_date: date
|
||||
training_weekdays: Optional[list[int]] = None
|
||||
|
||||
|
||||
class ExportResponse(BaseModel):
|
||||
version: int = 1
|
||||
exported_at: datetime
|
||||
data: dict[str, Any]
|
||||
|
||||
|
||||
class ImportRequest(BaseModel):
|
||||
version: int
|
||||
data: dict[str, Any]
|
||||
|
||||
|
||||
class MessageResponse(BaseModel):
|
||||
message: str
|
||||
workout: Optional[WorkoutDetail] = None
|
||||
profile: Optional[ProfileResponse] = None
|
||||
|
||||
|
||||
class MoveWorkoutRequest(BaseModel):
|
||||
scheduled_date: date
|
||||
|
||||
|
||||
class TrainingDaysRequest(BaseModel):
|
||||
training_weekdays: list[int] = Field(min_length=3, max_length=3)
|
||||
|
||||
@field_validator("training_weekdays")
|
||||
@classmethod
|
||||
def validate_days(cls, v: list[int]) -> list[int]:
|
||||
return OnboardingRequest.validate_days(v)
|
||||
Reference in new issue
Block a user