feat: define all 8 SQLModel tables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
"""SQLModel model definitions for SneakySwole.
|
||||||
|
|
||||||
|
All 8 tables are defined here and re-exported for convenient imports.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from app.models.user import User
|
||||||
|
from app.models.exercise import Exercise
|
||||||
|
from app.models.warmup import Warmup
|
||||||
|
from app.models.workout_day import WorkoutDay
|
||||||
|
from app.models.user_exercise_program import UserExerciseProgram
|
||||||
|
from app.models.workout_session import WorkoutSession
|
||||||
|
from app.models.workout_log import WorkoutLog
|
||||||
|
from app.models.progress_log import ProgressLog
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"User",
|
||||||
|
"Exercise",
|
||||||
|
"Warmup",
|
||||||
|
"WorkoutDay",
|
||||||
|
"UserExerciseProgram",
|
||||||
|
"WorkoutSession",
|
||||||
|
"WorkoutLog",
|
||||||
|
"ProgressLog",
|
||||||
|
]
|
||||||
|
|||||||
35
app/models/exercise.py
Normal file
35
app/models/exercise.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""Exercise model for the exercise library catalog.
|
||||||
|
|
||||||
|
Each exercise belongs to a workout day and includes form cues.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class Exercise(SQLModel, table=True):
|
||||||
|
"""An exercise in the workout library.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
name: Exercise name (e.g., "DB Chest Press (Floor)").
|
||||||
|
muscle_group: Target muscle group (e.g., "Chest", "Shoulders").
|
||||||
|
workout_day: Which day this exercise belongs to (Push/Pull/Lower/Full Body).
|
||||||
|
sets: Default number of sets.
|
||||||
|
tempo: Tempo notation (e.g., "3-1-2" = 3s eccentric, 1s pause, 2s concentric).
|
||||||
|
form_cues: Detailed form instructions.
|
||||||
|
created_at: Timestamp when the record was created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "exercises"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
name: str = Field(index=True)
|
||||||
|
muscle_group: str = Field(default="")
|
||||||
|
workout_day: str = Field(index=True)
|
||||||
|
sets: int = Field(default=3)
|
||||||
|
tempo: str = Field(default="")
|
||||||
|
form_cues: str = Field(default="")
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
39
app/models/progress_log.py
Normal file
39
app/models/progress_log.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
"""ProgressLog model for tracking progression suggestions and outcomes.
|
||||||
|
|
||||||
|
Records what the progression engine suggested vs what the user actually did.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressLog(SQLModel, table=True):
|
||||||
|
"""A progression tracking entry for a specific exercise.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
user_id: FK to users table.
|
||||||
|
exercise_id: FK to exercises table.
|
||||||
|
date: The date this progression entry applies to.
|
||||||
|
suggested_reps: What the engine recommended.
|
||||||
|
suggested_weight: What the engine recommended.
|
||||||
|
actual_reps: What the user actually did.
|
||||||
|
actual_weight: What the user actually used.
|
||||||
|
progression_applied: Type of progression (e.g., "reps_increase", "weight_increase", "deload").
|
||||||
|
created_at: Timestamp when the record was created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "progress_log"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
user_id: int = Field(foreign_key="users.id", index=True)
|
||||||
|
exercise_id: int = Field(foreign_key="exercises.id")
|
||||||
|
date: dt.date = Field(default_factory=dt.date.today)
|
||||||
|
suggested_reps: Optional[int] = Field(default=None)
|
||||||
|
suggested_weight: Optional[str] = Field(default=None)
|
||||||
|
actual_reps: Optional[int] = Field(default=None)
|
||||||
|
actual_weight: Optional[str] = Field(default=None)
|
||||||
|
progression_applied: Optional[str] = Field(default=None)
|
||||||
|
created_at: dt.datetime = Field(default_factory=dt.datetime.utcnow)
|
||||||
39
app/models/user.py
Normal file
39
app/models/user.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
"""User model for profile management.
|
||||||
|
|
||||||
|
Stores admin and regular user profiles with physical stats and goals.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class User(SQLModel, table=True):
|
||||||
|
"""A user profile in the system.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
username: Unique login identifier.
|
||||||
|
password_hash: bcrypt-hashed password (admin only initially).
|
||||||
|
display_name: Human-readable name shown in the UI.
|
||||||
|
height: User's height as a string (e.g., "6'0\"").
|
||||||
|
weight: User's weight as a string (e.g., "260 lbs").
|
||||||
|
goals: Free-text training goals.
|
||||||
|
is_admin: Whether this user has admin privileges.
|
||||||
|
created_at: Timestamp when the record was created.
|
||||||
|
updated_at: Timestamp of the last update.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "users"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
username: str = Field(index=True, unique=True)
|
||||||
|
password_hash: str = Field(default="")
|
||||||
|
display_name: str = Field(default="")
|
||||||
|
height: Optional[str] = Field(default=None)
|
||||||
|
weight: Optional[str] = Field(default=None)
|
||||||
|
goals: Optional[str] = Field(default=None)
|
||||||
|
is_admin: bool = Field(default=False)
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
37
app/models/user_exercise_program.py
Normal file
37
app/models/user_exercise_program.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"""UserExerciseProgram model for per-user exercise programming.
|
||||||
|
|
||||||
|
Links a user to an exercise with week 1 and week 4 rep/weight targets.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class UserExerciseProgram(SQLModel, table=True):
|
||||||
|
"""Per-user programming for a specific exercise.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
user_id: FK to users table.
|
||||||
|
exercise_id: FK to exercises table.
|
||||||
|
wk1_reps: Week 1 target reps (string to support "30 sec" style).
|
||||||
|
wk4_reps: Week 4 target reps.
|
||||||
|
wk1_weight: Week 1 target weight (e.g., "30 lbs", "BW").
|
||||||
|
wk4_weight: Week 4 target weight.
|
||||||
|
created_at: Timestamp when the record was created.
|
||||||
|
updated_at: Timestamp of the last update.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "user_exercise_programs"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
user_id: int = Field(foreign_key="users.id", index=True)
|
||||||
|
exercise_id: int = Field(foreign_key="exercises.id", index=True)
|
||||||
|
wk1_reps: str = Field(default="")
|
||||||
|
wk4_reps: str = Field(default="")
|
||||||
|
wk1_weight: str = Field(default="")
|
||||||
|
wk4_weight: str = Field(default="")
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
33
app/models/warmup.py
Normal file
33
app/models/warmup.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"""Warmup model for the standardized warmup routine.
|
||||||
|
|
||||||
|
Warmups are displayed before every workout in a fixed order.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class Warmup(SQLModel, table=True):
|
||||||
|
"""A warmup exercise in the standardized routine.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
name: Warmup name (e.g., "Cat / Cow").
|
||||||
|
type: Category (e.g., "Thoracic Mob", "Hip Mobility").
|
||||||
|
reps: Rep scheme as a string (e.g., "8 reps", "8 each side").
|
||||||
|
form_cues: Detailed form instructions.
|
||||||
|
sort_order: Display order in the warmup sequence.
|
||||||
|
created_at: Timestamp when the record was created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "warmups"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
name: str = Field(index=True)
|
||||||
|
type: str = Field(default="")
|
||||||
|
reps: str = Field(default="")
|
||||||
|
form_cues: str = Field(default="")
|
||||||
|
sort_order: int = Field(default=0)
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
26
app/models/workout_day.py
Normal file
26
app/models/workout_day.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
"""WorkoutDay model for the 4-day training split.
|
||||||
|
|
||||||
|
Defines the named workout days and their order.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class WorkoutDay(SQLModel, table=True):
|
||||||
|
"""A named workout day in the training program.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
name: Day name (Push, Pull, Lower, Full Body).
|
||||||
|
day_number: Order in the weekly rotation (1-4).
|
||||||
|
description: Brief description of the day's focus.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "workout_days"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
name: str = Field(index=True, unique=True)
|
||||||
|
day_number: int = Field(unique=True)
|
||||||
|
description: str = Field(default="")
|
||||||
37
app/models/workout_log.py
Normal file
37
app/models/workout_log.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"""WorkoutLog model for per-exercise set logging.
|
||||||
|
|
||||||
|
Each log entry records one set of one exercise within a session.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class WorkoutLog(SQLModel, table=True):
|
||||||
|
"""A single set log for an exercise within a workout session.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
session_id: FK to workout_sessions table.
|
||||||
|
exercise_id: FK to exercises table.
|
||||||
|
set_number: Which set this is (1, 2, 3...).
|
||||||
|
reps_completed: Actual reps performed.
|
||||||
|
weight_used: Weight used as string (e.g., "30 lbs", "BW").
|
||||||
|
felt_easy: Whether the user felt the set was easy (progression signal).
|
||||||
|
notes: Optional notes about this specific set.
|
||||||
|
created_at: Timestamp when the record was created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "workout_logs"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
session_id: int = Field(foreign_key="workout_sessions.id", index=True)
|
||||||
|
exercise_id: int = Field(foreign_key="exercises.id")
|
||||||
|
set_number: int = Field(default=1)
|
||||||
|
reps_completed: int = Field(default=0)
|
||||||
|
weight_used: str = Field(default="")
|
||||||
|
felt_easy: bool = Field(default=False)
|
||||||
|
notes: Optional[str] = Field(default=None)
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
31
app/models/workout_session.py
Normal file
31
app/models/workout_session.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
"""WorkoutSession model for tracking completed workout sessions.
|
||||||
|
|
||||||
|
Each session ties a user to a workout day on a specific date.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class WorkoutSession(SQLModel, table=True):
|
||||||
|
"""A completed workout session.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: Primary key, auto-incremented.
|
||||||
|
user_id: FK to users table.
|
||||||
|
workout_day_id: FK to workout_days table.
|
||||||
|
date: The date the workout was performed.
|
||||||
|
notes: Optional free-text notes about the session.
|
||||||
|
created_at: Timestamp when the record was created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "workout_sessions"
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
user_id: int = Field(foreign_key="users.id", index=True)
|
||||||
|
workout_day_id: int = Field(foreign_key="workout_days.id")
|
||||||
|
date: dt.date = Field(default_factory=dt.date.today)
|
||||||
|
notes: Optional[str] = Field(default=None)
|
||||||
|
created_at: dt.datetime = Field(default_factory=dt.datetime.utcnow)
|
||||||
228
tests/test_models.py
Normal file
228
tests/test_models.py
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
"""Tests for SQLModel model definitions."""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from sqlmodel import SQLModel, Session, create_engine
|
||||||
|
|
||||||
|
from app.models.user import User
|
||||||
|
from app.models.exercise import Exercise
|
||||||
|
from app.models.warmup import Warmup
|
||||||
|
from app.models.workout_day import WorkoutDay
|
||||||
|
from app.models.user_exercise_program import UserExerciseProgram
|
||||||
|
from app.models.workout_session import WorkoutSession
|
||||||
|
from app.models.workout_log import WorkoutLog
|
||||||
|
from app.models.progress_log import ProgressLog
|
||||||
|
|
||||||
|
|
||||||
|
class TestModels:
|
||||||
|
"""Tests that all models can be instantiated and persisted."""
|
||||||
|
|
||||||
|
def _create_engine(self):
|
||||||
|
"""Create an in-memory SQLite engine with all tables."""
|
||||||
|
engine = create_engine("sqlite:///:memory:")
|
||||||
|
SQLModel.metadata.create_all(engine)
|
||||||
|
return engine
|
||||||
|
|
||||||
|
def test_user_model_roundtrip(self) -> None:
|
||||||
|
"""User model should persist and retrieve correctly."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
user = User(
|
||||||
|
username="testuser",
|
||||||
|
password_hash="fakehash",
|
||||||
|
display_name="Test User",
|
||||||
|
height="6'0\"",
|
||||||
|
weight="200 lbs",
|
||||||
|
goals="Get strong",
|
||||||
|
is_admin=False,
|
||||||
|
)
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(user)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(user)
|
||||||
|
assert user.id is not None
|
||||||
|
assert user.username == "testuser"
|
||||||
|
assert user.is_admin is False
|
||||||
|
assert isinstance(user.created_at, datetime)
|
||||||
|
|
||||||
|
def test_exercise_model_roundtrip(self) -> None:
|
||||||
|
"""Exercise model should persist and retrieve correctly."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
exercise = Exercise(
|
||||||
|
name="DB Chest Press (Floor)",
|
||||||
|
muscle_group="Chest",
|
||||||
|
workout_day="Push",
|
||||||
|
sets=3,
|
||||||
|
tempo="3-1-2",
|
||||||
|
form_cues="Lie on the floor...",
|
||||||
|
)
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(exercise)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(exercise)
|
||||||
|
assert exercise.id is not None
|
||||||
|
assert exercise.name == "DB Chest Press (Floor)"
|
||||||
|
|
||||||
|
def test_warmup_model_roundtrip(self) -> None:
|
||||||
|
"""Warmup model should persist and retrieve correctly."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
warmup = Warmup(
|
||||||
|
name="Cat / Cow",
|
||||||
|
type="Thoracic Mob",
|
||||||
|
reps="8 reps",
|
||||||
|
form_cues="Start on hands and knees...",
|
||||||
|
sort_order=1,
|
||||||
|
)
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(warmup)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(warmup)
|
||||||
|
assert warmup.id is not None
|
||||||
|
|
||||||
|
def test_workout_day_model_roundtrip(self) -> None:
|
||||||
|
"""WorkoutDay model should persist and retrieve correctly."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
day = WorkoutDay(
|
||||||
|
name="Push",
|
||||||
|
day_number=1,
|
||||||
|
description="Chest, shoulders, triceps",
|
||||||
|
)
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(day)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(day)
|
||||||
|
assert day.id is not None
|
||||||
|
assert day.day_number == 1
|
||||||
|
|
||||||
|
def test_user_exercise_program_model_roundtrip(self) -> None:
|
||||||
|
"""UserExerciseProgram model should persist with FK references."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
with Session(engine) as session:
|
||||||
|
user = User(username="u", password_hash="h", display_name="U")
|
||||||
|
exercise = Exercise(
|
||||||
|
name="Test Ex", muscle_group="Test",
|
||||||
|
workout_day="Push", sets=3, tempo="3-1-2", form_cues="..."
|
||||||
|
)
|
||||||
|
session.add(user)
|
||||||
|
session.add(exercise)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(user)
|
||||||
|
session.refresh(exercise)
|
||||||
|
|
||||||
|
program = UserExerciseProgram(
|
||||||
|
user_id=user.id,
|
||||||
|
exercise_id=exercise.id,
|
||||||
|
wk1_reps="8",
|
||||||
|
wk4_reps="12",
|
||||||
|
wk1_weight="30 lbs",
|
||||||
|
wk4_weight="40 lbs",
|
||||||
|
)
|
||||||
|
session.add(program)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(program)
|
||||||
|
assert program.id is not None
|
||||||
|
assert program.user_id == user.id
|
||||||
|
|
||||||
|
def test_workout_session_model_roundtrip(self) -> None:
|
||||||
|
"""WorkoutSession model should persist correctly."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
with Session(engine) as session:
|
||||||
|
user = User(username="u", password_hash="h", display_name="U")
|
||||||
|
day = WorkoutDay(name="Push", day_number=1, description="Push day")
|
||||||
|
session.add(user)
|
||||||
|
session.add(day)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(user)
|
||||||
|
session.refresh(day)
|
||||||
|
|
||||||
|
ws = WorkoutSession(
|
||||||
|
user_id=user.id,
|
||||||
|
workout_day_id=day.id,
|
||||||
|
date=datetime.utcnow().date(),
|
||||||
|
)
|
||||||
|
session.add(ws)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(ws)
|
||||||
|
assert ws.id is not None
|
||||||
|
|
||||||
|
def test_workout_log_model_roundtrip(self) -> None:
|
||||||
|
"""WorkoutLog model should persist correctly."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
with Session(engine) as session:
|
||||||
|
user = User(username="u", password_hash="h", display_name="U")
|
||||||
|
day = WorkoutDay(name="Push", day_number=1, description="Push day")
|
||||||
|
exercise = Exercise(
|
||||||
|
name="Ex", muscle_group="Test",
|
||||||
|
workout_day="Push", sets=3, tempo="3-1-2", form_cues="..."
|
||||||
|
)
|
||||||
|
session.add_all([user, day, exercise])
|
||||||
|
session.commit()
|
||||||
|
session.refresh(user)
|
||||||
|
session.refresh(day)
|
||||||
|
session.refresh(exercise)
|
||||||
|
|
||||||
|
ws = WorkoutSession(
|
||||||
|
user_id=user.id,
|
||||||
|
workout_day_id=day.id,
|
||||||
|
date=datetime.utcnow().date(),
|
||||||
|
)
|
||||||
|
session.add(ws)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(ws)
|
||||||
|
|
||||||
|
log = WorkoutLog(
|
||||||
|
session_id=ws.id,
|
||||||
|
exercise_id=exercise.id,
|
||||||
|
set_number=1,
|
||||||
|
reps_completed=8,
|
||||||
|
weight_used="30 lbs",
|
||||||
|
felt_easy=False,
|
||||||
|
)
|
||||||
|
session.add(log)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(log)
|
||||||
|
assert log.id is not None
|
||||||
|
assert log.felt_easy is False
|
||||||
|
|
||||||
|
def test_progress_log_model_roundtrip(self) -> None:
|
||||||
|
"""ProgressLog model should persist correctly."""
|
||||||
|
engine = self._create_engine()
|
||||||
|
with Session(engine) as session:
|
||||||
|
user = User(username="u", password_hash="h", display_name="U")
|
||||||
|
exercise = Exercise(
|
||||||
|
name="Ex", muscle_group="Test",
|
||||||
|
workout_day="Push", sets=3, tempo="3-1-2", form_cues="..."
|
||||||
|
)
|
||||||
|
session.add_all([user, exercise])
|
||||||
|
session.commit()
|
||||||
|
session.refresh(user)
|
||||||
|
session.refresh(exercise)
|
||||||
|
|
||||||
|
pl = ProgressLog(
|
||||||
|
user_id=user.id,
|
||||||
|
exercise_id=exercise.id,
|
||||||
|
date=datetime.utcnow().date(),
|
||||||
|
suggested_reps=10,
|
||||||
|
suggested_weight="35 lbs",
|
||||||
|
actual_reps=10,
|
||||||
|
actual_weight="35 lbs",
|
||||||
|
progression_applied="reps_increase",
|
||||||
|
)
|
||||||
|
session.add(pl)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(pl)
|
||||||
|
assert pl.id is not None
|
||||||
|
|
||||||
|
def test_all_models_importable_from_init(self) -> None:
|
||||||
|
"""All models should be importable from app.models."""
|
||||||
|
from app.models import (
|
||||||
|
User, Exercise, Warmup, WorkoutDay,
|
||||||
|
UserExerciseProgram, WorkoutSession, WorkoutLog, ProgressLog,
|
||||||
|
)
|
||||||
|
assert User is not None
|
||||||
|
assert Exercise is not None
|
||||||
|
assert Warmup is not None
|
||||||
|
assert WorkoutDay is not None
|
||||||
|
assert UserExerciseProgram is not None
|
||||||
|
assert WorkoutSession is not None
|
||||||
|
assert WorkoutLog is not None
|
||||||
|
assert ProgressLog is not None
|
||||||
Reference in New Issue
Block a user