add more stats to mobile profile

This commit is contained in:
larssand
2026-09-04 16:17:45 +02:00
parent 142c45be04
commit 4068cc7c19
13 changed files with 267 additions and 22 deletions

View File

@@ -29,9 +29,43 @@ function getPublicDriverProfile(drivers, reference) {
};
}
const PUBLIC_TRAINING_SECTION_KEYS = ["progress", "achievements", "performance", "goals", "history"];
function normalizePublicTrainingVisibility(value) {
const source = value && typeof value === "object" ? value : {};
return Object.fromEntries(PUBLIC_TRAINING_SECTION_KEYS.map((key) => [key, source[key] !== false]));
}
function filterPublicTrainingStats(stats, visibility) {
const filtered = {
totalLaps: stats.totalLaps, totalSessions: stats.totalSessions, trainingDays: stats.trainingDays,
bestLapMs: stats.bestLapMs, averageLapMs: stats.averageLapMs, recentLapCount: stats.recentLapCount,
consistencySpreadPct: stats.consistencySpreadPct,
};
if (visibility.progress) Object.assign(filtered, {
recentAverageMs: stats.recentAverageMs, previousRecentAverageMs: stats.previousRecentAverageMs,
averageTrendMs: stats.averageTrendMs, previousConsistencySpreadPct: stats.previousConsistencySpreadPct,
consistencyTrendPct: stats.consistencyTrendPct, trendSampleReady: stats.trendSampleReady,
controlledLapStreak: stats.controlledLapStreak, todayControlledLapStreak: stats.todayControlledLapStreak,
todayStrongFinishStints: stats.todayStrongFinishStints, todayCleanFiveMinuteStints: stats.todayCleanFiveMinuteStints,
todayCompletedFiveMinuteStints: stats.todayCompletedFiveMinuteStints, paceHistory: stats.dailySummaries.slice(0, 12),
});
if (visibility.achievements) filtered.achievements = stats.achievements;
if (visibility.performance) Object.assign(filtered, {
controlZoneMinMs: stats.controlZoneMinMs, controlZoneMaxMs: stats.controlZoneMaxMs,
outlierLaps: stats.outlierLaps, strongFinishStints: stats.strongFinishStints,
cleanFiveMinuteStints: stats.cleanFiveMinuteStints, stintCount: stats.stintCount,
stintSummaries: stats.stintSummaries, bestThreeConsecutive: stats.bestThreeConsecutive,
bestFiveMinuteResult: stats.bestFiveMinuteResult,
});
if (visibility.history) filtered.dailySummaries = stats.dailySummaries;
return filtered;
}
function buildPublicTrainingProfile(state, shareToken, updatedAt, sinceTimestamp = 0, windowMs = 0) {
const share = findPublicTrainingShare(state, shareToken);
if (!share) return null;
const visibility = normalizePublicTrainingVisibility(share.visibility);
const profileReference = String(share.driverId || "");
const drivers = Array.isArray(state?.drivers) ? state.drivers : [];
const driverProfile = getPublicDriverProfile(drivers, profileReference);
@@ -66,11 +100,11 @@ function buildPublicTrainingProfile(state, shareToken, updatedAt, sinceTimestamp
classId,
className: String(classes.find((item) => String(item?.id || "") === classId)?.name || "").trim(),
brand: brands.join(" / "),
stats,
channelStats,
stats: filterPublicTrainingStats(stats, visibility),
channelStats: visibility.performance ? channelStats : {},
nextMilestone: getPublicTrainingMilestone(stats.totalLaps),
recommendation: getPublicTrainingRecommendation(stats),
goals: classGoals,
goals: visibility.goals ? classGoals : [],
currentSession: sinceTimestamp
? buildPublicTrainingWindowStats(classLaps.filter((lap) => lap.timestamp >= sinceTimestamp && (!windowMs || lap.timestamp <= sinceTimestamp + windowMs)))
: null,
@@ -78,14 +112,15 @@ function buildPublicTrainingProfile(state, shareToken, updatedAt, sinceTimestamp
});
const activeClass = classProfiles[0] || {
classId: "", className: "", brand: "",
stats: buildPublicTrainingStats([]), nextMilestone: getPublicTrainingMilestone(0),
channelStats: buildPublicTimingChannelStats({}, new Set()),
stats: filterPublicTrainingStats(buildPublicTrainingStats([]), visibility), nextMilestone: getPublicTrainingMilestone(0),
channelStats: visibility.performance ? buildPublicTimingChannelStats({}, new Set()) : {},
recommendation: getPublicTrainingRecommendation({ totalLaps: 0 }), goals: [], currentSession: null,
};
const settings = state?.settings && typeof state.settings === "object" ? state.settings : {};
return {
updatedAt: String(updatedAt || ""),
visibility,
profile: {
name: String(driver.name || "Driver").trim(),
brand: activeClass.brand,
@@ -263,6 +298,17 @@ function buildPublicTrainingStats(laps) {
})));
const bestThreeConsecutive = getPublicBestConsecutiveLaps(stints, 3);
const bestFiveMinuteResult = getPublicBestDurationResult(stints, 5 * 60 * 1000);
const stintSummaries = stints.map((stint, index) => {
const paceLaps = stint.laps.filter((lap) => !lap.excludedFromPace);
const values = paceLaps.map((lap) => lap.lapMs);
const startedAt = Math.max(0, Number(stint.laps[0]?.timestamp || 0) - Number(stint.laps[0]?.lapMs || 0));
const endedAt = Number(stint.laps.at(-1)?.timestamp || startedAt);
return {
number: index + 1, startedAt, lapCount: stint.laps.length,
bestLapMs: values.length ? Math.min(...values) : 0, averageLapMs: average(values),
durationMs: Math.max(0, endedAt - startedAt),
};
}).sort((left, right) => right.startedAt - left.startedAt).slice(0, 8);
const stats = {
totalLaps: laps.length,
@@ -290,6 +336,7 @@ function buildPublicTrainingStats(laps) {
todayCompletedFiveMinuteStints: todayStintSummary.completedFiveMinuteStints,
dailySummaries,
stintCount: stints.length,
stintSummaries,
bestThreeConsecutive,
bestFiveMinuteResult,
};