possibly auth
This commit is contained in:
+216
-74
@@ -1,82 +1,191 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
from typing import List
|
||||
|
||||
import auth
|
||||
import database
|
||||
import models
|
||||
import schemas
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter(prefix="/workouts", tags=["workouts"])
|
||||
|
||||
def get_current_timestamp():
|
||||
return datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%fZ')
|
||||
router = APIRouter(
|
||||
prefix="/workouts",
|
||||
tags=["workouts"],
|
||||
dependencies=[Depends(auth.get_current_user)],
|
||||
)
|
||||
|
||||
@router.post("/", response_model=schemas.Workout)
|
||||
def create_workout(workout: schemas.WorkoutCreate, db: Session = Depends(database.get_db)):
|
||||
# Проверяем уникальность sync_id
|
||||
db_workout = db.query(models.Workout).filter(models.Workout.sync_id == workout.sync_id).first()
|
||||
if db_workout:
|
||||
raise HTTPException(status_code=400, detail="Workout with this sync_id already exists")
|
||||
|
||||
def get_current_timestamp() -> str:
|
||||
return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%fZ")
|
||||
|
||||
|
||||
def new_sync_id() -> str:
|
||||
return uuid.uuid4().hex
|
||||
|
||||
|
||||
def get_owned_workout(
|
||||
db: Session,
|
||||
workout_id: int,
|
||||
user: models.User,
|
||||
) -> models.Workout:
|
||||
workout = (
|
||||
db.query(models.Workout)
|
||||
.filter(
|
||||
models.Workout.id == workout_id,
|
||||
models.Workout.owner_id == user.id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not workout:
|
||||
raise HTTPException(status_code=404, detail="Workout not found")
|
||||
return workout
|
||||
|
||||
|
||||
def validate_client(
|
||||
db: Session,
|
||||
client_id: Optional[int],
|
||||
user: models.User,
|
||||
) -> Optional[int]:
|
||||
if not client_id:
|
||||
return None
|
||||
|
||||
client = (
|
||||
db.query(models.Client)
|
||||
.filter(
|
||||
models.Client.id == client_id,
|
||||
models.Client.owner_id == user.id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not client:
|
||||
raise HTTPException(status_code=404, detail="Client not found")
|
||||
return client.id
|
||||
|
||||
|
||||
def validate_workout_type(
|
||||
db: Session,
|
||||
workout_type_id: Optional[int],
|
||||
) -> Optional[int]:
|
||||
if not workout_type_id:
|
||||
return None
|
||||
|
||||
wt = db.get(models.WorkoutType, workout_type_id)
|
||||
if not wt:
|
||||
raise HTTPException(status_code=422, detail="Workout type not found")
|
||||
return wt.id
|
||||
|
||||
|
||||
def create_set_record(
|
||||
db: Session,
|
||||
workout_exercise_id: int,
|
||||
set_data: schemas.SetCreate,
|
||||
timestamp: str,
|
||||
) -> models.Set:
|
||||
is_dropset = 1 if set_data.is_dropset else 0
|
||||
|
||||
if is_dropset:
|
||||
if set_data.weight is not None or set_data.reps is not None:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="Dropset must not have top-level weight/reps",
|
||||
)
|
||||
if not set_data.dropset_items:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="Dropset must have at least one dropset item",
|
||||
)
|
||||
else:
|
||||
if set_data.weight is None or set_data.reps is None:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="Regular set must have weight and reps",
|
||||
)
|
||||
|
||||
db_set = models.Set(
|
||||
sync_id=set_data.sync_id or new_sync_id(),
|
||||
workout_exercise_id=workout_exercise_id,
|
||||
is_dropset=is_dropset,
|
||||
weight=None if is_dropset else float(set_data.weight),
|
||||
reps=None if is_dropset else int(set_data.reps),
|
||||
rating=set_data.rating or 3,
|
||||
notes=set_data.notes or "",
|
||||
sort_order=set_data.sort_order or 0,
|
||||
timestamp=set_data.timestamp or timestamp,
|
||||
created_at=timestamp,
|
||||
)
|
||||
|
||||
db.add(db_set)
|
||||
db.flush()
|
||||
|
||||
if is_dropset:
|
||||
for idx, item in enumerate(set_data.dropset_items or []):
|
||||
db.add(
|
||||
models.DropsetItem(
|
||||
set_id=db_set.id,
|
||||
weight=float(item.weight),
|
||||
reps=int(item.reps),
|
||||
sort_order=item.sort_order if item.sort_order is not None else idx,
|
||||
)
|
||||
)
|
||||
|
||||
return db_set
|
||||
|
||||
|
||||
@router.post("/", response_model=schemas.WorkoutOut)
|
||||
def create_workout(
|
||||
workout: schemas.WorkoutCreate,
|
||||
current_user: models.User = Depends(auth.get_current_user),
|
||||
db: Session = Depends(database.get_db),
|
||||
):
|
||||
timestamp = get_current_timestamp()
|
||||
sync_id = workout.sync_id or new_sync_id()
|
||||
|
||||
existing = db.query(models.Workout).filter(models.Workout.sync_id == sync_id).first()
|
||||
if existing:
|
||||
if existing.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=409, detail="Workout sync_id belongs to another user")
|
||||
return existing
|
||||
|
||||
client_id = validate_client(db, workout.client_id, current_user)
|
||||
workout_type_id = validate_workout_type(db, workout.workout_type_id)
|
||||
|
||||
db_workout = models.Workout(
|
||||
sync_id=workout.sync_id,
|
||||
client_id=workout.client_id,
|
||||
workout_type_id=workout.workout_type_id,
|
||||
sync_id=sync_id,
|
||||
owner_id=current_user.id,
|
||||
client_id=client_id,
|
||||
workout_type_id=workout_type_id,
|
||||
start_time=workout.start_time,
|
||||
end_time=workout.end_time,
|
||||
notes=workout.notes,
|
||||
notes=workout.notes or "",
|
||||
synced=1,
|
||||
created_at=timestamp,
|
||||
updated_at=timestamp
|
||||
updated_at=timestamp,
|
||||
)
|
||||
db.add(db_workout)
|
||||
db.flush() # Получаем ID
|
||||
|
||||
# Добавляем упражнения
|
||||
db.add(db_workout)
|
||||
db.flush()
|
||||
|
||||
for we_data in workout.workout_exercises or []:
|
||||
db_we = models.WorkoutExercise(
|
||||
sync_id=we_data.sync_id,
|
||||
sync_id=we_data.sync_id or new_sync_id(),
|
||||
workout_id=db_workout.id,
|
||||
exercise_id=we_data.exercise_id,
|
||||
exercise_name=we_data.exercise_name,
|
||||
sort_order=we_data.sort_order,
|
||||
created_at=timestamp
|
||||
sort_order=we_data.sort_order or 0,
|
||||
created_at=timestamp,
|
||||
)
|
||||
db.add(db_we)
|
||||
db.flush()
|
||||
|
||||
# Добавляем подходы
|
||||
for set_data in we_data.sets or []:
|
||||
db_set = models.Set(
|
||||
sync_id=set_data.sync_id,
|
||||
workout_exercise_id=db_we.id,
|
||||
is_dropset=set_data.is_dropset,
|
||||
weight=set_data.weight,
|
||||
reps=set_data.reps,
|
||||
rating=set_data.rating,
|
||||
notes=set_data.notes,
|
||||
sort_order=set_data.sort_order,
|
||||
timestamp=set_data.timestamp,
|
||||
created_at=timestamp
|
||||
)
|
||||
db.add(db_set)
|
||||
db.flush()
|
||||
create_set_record(db, db_we.id, set_data, timestamp)
|
||||
|
||||
# Добавляем элементы дроп-сета
|
||||
if set_data.is_dropset:
|
||||
for item_data in set_data.dropset_items or []:
|
||||
db_item = models.DropsetItem(
|
||||
set_id=db_set.id,
|
||||
weight=item_data.weight,
|
||||
reps=item_data.reps,
|
||||
sort_order=item_data.sort_order
|
||||
)
|
||||
db.add(db_item)
|
||||
|
||||
# Добавляем кардио
|
||||
for cardio_data in workout.cardio_sessions or []:
|
||||
db_cardio = models.Cardio(
|
||||
sync_id=cardio_data.sync_id,
|
||||
sync_id=cardio_data.sync_id or new_sync_id(),
|
||||
workout_id=db_workout.id,
|
||||
cardio_type=cardio_data.cardio_type,
|
||||
purpose=cardio_data.purpose,
|
||||
@@ -85,9 +194,9 @@ def create_workout(workout: schemas.WorkoutCreate, db: Session = Depends(databas
|
||||
speed=cardio_data.speed,
|
||||
incline=cardio_data.incline,
|
||||
resistance=cardio_data.resistance,
|
||||
notes=cardio_data.notes,
|
||||
sort_order=cardio_data.sort_order,
|
||||
created_at=timestamp
|
||||
notes=cardio_data.notes or "",
|
||||
sort_order=cardio_data.sort_order or 0,
|
||||
created_at=timestamp,
|
||||
)
|
||||
db.add(db_cardio)
|
||||
|
||||
@@ -95,45 +204,75 @@ def create_workout(workout: schemas.WorkoutCreate, db: Session = Depends(databas
|
||||
db.refresh(db_workout)
|
||||
return db_workout
|
||||
|
||||
@router.get("/", response_model=List[schemas.Workout])
|
||||
|
||||
@router.get("/", response_model=List[schemas.WorkoutOut])
|
||||
def list_workouts(
|
||||
client_id: int = None,
|
||||
client_id: Optional[int] = None,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(database.get_db)
|
||||
current_user: models.User = Depends(auth.get_current_user),
|
||||
db: Session = Depends(database.get_db),
|
||||
):
|
||||
query = db.query(models.Workout).options(
|
||||
joinedload(models.Workout.workout_exercises)
|
||||
.joinedload(models.WorkoutExercise.sets)
|
||||
.joinedload(models.Set.dropset_items),
|
||||
joinedload(models.Workout.cardio_sessions)
|
||||
joinedload(models.Workout.cardio_sessions),
|
||||
).filter(
|
||||
models.Workout.owner_id == current_user.id
|
||||
)
|
||||
|
||||
if client_id:
|
||||
validate_client(db, client_id, current_user)
|
||||
query = query.filter(models.Workout.client_id == client_id)
|
||||
|
||||
return query.order_by(models.Workout.start_time.desc()).offset(skip).limit(limit).all()
|
||||
|
||||
@router.get("/{workout_id}", response_model=schemas.Workout)
|
||||
def get_workout(workout_id: int, db: Session = Depends(database.get_db)):
|
||||
db_workout = db.query(models.Workout).options(
|
||||
joinedload(models.Workout.workout_exercises)
|
||||
.joinedload(models.WorkoutExercise.sets)
|
||||
.joinedload(models.Set.dropset_items),
|
||||
joinedload(models.Workout.cardio_sessions)
|
||||
).filter(models.Workout.id == workout_id).first()
|
||||
|
||||
@router.get("/{workout_id}", response_model=schemas.WorkoutOut)
|
||||
def get_workout(
|
||||
workout_id: int,
|
||||
current_user: models.User = Depends(auth.get_current_user),
|
||||
db: Session = Depends(database.get_db),
|
||||
):
|
||||
db_workout = (
|
||||
db.query(models.Workout)
|
||||
.options(
|
||||
joinedload(models.Workout.workout_exercises)
|
||||
.joinedload(models.WorkoutExercise.sets)
|
||||
.joinedload(models.Set.dropset_items),
|
||||
joinedload(models.Workout.cardio_sessions),
|
||||
)
|
||||
.filter(
|
||||
models.Workout.id == workout_id,
|
||||
models.Workout.owner_id == current_user.id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
if not db_workout:
|
||||
raise HTTPException(status_code=404, detail="Workout not found")
|
||||
|
||||
return db_workout
|
||||
|
||||
@router.put("/{workout_id}", response_model=schemas.Workout)
|
||||
def update_workout(workout_id: int, workout: schemas.WorkoutUpdate, db: Session = Depends(database.get_db)):
|
||||
db_workout = db.query(models.Workout).filter(models.Workout.id == workout_id).first()
|
||||
if not db_workout:
|
||||
raise HTTPException(status_code=404, detail="Workout not found")
|
||||
|
||||
@router.put("/{workout_id}", response_model=schemas.WorkoutOut)
|
||||
def update_workout(
|
||||
workout_id: int,
|
||||
workout: schemas.WorkoutUpdate,
|
||||
current_user: models.User = Depends(auth.get_current_user),
|
||||
db: Session = Depends(database.get_db),
|
||||
):
|
||||
db_workout = get_owned_workout(db, workout_id, current_user)
|
||||
|
||||
update_data = workout.dict(exclude_unset=True)
|
||||
|
||||
if "workout_type_id" in update_data:
|
||||
update_data["workout_type_id"] = validate_workout_type(
|
||||
db,
|
||||
update_data["workout_type_id"],
|
||||
)
|
||||
|
||||
for key, value in update_data.items():
|
||||
setattr(db_workout, key, value)
|
||||
|
||||
@@ -142,11 +281,14 @@ def update_workout(workout_id: int, workout: schemas.WorkoutUpdate, db: Session
|
||||
db.refresh(db_workout)
|
||||
return db_workout
|
||||
|
||||
|
||||
@router.delete("/{workout_id}")
|
||||
def delete_workout(workout_id: int, db: Session = Depends(database.get_db)):
|
||||
db_workout = db.query(models.Workout).filter(models.Workout.id == workout_id).first()
|
||||
if not db_workout:
|
||||
raise HTTPException(status_code=404, detail="Workout not found")
|
||||
def delete_workout(
|
||||
workout_id: int,
|
||||
current_user: models.User = Depends(auth.get_current_user),
|
||||
db: Session = Depends(database.get_db),
|
||||
):
|
||||
db_workout = get_owned_workout(db, workout_id, current_user)
|
||||
db.delete(db_workout)
|
||||
db.commit()
|
||||
return {"status": "deleted"}
|
||||
|
||||
Reference in New Issue
Block a user