förbättreing tränings
This commit is contained in:
@@ -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 = "") {
|
||||
|
||||
Reference in New Issue
Block a user