# Спецификация 03: тренировки и CSV (AdminWorkoutsController) Источники: Ф-9.2, tech-req 2.4 (CSV), m04 (workout_metrics), m06 (facts_json workout-facts/1). ## 1. Список `GET /api/admin/workouts?athleteId&dateFrom&dateTo&limit&offset` (Bearer) → `{"total":N,"items":[карточки]}`; карточка: `workoutId, athleteId, athleteName, startedAt, endedAt, durationMin, status, sessionKind, avgHr, setsCount, exercisesCount, adherence` (adherence — из workout_metrics; нет строки → null). Все атлеты; `athleteId` опционален; limit ≤ 100 (default 50), offset ≥ 0. ## 2. Карточка `GET /api/admin/workouts/{id}` → карточка списка +: - `sets[]`: seq, exerciseName, weightKg, reps, inPlan, rpe, kneePain (из workout_select-агрегата m01; имя — по exercise_id из библиотеки атлета); - `planFact`: по каждому упражнению — план (активная версия: вес × повторы по сетам дня недели) против факта (подходы); план дня не сопоставился → только факт (как в m04 adherence fallback); - `timeline[]`: из `facts_json.laps[]` + `records[]` — по каждому подходу: `startOffsetS, durationS, avgHr, peakHr, restAfterS` (отдых = старт следующего lap − конец этого); `hrCurve[]`: `[{t,hr}]` из records (прореживание: не более 600 точек, шаг `duration/600`). facts_json нет → `timeline/hrCurve` пустые. ## 3. CSV (КУ-4) `GET /api/admin/workouts.csv?athleteId&dateFrom&dateTo` — та же выборка (без пагинации, лимит 10 000): - разделитель `;`, UTF-8 **c BOM**, заголовок: `athlete;date;duration_min;avg_hr;sets;exercises;adherence_pct`; - строка — тренировка: имя атлета; дата YYYY-MM-DD; длительность (мин, из ended−started; нет — пустая ячейка); avg_hr; число подходов; число упражнений; adherence (нет — пустая); - `Content-Type: text/csv; charset=utf-8`, `Content-Disposition: attachment; filename="workouts.csv"`. ## 4. Ошибки 401 без токена; 404 чужой/несуществующий id. 🛡3: только чтение.