160 lines
5.2 KiB
Python
160 lines
5.2 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date, timedelta
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database import get_db
|
|
from app.models import User, Workout
|
|
from app.routers.auth import require_auth
|
|
from app.schemas import (
|
|
AdjustSetRequest,
|
|
CompleteSetRequest,
|
|
MoveWorkoutRequest,
|
|
PromptChoiceRequest,
|
|
SetWeightRequest,
|
|
WorkoutDetail,
|
|
)
|
|
from app.services.auth import get_or_create_profile
|
|
from app.services.reschedule import plan_move
|
|
from app.services.workout import (
|
|
adjust_completed_set,
|
|
complete_set,
|
|
load_workout,
|
|
pause_workout,
|
|
resolve_prompt,
|
|
resume_workout,
|
|
set_exercise_weight,
|
|
start_workout,
|
|
workout_detail,
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/workouts", tags=["workout-actions"])
|
|
|
|
|
|
@router.post("/{workout_id}/start", response_model=WorkoutDetail)
|
|
def api_start(workout_id: int, _: User = Depends(require_auth), db: Session = Depends(get_db)) -> WorkoutDetail:
|
|
w = start_workout(db, load_workout(db, workout_id))
|
|
return workout_detail(db, w)
|
|
|
|
|
|
@router.post("/{workout_id}/pause", response_model=WorkoutDetail)
|
|
def api_pause(workout_id: int, _: User = Depends(require_auth), db: Session = Depends(get_db)) -> WorkoutDetail:
|
|
w = pause_workout(db, load_workout(db, workout_id))
|
|
return workout_detail(db, w)
|
|
|
|
|
|
@router.post("/{workout_id}/resume", response_model=WorkoutDetail)
|
|
def api_resume(workout_id: int, _: User = Depends(require_auth), db: Session = Depends(get_db)) -> WorkoutDetail:
|
|
w = resume_workout(db, load_workout(db, workout_id))
|
|
return workout_detail(db, w)
|
|
|
|
|
|
@router.post("/{workout_id}/complete-set", response_model=WorkoutDetail)
|
|
def api_complete_set(
|
|
workout_id: int,
|
|
body: CompleteSetRequest,
|
|
_: User = Depends(require_auth),
|
|
db: Session = Depends(get_db),
|
|
) -> WorkoutDetail:
|
|
w = complete_set(
|
|
db,
|
|
load_workout(db, workout_id),
|
|
actual_reps=body.actual_reps,
|
|
actual_weight_kg=body.actual_weight_kg,
|
|
)
|
|
return workout_detail(db, w)
|
|
|
|
|
|
@router.post("/{workout_id}/exercises/{exercise_id}/weight", response_model=WorkoutDetail)
|
|
def api_set_weight(
|
|
workout_id: int,
|
|
exercise_id: int,
|
|
body: SetWeightRequest,
|
|
_: User = Depends(require_auth),
|
|
db: Session = Depends(get_db),
|
|
) -> WorkoutDetail:
|
|
workout = load_workout(db, workout_id)
|
|
exercise = next((e for e in workout.exercises if e.id == exercise_id), None)
|
|
if not exercise:
|
|
raise HTTPException(404, "Упражнение не найдено")
|
|
w = set_exercise_weight(db, workout, exercise, body.weight_kg)
|
|
return workout_detail(db, w)
|
|
|
|
|
|
@router.post("/{workout_id}/exercises/{exercise_id}/sets/{set_id}", response_model=WorkoutDetail)
|
|
def api_adjust_set(
|
|
workout_id: int,
|
|
exercise_id: int,
|
|
set_id: int,
|
|
body: AdjustSetRequest,
|
|
_: User = Depends(require_auth),
|
|
db: Session = Depends(get_db),
|
|
) -> WorkoutDetail:
|
|
workout = load_workout(db, workout_id)
|
|
exercise = next((e for e in workout.exercises if e.id == exercise_id), None)
|
|
if not exercise:
|
|
raise HTTPException(404, "Упражнение не найдено")
|
|
workout_set = next((s for s in exercise.sets if s.id == set_id), None)
|
|
if not workout_set:
|
|
raise HTTPException(404, "Подход не найден")
|
|
w = adjust_completed_set(
|
|
db,
|
|
workout,
|
|
exercise,
|
|
workout_set,
|
|
actual_weight_kg=body.actual_weight_kg,
|
|
actual_reps=body.actual_reps,
|
|
)
|
|
return workout_detail(db, w)
|
|
|
|
|
|
@router.post("/{workout_id}/move", response_model=WorkoutDetail)
|
|
def api_move(
|
|
workout_id: int,
|
|
body: MoveWorkoutRequest,
|
|
_: User = Depends(require_auth),
|
|
db: Session = Depends(get_db),
|
|
) -> WorkoutDetail:
|
|
workout = load_workout(db, workout_id)
|
|
profile = get_or_create_profile(db)
|
|
weekdays = profile.training_weekdays or []
|
|
if len(weekdays) != 3:
|
|
raise HTTPException(400, "Не заданы дни тренировок")
|
|
rows = db.query(Workout).filter(Workout.cycle_number == workout.cycle_number).all()
|
|
assigned = plan_move(rows, workout_id, body.scheduled_date, list(weekdays))
|
|
for row in rows:
|
|
if row.id in assigned:
|
|
row.scheduled_date = date(2099, 1, 1) + timedelta(days=row.id % 300)
|
|
db.flush()
|
|
by_id = {row.id: row for row in rows}
|
|
for workout_row_id, scheduled in assigned.items():
|
|
by_id[workout_row_id].scheduled_date = scheduled
|
|
db.commit()
|
|
return workout_detail(db, load_workout(db, workout_id))
|
|
|
|
|
|
@router.post("/{workout_id}/exercises/{exercise_id}/prompt", response_model=WorkoutDetail)
|
|
def api_prompt(
|
|
workout_id: int,
|
|
exercise_id: int,
|
|
body: PromptChoiceRequest,
|
|
_: User = Depends(require_auth),
|
|
db: Session = Depends(get_db),
|
|
) -> WorkoutDetail:
|
|
workout = load_workout(db, workout_id)
|
|
exercise = next((e for e in workout.exercises if e.id == exercise_id), None)
|
|
if not exercise:
|
|
raise HTTPException(404, "Упражнение не найдено")
|
|
w = resolve_prompt(
|
|
db,
|
|
workout,
|
|
exercise,
|
|
choice=body.choice,
|
|
weight_kg=body.weight_kg,
|
|
increase_kg=body.increase_kg,
|
|
decrease_kg=body.decrease_kg,
|
|
)
|
|
return workout_detail(db, w)
|