Files
SneakySwole/app/services/export_service.py
Phillip Tarrant ebecfd0b58 feat: add CSV export of workout history to dashboard
Add date range picker and download button to the dashboard page,
backed by GET /dashboard/export endpoint that returns a StreamingResponse
CSV file. Completes Phase 4 (final V2 improvement).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:22:50 -05:00

81 lines
2.5 KiB
Python

"""Export service for generating CSV-ready workout history data.
Queries workout logs joined with sessions, days, and exercises
to produce flat rows suitable for CSV export.
"""
from datetime import date
import structlog
from sqlmodel import Session, select
from app.models.exercise import Exercise
from app.models.workout_log import WorkoutLog
from app.models.workout_session import WorkoutSession
logger = structlog.get_logger(__name__)
class ExportService:
"""Builds export-ready workout history rows.
Args:
session: An active SQLModel Session.
"""
def __init__(self, session: Session) -> None:
self._session = session
def get_export_rows(
self, user_id: int, start_date: date, end_date: date,
) -> list[dict]:
"""Get flat workout log rows for CSV export.
Args:
user_id: The user whose data to export.
start_date: Inclusive start of date range.
end_date: Inclusive end of date range.
Returns:
List of dicts with keys: date, workout_type, exercise,
set_number, reps, weight, felt_easy.
"""
sessions = self._session.exec(
select(WorkoutSession)
.where(
WorkoutSession.user_id == user_id,
WorkoutSession.date >= start_date,
WorkoutSession.date <= end_date,
)
.order_by(WorkoutSession.date.asc())
).all()
if not sessions:
return []
# Pre-load exercises for name lookup
exercises = self._session.exec(select(Exercise)).all()
exercise_map = {e.id: e for e in exercises}
rows = []
for ws in sessions:
logs = self._session.exec(
select(WorkoutLog)
.where(WorkoutLog.session_id == ws.id)
.order_by(WorkoutLog.exercise_id, WorkoutLog.set_number)
).all()
for log_entry in logs:
exercise = exercise_map.get(log_entry.exercise_id)
rows.append({
"date": ws.date.isoformat(),
"workout_type": exercise.workout_day if exercise else "Unknown",
"exercise": exercise.name if exercise else "Unknown",
"set_number": log_entry.set_number,
"reps": log_entry.reps_completed,
"weight": log_entry.weight_used,
"felt_easy": "Yes" if log_entry.felt_easy else "No",
})
return rows