f1tness_parser/backend/app/api/v1/trainings.py

105 lines
3.8 KiB
Python

from typing import List
from fastapi import APIRouter, Depends, HTTPException
from app.api.deps import (
get_current_user,
get_training_repository,
get_user_repository,
require_superuser,
)
from app.core.database.repository import TrainingRepository
from app.core.database.user_repository import UserRepository
from app.core.dto.training import TrainingDTO
from app.core.dto.user import UserDTO, UserRole
from app.core.parsers.obsidian import parse_training_data
from app.schemas.trainings import TrainingUpdate
router = APIRouter()
async def _check_participant(
repository: UserRepository, user_id: int, role: UserRole
) -> None:
user = await repository.get(user_id)
if user is None or user.role != role:
raise HTTPException(
status_code=422, detail=f"User {user_id} is not an existing {role.value}"
)
async def _get_editable_training(
training_id: int, user: UserDTO, repository: TrainingRepository
) -> TrainingDTO:
"""A training the user may change: their own (as the trainee), or any for a superuser.
The trainer can only see the training. A training the user can't see at all is a 404.
"""
training = await repository.get(training_id, viewer=user)
if training is None:
raise HTTPException(status_code=404, detail="Training not found")
if not (user.is_superuser or training.trainee_id == user.id):
raise HTTPException(status_code=403, detail="Only the trainee can change a training")
return training
@router.get("/obsidian/", dependencies=[Depends(require_superuser)])
async def obsidian_trainings_list():
return {"data": parse_training_data()}
@router.get("/", response_model=List[TrainingDTO])
async def get_trainings_list(
user: UserDTO = Depends(get_current_user),
repository: TrainingRepository = Depends(get_training_repository),
):
"""Trainings the user takes part in (all of them for a superuser)."""
return await repository.list(viewer=user)
@router.get("/{training_id}/", response_model=TrainingDTO)
async def get_training(
training_id: int,
user: UserDTO = Depends(get_current_user),
repository: TrainingRepository = Depends(get_training_repository),
):
training = await repository.get(training_id, viewer=user)
if training is None:
raise HTTPException(status_code=404, detail="Training not found")
return training
@router.put("/{training_id}/", response_model=TrainingDTO)
async def update_training(
training_id: int,
payload: TrainingUpdate,
user: UserDTO = Depends(get_current_user),
repository: TrainingRepository = Depends(get_training_repository),
user_repository: UserRepository = Depends(get_user_repository),
):
"""Replace the training: its date, trainee, trainer, exercises and approaches."""
current = await _get_editable_training(training_id, user, repository)
if payload.trainee_id != current.trainee_id:
if not user.is_superuser:
raise HTTPException(
status_code=403, detail="Only a superuser can change the trainee"
)
await _check_participant(user_repository, payload.trainee_id, UserRole.trainee)
if payload.trainer_id is not None:
await _check_participant(user_repository, payload.trainer_id, UserRole.trainer)
training = TrainingDTO.model_validate(payload.model_dump())
if not await repository.replace(training_id, training):
raise HTTPException(status_code=404, detail="Training not found")
return await repository.get(training_id)
@router.delete("/{training_id}/")
async def delete_training(
training_id: int,
user: UserDTO = Depends(get_current_user),
repository: TrainingRepository = Depends(get_training_repository),
):
await _get_editable_training(training_id, user, repository)
await repository.delete([training_id])
return {"status": "ok"}