71 lines
2.5 KiB
Python
71 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(
|
|
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
|
|
],
|
|
)
|