possibly auth

This commit is contained in:
2026-10-03 09:44:11 +03:00
parent 86bcd5b141
commit ea21db141a
12 changed files with 1780 additions and 591 deletions
+216 -74
View File
@@ -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"}