297 lines
8.9 KiB
Python
297 lines
8.9 KiB
Python
from sqlalchemy import (
|
|
Column,
|
|
Integer,
|
|
String,
|
|
Float,
|
|
ForeignKey,
|
|
CheckConstraint,
|
|
UniqueConstraint,
|
|
)
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from database import Base
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
username = Column(String, unique=True, nullable=False, index=True)
|
|
password_hash = Column(String, nullable=False)
|
|
display_name = Column(String, default="")
|
|
role = Column(String, default="personal") # personal | trainer
|
|
created_at = Column(String)
|
|
|
|
|
|
class WorkoutType(Base):
|
|
__tablename__ = "workout_types"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
name = Column(String, unique=True, nullable=False)
|
|
sort_order = Column(Integer, default=0)
|
|
created_at = Column(String)
|
|
|
|
exercises = relationship("Exercise", back_populates="workout_type")
|
|
workouts = relationship("Workout", back_populates="workout_type")
|
|
|
|
|
|
class Exercise(Base):
|
|
__tablename__ = "exercises"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
# NULL = глобальный справочник, иначе — личное упражнение пользователя
|
|
owner_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
|
|
workout_type_id = Column(
|
|
Integer,
|
|
ForeignKey("workout_types.id", ondelete="SET NULL"),
|
|
index=True,
|
|
)
|
|
|
|
name = Column(String, nullable=False, index=True)
|
|
created_at = Column(String)
|
|
updated_at = Column(String)
|
|
|
|
workout_type = relationship("WorkoutType", back_populates="exercises")
|
|
workout_exercises = relationship("WorkoutExercise", back_populates="exercise")
|
|
|
|
|
|
class Client(Base):
|
|
__tablename__ = "clients"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
owner_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
|
|
full_name = Column(String, nullable=False)
|
|
birth_date = Column(String)
|
|
goal = Column(String, default="")
|
|
notes = Column(String, default="")
|
|
color = Column(String)
|
|
is_active = Column(Integer, default=1)
|
|
|
|
created_at = Column(String)
|
|
updated_at = Column(String)
|
|
|
|
workouts = relationship("Workout", back_populates="client")
|
|
body_metrics = relationship("BodyMetric", back_populates="client")
|
|
|
|
|
|
class Workout(Base):
|
|
__tablename__ = "workouts"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
owner_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
client_id = Column(Integer, ForeignKey("clients.id", ondelete="SET NULL"), index=True)
|
|
workout_type_id = Column(
|
|
Integer,
|
|
ForeignKey("workout_types.id", ondelete="SET NULL"),
|
|
index=True,
|
|
)
|
|
|
|
start_time = Column(String, nullable=False)
|
|
end_time = Column(String)
|
|
notes = Column(String, default="")
|
|
synced = Column(Integer, default=0)
|
|
|
|
created_at = Column(String)
|
|
updated_at = Column(String)
|
|
|
|
client = relationship("Client", back_populates="workouts")
|
|
workout_type = relationship("WorkoutType", back_populates="workouts")
|
|
|
|
workout_exercises = relationship(
|
|
"WorkoutExercise",
|
|
back_populates="workout",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
cardio_sessions = relationship(
|
|
"Cardio",
|
|
back_populates="workout",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
|
|
|
|
class WorkoutExercise(Base):
|
|
__tablename__ = "workout_exercises"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
workout_id = Column(Integer, ForeignKey("workouts.id", ondelete="CASCADE"), index=True)
|
|
exercise_id = Column(Integer, ForeignKey("exercises.id", ondelete="SET NULL"), index=True)
|
|
|
|
exercise_name = Column(String, nullable=False, index=True)
|
|
sort_order = Column(Integer, default=0)
|
|
created_at = Column(String)
|
|
|
|
workout = relationship("Workout", back_populates="workout_exercises")
|
|
exercise = relationship("Exercise", back_populates="workout_exercises")
|
|
sets = relationship(
|
|
"Set",
|
|
back_populates="workout_exercise",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
|
|
|
|
class Set(Base):
|
|
__tablename__ = "sets"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
workout_exercise_id = Column(
|
|
Integer,
|
|
ForeignKey("workout_exercises.id", ondelete="CASCADE"),
|
|
index=True,
|
|
)
|
|
|
|
is_dropset = Column(Integer, default=0)
|
|
weight = Column(Float)
|
|
reps = Column(Integer)
|
|
rating = Column(Integer)
|
|
notes = Column(String, default="")
|
|
sort_order = Column(Integer, default=0)
|
|
timestamp = Column(String, nullable=False)
|
|
created_at = Column(String)
|
|
|
|
workout_exercise = relationship("WorkoutExercise", back_populates="sets")
|
|
dropset_items = relationship(
|
|
"DropsetItem",
|
|
back_populates="set",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
|
|
__table_args__ = (
|
|
CheckConstraint(
|
|
"(is_dropset = 0 AND weight IS NOT NULL AND reps IS NOT NULL) OR "
|
|
"(is_dropset = 1 AND weight IS NULL AND reps IS NULL)",
|
|
name="ck_sets_dropset_shape",
|
|
),
|
|
CheckConstraint(
|
|
"rating IS NULL OR (rating >= 1 AND rating <= 5)",
|
|
name="ck_sets_rating",
|
|
),
|
|
)
|
|
|
|
|
|
class DropsetItem(Base):
|
|
__tablename__ = "dropset_items"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
set_id = Column(Integer, ForeignKey("sets.id", ondelete="CASCADE"), index=True)
|
|
|
|
weight = Column(Float, nullable=False)
|
|
reps = Column(Integer, nullable=False)
|
|
sort_order = Column(Integer, default=0)
|
|
|
|
set = relationship("Set", back_populates="dropset_items")
|
|
|
|
__table_args__ = (
|
|
CheckConstraint("weight > 0", name="ck_dropset_weight_positive"),
|
|
CheckConstraint("reps > 0", name="ck_dropset_reps_positive"),
|
|
)
|
|
|
|
|
|
class Cardio(Base):
|
|
__tablename__ = "cardio"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
workout_id = Column(Integer, ForeignKey("workouts.id", ondelete="CASCADE"), index=True)
|
|
|
|
cardio_type = Column(String, nullable=False)
|
|
purpose = Column(String, nullable=False) # warmup | cooldown | separate | другое
|
|
|
|
duration = Column(Integer)
|
|
distance = Column(Float)
|
|
speed = Column(Float)
|
|
incline = Column(Float)
|
|
resistance = Column(Integer)
|
|
|
|
notes = Column(String, default="")
|
|
sort_order = Column(Integer, default=0)
|
|
created_at = Column(String)
|
|
|
|
workout = relationship("Workout", back_populates="cardio_sessions")
|
|
|
|
|
|
class MetricType(Base):
|
|
__tablename__ = "metric_types"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
name = Column(String, unique=True, nullable=False)
|
|
unit = Column(String, default="см")
|
|
category = Column(String, default="body")
|
|
sort_order = Column(Integer, default=0)
|
|
created_at = Column(String)
|
|
|
|
metric_values = relationship("BodyMetricValue", back_populates="metric_type")
|
|
|
|
|
|
class BodyMetric(Base):
|
|
__tablename__ = "body_metrics"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
sync_id = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
owner_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
client_id = Column(Integer, ForeignKey("clients.id", ondelete="SET NULL"), index=True)
|
|
|
|
metric_date = Column(String, nullable=False)
|
|
notes = Column(String, default="")
|
|
created_at = Column(String)
|
|
|
|
client = relationship("Client", back_populates="body_metrics")
|
|
values = relationship(
|
|
"BodyMetricValue",
|
|
back_populates="body_metric",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
|
|
|
|
class BodyMetricValue(Base):
|
|
__tablename__ = "body_metric_values"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
|
|
body_metric_id = Column(
|
|
Integer,
|
|
ForeignKey("body_metrics.id", ondelete="CASCADE"),
|
|
index=True,
|
|
)
|
|
metric_type_id = Column(
|
|
Integer,
|
|
ForeignKey("metric_types.id", ondelete="CASCADE"),
|
|
index=True,
|
|
)
|
|
|
|
value = Column(Float, nullable=False)
|
|
|
|
body_metric = relationship("BodyMetric", back_populates="values")
|
|
metric_type = relationship("MetricType", back_populates="metric_values")
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint(
|
|
"body_metric_id",
|
|
"metric_type_id",
|
|
name="uq_body_metric_value_type",
|
|
),
|
|
)
|
|
|
|
|
|
class SyncLog(Base):
|
|
__tablename__ = "sync_log"
|
|
|
|
device_id = Column(String, primary_key=True)
|
|
last_sync = Column(String, nullable=False)
|
|
updated_at = Column(String)
|