förbättreing tränings

This commit is contained in:
larssand
2026-08-28 21:44:23 +02:00
parent c6262aceab
commit 375bfb2416
14 changed files with 420 additions and 54 deletions

View File

@@ -136,6 +136,37 @@ function percentile(values, ratio) {
return sorted[index];
}
function average(values) {
return values.length ? Math.round(values.reduce((sum, value) => sum + value, 0) / values.length) : 0;
}
function consistencySpread(values) {
const mean = average(values);
return mean ? ((percentile(values, 0.9) - percentile(values, 0.1)) / mean) * 100 : 0;
}
export function buildTrainingAchievements(stats) {
const source = stats && typeof stats === "object" ? stats : {};
const definitions = [
["first_session", Number(source.totalSessions || 0), 1],
["laps_100", Number(source.totalLaps || 0), 100],
["laps_500", Number(source.totalLaps || 0), 500],
["laps_1000", Number(source.totalLaps || 0), 1000],
["laps_1500", Number(source.totalLaps || 0), 1500],
["days_3", Number(source.trainingDays || 0), 3],
["days_10", Number(source.trainingDays || 0), 10],
["controlled_5", Number(source.controlledLapStreak || 0), 5],
["five_minute", source.bestFiveMinuteResult ? 1 : 0, 1],
];
return definitions.map(([id, current, target]) => ({
id,
current: Math.max(0, current),
target,
percent: Math.min(100, Math.max(0, (current / target) * 100)),
unlocked: current >= target,
}));
}
function getLocalDayKey(timestamp) {
const date = new Date(Number(timestamp || 0));
if (Number.isNaN(date.getTime())) return "unknown";
@@ -267,18 +298,16 @@ export function buildDriverTrainingStats(state, driverId, classId = "") {
const laps = getDriverTrainingLaps(state, driverId, classId);
const lapTimes = laps.map((lap) => lap.lapMs);
const recentLapTimes = lapTimes.slice(-30);
const previousWindowLapTimes = lapTimes.slice(Math.max(0, lapTimes.length - 60), Math.max(0, lapTimes.length - 30));
const earlierLapTimes = lapTimes.slice(0, Math.max(0, lapTimes.length - recentLapTimes.length));
const totalLaps = laps.length;
const bestLapMs = totalLaps ? Math.min(...lapTimes) : 0;
const averageLapMs = totalLaps ? Math.round(lapTimes.reduce((sum, value) => sum + value, 0) / totalLaps) : 0;
const recentAverageMs = recentLapTimes.length
? Math.round(recentLapTimes.reduce((sum, value) => sum + value, 0) / recentLapTimes.length)
: 0;
const recentP10Ms = percentile(recentLapTimes, 0.1);
const recentP90Ms = percentile(recentLapTimes, 0.9);
const consistencySpreadPct = recentAverageMs
? ((recentP90Ms - recentP10Ms) / recentAverageMs) * 100
: 0;
const recentAverageMs = average(recentLapTimes);
const previousRecentAverageMs = average(previousWindowLapTimes);
const consistencySpreadPct = consistencySpread(recentLapTimes);
const previousConsistencySpreadPct = consistencySpread(previousWindowLapTimes);
const trendSampleReady = recentLapTimes.length >= 10 && previousWindowLapTimes.length >= 10;
const previousBestMs = earlierLapTimes.length ? Math.min(...earlierLapTimes) : 0;
const recentBestMs = recentLapTimes.length ? Math.min(...recentLapTimes) : 0;
const personalBestGainMs = previousBestMs && recentBestMs ? Math.max(0, previousBestMs - recentBestMs) : 0;
@@ -293,18 +322,23 @@ export function buildDriverTrainingStats(state, driverId, classId = "") {
const bestThreeConsecutive = getBestConsecutiveTrainingLaps(stints, 3);
const bestFiveMinuteResult = getBestTrainingDurationResult(stints, 5 * 60 * 1000);
return {
const stats = {
totalLaps,
totalSessions: new Set(laps.map((lap) => lap.sessionId)).size,
trainingDays: dailySummaries.length,
bestLapMs,
averageLapMs,
recentAverageMs,
previousRecentAverageMs,
averageTrendMs: trendSampleReady ? previousRecentAverageMs - recentAverageMs : 0,
recentBestMs,
previousBestMs,
personalBestGainMs,
controlledLapStreak,
consistencySpreadPct,
previousConsistencySpreadPct,
consistencyTrendPct: trendSampleReady ? previousConsistencySpreadPct - consistencySpreadPct : 0,
trendSampleReady,
recentLapCount: recentLapTimes.length,
lastLapAt: laps.at(-1)?.timestamp || 0,
dailySummaries,
@@ -312,6 +346,7 @@ export function buildDriverTrainingStats(state, driverId, classId = "") {
bestThreeConsecutive,
bestFiveMinuteResult,
};
return { ...stats, achievements: buildTrainingAchievements(stats) };
}
export function buildDriverTimingChannelStats(state, driverId, classId = "") {