initial commit

This commit is contained in:
parovin committed 2026-10-11 14:12:10 +03:00
commit 39fdd43d8e
127 files changed
+13919

No files matched your search

+118
View File
@@ -0,0 +1,118 @@
from __future__ import annotations
from datetime import date, datetime
from typing import Any, Optional
from sqlalchemy import Boolean, Date, DateTime, Float, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
class Profile(Base):
__tablename__ = "profiles"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
one_rm_kg: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
training_weekdays: Mapped[Optional[list[Any]]] = mapped_column(JSONB, nullable=True)
start_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
onboarded: Mapped[bool] = mapped_column(Boolean, default=False)
phase: Mapped[str] = mapped_column(String(32), default="active")
# active | rest | probe | completed_cycle
rest_until: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
probe_available_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
cycle_number: Mapped[int] = mapped_column(Integer, default=1)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class Workout(Base):
__tablename__ = "workouts"
__table_args__ = (UniqueConstraint("scheduled_date", "cycle_number", name="uq_workout_date_cycle"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
cycle_number: Mapped[int] = mapped_column(Integer, default=1)
week_number: Mapped[int] = mapped_column(Integer, nullable=False)
day_index: Mapped[int] = mapped_column(Integer, nullable=False) # 0..2 within week
scheduled_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
status: Mapped[str] = mapped_column(String(32), default="planned")
# planned | in_progress | paused | completed | abandoned
started_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
paused_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
current_exercise_index: Mapped[int] = mapped_column(Integer, default=0)
current_set_index: Mapped[int] = mapped_column(Integer, default=0)
rest_started_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
exercises: Mapped[list[WorkoutExercise]] = relationship(
back_populates="workout", cascade="all, delete-orphan", order_by="WorkoutExercise.order_index"
)
class WorkoutExercise(Base):
__tablename__ = "workout_exercises"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
workout_id: Mapped[int] = mapped_column(ForeignKey("workouts.id", ondelete="CASCADE"), index=True)
order_index: Mapped[int] = mapped_column(Integer, nullable=False)
name: Mapped[str] = mapped_column(String(128), nullable=False)
kind: Mapped[str] = mapped_column(String(32), nullable=False) # bench | accessory | failure
prescribed_weight_kg: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
weight_locked: Mapped[bool] = mapped_column(Boolean, default=False)
needs_weight_input: Mapped[bool] = mapped_column(Boolean, default=False)
pending_prompt: Mapped[Optional[str]] = mapped_column(String(32), nullable=True)
# None | choose_weight_missed | choose_weight_easy | confirm_easy
workout: Mapped[Workout] = relationship(back_populates="exercises")
sets: Mapped[list[WorkoutSet]] = relationship(
back_populates="exercise", cascade="all, delete-orphan", order_by="WorkoutSet.set_index"
)
class WorkoutSet(Base):
__tablename__ = "workout_sets"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
exercise_id: Mapped[int] = mapped_column(ForeignKey("workout_exercises.id", ondelete="CASCADE"), index=True)
set_index: Mapped[int] = mapped_column(Integer, nullable=False)
target_percent: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
target_reps: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
target_weight_kg: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
is_failure: Mapped[bool] = mapped_column(Boolean, default=False)
completed: Mapped[bool] = mapped_column(Boolean, default=False)
actual_reps: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
actual_weight_kg: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
exercise: Mapped[WorkoutExercise] = relationship(back_populates="sets")
class AccessoryState(Base):
"""Tracks working weight and lock status for accessory exercises within a cycle."""
__tablename__ = "accessory_states"
__table_args__ = (UniqueConstraint("cycle_number", "exercise_name", name="uq_accessory_cycle_name"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
cycle_number: Mapped[int] = mapped_column(Integer, default=1)
exercise_name: Mapped[str] = mapped_column(String(128), nullable=False)
weight_kg: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
locked: Mapped[bool] = mapped_column(Boolean, default=False)
pending_weight_kg: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class AppMeta(Base):
__tablename__ = "app_meta"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
key: Mapped[str] = mapped_column(String(64), unique=True)
value: Mapped[str] = mapped_column(Text, nullable=False)