138 lines
3.5 KiB
Python
138 lines
3.5 KiB
Python
from datetime import date
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from pydantic import BaseModel
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session, selectinload
|
|
|
|
from app.auth import get_current_user
|
|
from app.database import get_db
|
|
from app.models.workout import Exercise, Workout, WorkoutSet
|
|
|
|
router = APIRouter(
|
|
prefix="/api", tags=["workouts"], dependencies=[Depends(get_current_user)]
|
|
)
|
|
|
|
|
|
# --- Schemas ---
|
|
|
|
|
|
class ExerciseCreate(BaseModel):
|
|
name: str
|
|
category: str
|
|
|
|
|
|
class ExerciseRead(ExerciseCreate):
|
|
id: int
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class WorkoutSetCreate(BaseModel):
|
|
exercise_id: int
|
|
set_number: int
|
|
reps: int | None = None
|
|
weight_lbs: float | None = None
|
|
duration_min: float | None = None
|
|
|
|
|
|
class WorkoutSetRead(WorkoutSetCreate):
|
|
id: int
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class WorkoutCreate(BaseModel):
|
|
date: date
|
|
name: str | None = None
|
|
notes: str | None = None
|
|
sets: list[WorkoutSetCreate] = []
|
|
|
|
|
|
class WorkoutRead(BaseModel):
|
|
id: int
|
|
date: date
|
|
name: str | None
|
|
notes: str | None
|
|
sets: list[WorkoutSetRead]
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# --- Exercise endpoints ---
|
|
|
|
|
|
@router.get("/exercises", response_model=list[ExerciseRead])
|
|
def list_exercises(db: Session = Depends(get_db)):
|
|
return db.scalars(select(Exercise).order_by(Exercise.name)).all()
|
|
|
|
|
|
@router.post("/exercises", response_model=ExerciseRead, status_code=201)
|
|
def create_exercise(body: ExerciseCreate, db: Session = Depends(get_db)):
|
|
ex = Exercise(**body.model_dump())
|
|
db.add(ex)
|
|
db.commit()
|
|
db.refresh(ex)
|
|
return ex
|
|
|
|
|
|
# --- Workout endpoints ---
|
|
|
|
|
|
@router.get("/workouts", response_model=list[WorkoutRead])
|
|
def list_workouts(
|
|
from_date: date | None = Query(None, alias="from"),
|
|
to_date: date | None = Query(None, alias="to"),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
q = (
|
|
select(Workout)
|
|
.options(selectinload(Workout.sets))
|
|
.order_by(Workout.date.desc())
|
|
)
|
|
if from_date:
|
|
q = q.where(Workout.date >= from_date)
|
|
if to_date:
|
|
q = q.where(Workout.date <= to_date)
|
|
return db.scalars(q).all()
|
|
|
|
|
|
@router.post("/workouts", response_model=WorkoutRead, status_code=201)
|
|
def create_workout(body: WorkoutCreate, db: Session = Depends(get_db)):
|
|
sets_data = body.sets
|
|
workout = Workout(
|
|
date=body.date,
|
|
name=body.name,
|
|
notes=body.notes,
|
|
sets=[WorkoutSet(**s.model_dump()) for s in sets_data],
|
|
)
|
|
db.add(workout)
|
|
db.commit()
|
|
db.refresh(workout)
|
|
return workout
|
|
|
|
|
|
@router.put("/workouts/{workout_id}", response_model=WorkoutRead)
|
|
def update_workout(
|
|
workout_id: int, body: WorkoutCreate, db: Session = Depends(get_db)
|
|
):
|
|
workout = db.get(Workout, workout_id, options=[selectinload(Workout.sets)])
|
|
if not workout:
|
|
raise HTTPException(status_code=404, detail="Workout not found")
|
|
workout.date = body.date
|
|
workout.name = body.name
|
|
workout.notes = body.notes
|
|
# Replace sets
|
|
workout.sets.clear()
|
|
for s in body.sets:
|
|
workout.sets.append(WorkoutSet(**s.model_dump()))
|
|
db.commit()
|
|
db.refresh(workout)
|
|
return workout
|
|
|
|
|
|
@router.delete("/workouts/{workout_id}", status_code=204)
|
|
def delete_workout(workout_id: int, db: Session = Depends(get_db)):
|
|
workout = db.get(Workout, workout_id)
|
|
if not workout:
|
|
raise HTTPException(status_code=404, detail="Workout not found")
|
|
db.delete(workout)
|
|
db.commit()
|