Fix lap limits and five-minute stint statistics

This commit is contained in:
larssand
2026-09-02 19:36:25 +02:00
parent f561dd8feb
commit 483a9dbd5a
14 changed files with 356 additions and 110 deletions

View File

@@ -51,6 +51,9 @@ function buildPublicTrainingProfile(state, shareToken, updatedAt, sinceTimestamp
timestamp: Math.max(0, Number(entry.timestamp || 0) || 0),
lapMs: Math.max(0, Math.round(Number(entry.lapMs || 0) || 0)),
sessionId: String(entry.sessionId || ""),
sourceId: String(entry.sourceId || entry.driverId || ""),
excludedFromPace: Boolean(entry.excludedFromPace),
stintBoundary: Boolean(entry.stintBoundary),
}))
.sort((left, right) => left.timestamp - right.timestamp);
const stats = buildPublicTrainingStats(classLaps);
@@ -148,9 +151,9 @@ function normalizePublicTrainingWindow(value) {
}
function buildPublicTrainingWindowStats(laps) {
const lapTimes = laps.map((lap) => Number(lap.lapMs || 0)).filter((lapMs) => lapMs > 500);
const lapTimes = laps.filter((lap) => !lap.excludedFromPace).map((lap) => Number(lap.lapMs || 0)).filter((lapMs) => lapMs > 500);
return {
laps: lapTimes.length,
laps: laps.length,
bestLapMs: lapTimes.length ? Math.min(...lapTimes) : 0,
averageLapMs: lapTimes.length ? Math.round(lapTimes.reduce((sum, lapMs) => sum + lapMs, 0) / lapTimes.length) : 0,
};
@@ -171,7 +174,8 @@ function buildPublicTrainingGoal(goal, currentLaps) {
}
function buildPublicTrainingStats(laps) {
const lapTimes = laps.map((lap) => lap.lapMs);
const paceLaps = laps.filter((lap) => !lap.excludedFromPace);
const lapTimes = paceLaps.map((lap) => lap.lapMs);
const recent = lapTimes.slice(-30);
const previous = lapTimes.slice(Math.max(0, lapTimes.length - 60), Math.max(0, lapTimes.length - 30));
const average = (values) => values.length ? Math.round(values.reduce((sum, value) => sum + value, 0) / values.length) : 0;
@@ -191,7 +195,7 @@ function buildPublicTrainingStats(laps) {
const previousConsistencySpreadPct = spread(previous);
const trendSampleReady = recent.length >= 10 && previous.length >= 10;
const daily = new Map();
laps.forEach((lap) => {
paceLaps.forEach((lap) => {
const date = new Date(lap.timestamp);
const dateKey = Number.isNaN(date.getTime()) ? "unknown" : `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
const day = daily.get(dateKey) || { dateKey, laps: 0, totalMs: 0, bestLapMs: 0, latestTimestamp: 0 };
@@ -265,24 +269,40 @@ function buildPublicTrainingAchievements(stats) {
}
function splitPublicTrainingStints(laps, breakThresholdMs = 2 * 60 * 1000) {
const stints = [];
const streams = new Map();
(Array.isArray(laps) ? laps : []).forEach((lap) => {
const previousStint = stints.at(-1);
const previousLap = previousStint?.laps.at(-1);
const lapStartedAt = Math.max(0, Number(lap.timestamp || 0) - Number(lap.lapMs || 0));
const breakMs = previousLap ? Math.max(0, Number(lap.timestamp || 0) - Number(previousLap.timestamp || 0)) : 0;
const startsNewStint = !previousStint
|| String(previousLap.sessionId || "") !== String(lap.sessionId || "")
|| breakMs > breakThresholdMs;
const normalizedLap = {
...lap,
excludedFromPace: Boolean(previousLap && breakMs > breakThresholdMs && Number(lap.lapMs || 0) > breakThresholdMs),
sessionId: String(lap.sessionId || ""),
sourceId: String(lap.sourceId || lap.driverId || "default"),
timestamp: Number(lap.timestamp || 0),
lapMs: Number(lap.lapMs || 0),
};
if (startsNewStint) {
stints.push({ sessionId: String(lap.sessionId || ""), laps: [normalizedLap] });
} else {
previousStint.laps.push(normalizedLap);
}
const key = `${normalizedLap.sessionId}:${normalizedLap.sourceId}`;
if (!streams.has(key)) streams.set(key, []);
streams.get(key).push(normalizedLap);
});
const stints = [];
streams.forEach((stream) => {
let current = null;
stream.sort((left, right) => left.timestamp - right.timestamp).forEach((lap) => {
const previousLap = current?.laps.at(-1);
const breakMs = previousLap ? Math.max(0, lap.timestamp - previousLap.timestamp) : 0;
const detectedBreak = Boolean(previousLap && breakMs > breakThresholdMs);
const explicitBoundary = Boolean(lap.stintBoundary);
const normalizedLap = {
...lap,
excludedFromPace: Boolean(lap.excludedFromPace || explicitBoundary || (detectedBreak && lap.lapMs > breakThresholdMs)),
stintBoundary: Boolean(explicitBoundary || detectedBreak),
};
if (!current || detectedBreak || explicitBoundary) {
current = { sessionId: lap.sessionId, sourceId: lap.sourceId, laps: [normalizedLap] };
stints.push(current);
} else {
current.laps.push(normalizedLap);
}
});
});
return stints;
}
@@ -319,6 +339,7 @@ function getPublicBestDurationResult(stints, targetMs) {
const lastLap = counted.at(-1);
if (!lastLap || Number(lastLap.timestamp || 0) < cutoffAt) continue;
const totalMs = Number(lastLap.timestamp || 0) - startedAt;
if (totalMs > targetMs + Number(lastLap.lapMs || 0)) continue;
const lapCount = counted.length;
const candidate = {
targetMs,