init
This commit is contained in:
@@ -3,7 +3,7 @@ import { ObjectId } from "mongodb";
|
||||
import { formatDate, formatDateShort, formatDistance, formatDuration, formatPace } from "@/lib/format";
|
||||
import { fetchRecentWellness, type DayWellness } from "@/lib/garmin/wellness";
|
||||
import { getAuthorizedClient } from "@/lib/garmin/client";
|
||||
import { getRunningActivity, listRunningActivities, type RunningActivity } from "@/lib/models/running";
|
||||
import { getRunningActivity, listRunningActivities, type RunMetrics, type RunningActivity } from "@/lib/models/running";
|
||||
import { getStrengthWorkout, listStrengthWorkouts, type StrengthWorkout } from "@/lib/models/strength";
|
||||
import {
|
||||
getLatestAnalysisForTarget,
|
||||
@@ -28,6 +28,50 @@ Jeśli podano dane z poprzednich treningów, odnieś się do progresu (np. zmian
|
||||
type PreviousRun = { run: RunningActivity; analysis: AiAnalysis | null };
|
||||
type PreviousWorkout = { workout: StrengthWorkout; analysis: AiAnalysis | null };
|
||||
|
||||
function buildRunMetricsSummary(metrics: RunMetrics, totalDistanceM: number): string[] {
|
||||
const { distanceKm, hrBpm, gcbLeftPct } = metrics;
|
||||
if (!hrBpm && !gcbLeftPct) return [];
|
||||
const n = distanceKm.length;
|
||||
if (n < 8) return [];
|
||||
|
||||
const maxDist = Math.max(...distanceKm);
|
||||
const totalKm = totalDistanceM / 1000;
|
||||
const useIndex = maxDist === 0;
|
||||
|
||||
const position = (i: number) => (useIndex ? i / n : distanceKm[i] / maxDist);
|
||||
const kmLabel = (i: number) =>
|
||||
useIndex ? ((i / n) * totalKm).toFixed(1) : distanceKm[i].toFixed(1);
|
||||
const avg = (vals: number[]) =>
|
||||
vals.length ? Math.round(vals.reduce((s, v) => s + v, 0) / vals.length) : null;
|
||||
|
||||
const lines = [`Dane w trakcie biegu (4 kwartyle):`];
|
||||
for (let q = 0; q < 4; q++) {
|
||||
const from = q / 4;
|
||||
const to = (q + 1) / 4;
|
||||
const idx = Array.from({ length: n }, (_, i) => i).filter(
|
||||
(i) => position(i) >= from && position(i) < to
|
||||
);
|
||||
if (idx.length === 0) continue;
|
||||
|
||||
const parts = [`${kmLabel(idx[0])}–${kmLabel(idx[idx.length - 1])} km`];
|
||||
|
||||
if (hrBpm) {
|
||||
const vals = idx.map((i) => hrBpm[i]).filter((v) => v > 0);
|
||||
const a = avg(vals);
|
||||
if (a !== null) parts.push(`HR śr. ${a} bpm`);
|
||||
}
|
||||
if (gcbLeftPct) {
|
||||
const vals = idx.map((i) => gcbLeftPct[i]).filter((v) => v > 0);
|
||||
if (vals.length > 0) {
|
||||
const mean = vals.reduce((s, v) => s + v, 0) / vals.length;
|
||||
parts.push(`balans L/P ${mean.toFixed(1)}%/${(100 - mean).toFixed(1)}%`);
|
||||
}
|
||||
}
|
||||
lines.push(`- ${parts.join(", ")}`);
|
||||
}
|
||||
return lines.length > 1 ? lines : [];
|
||||
}
|
||||
|
||||
function buildRunningPrompt(activity: RunningActivity, previousRuns: PreviousRun[]): string {
|
||||
const lines = [
|
||||
`Przeanalizuj poniższy bieg i podaj krótkie podsumowanie oraz wskazówki potreningowe.`,
|
||||
@@ -58,6 +102,13 @@ function buildRunningPrompt(activity: RunningActivity, previousRuns: PreviousRun
|
||||
if (activity.aerobicTrainingEffect) lines.push(`Efekt treningowy aerobowy: ${activity.aerobicTrainingEffect.toFixed(1)}`);
|
||||
if (activity.anaerobicTrainingEffect) lines.push(`Efekt treningowy anaerobowy: ${activity.anaerobicTrainingEffect.toFixed(1)}`);
|
||||
|
||||
if (activity.runMetrics) {
|
||||
const metricLines = buildRunMetricsSummary(activity.runMetrics, activity.distanceM);
|
||||
if (metricLines.length > 0) {
|
||||
lines.push(``, ...metricLines);
|
||||
}
|
||||
}
|
||||
|
||||
if (previousRuns.length > 0) {
|
||||
lines.push(``, `Poprzednie biegi (od najnowszego):`);
|
||||
for (const { run, analysis } of previousRuns) {
|
||||
|
||||
Reference in New Issue
Block a user