f1tness_parser/backend/app/api/v1/exercises.py
2026-10-23 20:21:48 +03:00

72 lines
2.5 KiB
Python

from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException
from app.api.deps import get_training_repository
from app.core.database.repository import TrainingRepository
from app.core.dto.training import ExerciseNameDTO
from app.core.metrics import Record, summarize
from app.schemas.exercises import (
ExerciseProgressResponse,
RecordResponse,
SessionMetricsResponse,
SessionRowResponse,
)
router = APIRouter()
def record_response(record: Optional[Record]) -> Optional[RecordResponse]:
return RecordResponse(value=record.value, date=record.date) if record else None
@router.get("/", response_model=List[ExerciseNameDTO])
async def list_exercise_names(
repository: TrainingRepository = Depends(get_training_repository),
):
"""Distinct exercise names with the number of times each was done."""
return await repository.list_exercise_names()
@router.get("/progress/", response_model=ExerciseProgressResponse)
async def exercise_progress(
name: str,
repository: TrainingRepository = Depends(get_training_repository),
):
"""The whole history of the exercise with progress metrics.
The name is matched exactly, but case-insensitively: what was typed may differ in case.
"""
names_by_casefold = {
item.name.casefold(): item.name
for item in await repository.list_exercise_names()
}
exact_name = names_by_casefold.get(name.strip().casefold())
if exact_name is None:
raise HTTPException(status_code=404, detail="Exercise not found")
progress = summarize(await repository.exercise_history(exact_name))
return ExerciseProgressResponse(
name=exact_name,
weighted=progress.weighted,
max_weight=progress.max_weight,
best_primary=record_response(progress.best_primary),
best_secondary=record_response(progress.best_secondary),
first_primary=progress.first_primary,
last_primary=progress.last_primary,
primary_change=progress.primary_change,
primary_change_percent=progress.primary_change_percent,
rows=[
SessionRowResponse(
training_id=row.session.training_id,
date=row.session.date,
trainer=row.session.trainer,
splitted_weight=row.session.splitted_weight,
approaches=row.session.approaches,
metrics=SessionMetricsResponse.model_validate(
row.metrics, from_attributes=True
),
)
for row in progress.rows
],
)