add more stats to mobile and fix refresh stint

This commit is contained in:
larssand
2026-09-04 16:41:51 +02:00
parent 4068cc7c19
commit 285babcfcf
10 changed files with 370 additions and 23 deletions

View File

@@ -56,7 +56,7 @@ function filterPublicTrainingStats(stats, visibility) {
outlierLaps: stats.outlierLaps, strongFinishStints: stats.strongFinishStints,
cleanFiveMinuteStints: stats.cleanFiveMinuteStints, stintCount: stats.stintCount,
stintSummaries: stats.stintSummaries, bestThreeConsecutive: stats.bestThreeConsecutive,
bestFiveMinuteResult: stats.bestFiveMinuteResult,
bestFiveMinuteResult: stats.bestFiveMinuteResult, paceBuckets: stats.paceBuckets,
});
if (visibility.history) filtered.dailySummaries = stats.dailySummaries;
return filtered;
@@ -91,6 +91,8 @@ function buildPublicTrainingProfile(state, shareToken, updatedAt, sinceTimestamp
}))
.sort((left, right) => left.timestamp - right.timestamp);
const stats = buildPublicTrainingStats(classLaps);
const classMarkers = (Array.isArray(state?.training?.markers) ? state.training.markers : []).filter((marker) => classDriverIds.has(String(marker?.driverId || "")) && String(marker?.classId || "") === classId);
stats.paceBuckets = buildPublicTrainingPaceBuckets(classLaps, classMarkers);
const channelStats = buildPublicTimingChannelStats(state, classDriverIds);
const classGoals = (Array.isArray(state?.training?.goals) ? state.training.goals : [])
.filter((goal) => profileReferences.has(String(goal?.driverId || "")) && (!goal?.classId || String(goal.classId) === classId))
@@ -217,6 +219,31 @@ function buildPublicTrainingGoal(goal, currentLaps) {
};
}
function buildPublicTrainingPaceBuckets(laps, markers, bucketMinutes = 30) {
const bucketMs = Math.max(5, Number(bucketMinutes || 30)) * 60 * 1000;
const buckets = new Map();
laps.filter((lap) => !lap.excludedFromPace).forEach((lap) => {
const date = new Date(lap.timestamp);
const minuteBlock = bucketMs / 60000;
const startTs = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), Math.floor(date.getMinutes() / minuteBlock) * minuteBlock, 0, 0).getTime();
const bucket = buckets.get(startTs) || { startTs, values: [] };
bucket.values.push(lap.lapMs);
buckets.set(startTs, bucket);
});
return [...buckets.values()].sort((left, right) => right.startTs - left.startTs).slice(0, 24).map((bucket) => {
const sorted = [...bucket.values].sort((left, right) => left - right);
const trim = sorted.length >= 5 ? Math.max(1, Math.floor(sorted.length * 0.1)) : 0;
const kept = trim ? sorted.slice(trim, sorted.length - trim) : sorted;
return {
startTs: bucket.startTs,
lapCount: bucket.values.length,
paceMs: kept.length ? Math.round(kept.reduce((sum, value) => sum + value, 0) / kept.length) : 0,
bestLapMs: bucket.values.length ? Math.min(...bucket.values) : 0,
markerCount: markers.filter((marker) => Number(marker?.timestamp || 0) >= bucket.startTs && Number(marker?.timestamp || 0) < bucket.startTs + bucketMs).length,
};
});
}
function buildPublicTrainingStats(laps) {
const paceLaps = laps.filter((lap) => !lap.excludedFromPace);
const lapTimes = paceLaps.map((lap) => lap.lapMs);
@@ -449,6 +476,25 @@ function getPublicBestDurationResult(stints, targetMs) {
return best;
}
function addPublicTrainingMarker(state, shareToken, input, timestamp = Date.now()) {
const share = findPublicTrainingShare(state, shareToken);
if (!share) return null;
const drivers = Array.isArray(state?.drivers) ? state.drivers : [];
const profile = getPublicDriverProfile(drivers, share.driverId);
const classId = String(input?.classId || "");
const classDriver = profile?.drivers.find((driver) => String(driver?.classId || "") === classId);
if (!classDriver) return null;
const type = ["setup", "tires", "suspension", "car", "note"].includes(String(input?.type || "")) ? String(input.type) : "car";
const marker = {
id: "training_marker_public_" + Math.round(timestamp) + "_" + Math.random().toString(36).slice(2, 9),
driverId: String(classDriver.id || ""), classId, sessionId: "", timestamp: Math.round(timestamp), type,
note: String(input?.note || "").trim().slice(0, 160), createdAt: new Date(timestamp).toISOString(),
};
if (!state.training || typeof state.training !== "object") state.training = {};
state.training.markers = [...(Array.isArray(state.training.markers) ? state.training.markers : []), marker];
return marker;
}
function normalizePublicLogoDataUrl(value) {
const logo = String(value || "").trim();
return logo.length <= 2_000_000 && /^data:image\/(?:png|jpeg|webp);base64,/i.test(logo) ? logo : "";
@@ -482,4 +528,5 @@ module.exports = {
splitPublicTrainingStints,
getPublicBestConsecutiveLaps,
getPublicBestDurationResult,
addPublicTrainingMarker,
};