add stat
This commit is contained in:
@@ -147,6 +147,9 @@ function buildPublicTrainingStats(laps) {
|
||||
if (!controlTargetMs || lapTimes[index] > controlTargetMs) break;
|
||||
controlledLapStreak += 1;
|
||||
}
|
||||
const stints = splitPublicTrainingStints(laps);
|
||||
const bestThreeConsecutive = getPublicBestConsecutiveLaps(stints, 3);
|
||||
const bestFiveMinuteResult = getPublicBestDurationResult(stints, 5 * 60 * 1000);
|
||||
|
||||
return {
|
||||
totalLaps: laps.length,
|
||||
@@ -159,9 +162,80 @@ function buildPublicTrainingStats(laps) {
|
||||
consistencySpreadPct: recentAverageMs ? ((percentile(0.9) - percentile(0.1)) / recentAverageMs) * 100 : 0,
|
||||
controlledLapStreak,
|
||||
dailySummaries,
|
||||
stintCount: stints.length,
|
||||
bestThreeConsecutive,
|
||||
bestFiveMinuteResult,
|
||||
};
|
||||
}
|
||||
|
||||
function splitPublicTrainingStints(laps, breakThresholdMs = 2 * 60 * 1000) {
|
||||
const stints = [];
|
||||
(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),
|
||||
};
|
||||
if (startsNewStint) {
|
||||
stints.push({ sessionId: String(lap.sessionId || ""), laps: [normalizedLap] });
|
||||
} else {
|
||||
previousStint.laps.push(normalizedLap);
|
||||
}
|
||||
});
|
||||
return stints;
|
||||
}
|
||||
|
||||
function getPublicBestConsecutiveLaps(stints, lapCount) {
|
||||
let best = null;
|
||||
(Array.isArray(stints) ? stints : []).forEach((stint) => {
|
||||
const paceLaps = stint.laps.filter((lap) => !lap.excludedFromPace);
|
||||
for (let index = 0; index <= paceLaps.length - lapCount; index += 1) {
|
||||
const laps = paceLaps.slice(index, index + lapCount);
|
||||
const totalMs = laps.reduce((sum, lap) => sum + Number(lap.lapMs || 0), 0);
|
||||
const candidate = { lapCount, totalMs, averageLapMs: Math.round(totalMs / lapCount) };
|
||||
if (!best || candidate.totalMs < best.totalMs) best = candidate;
|
||||
}
|
||||
});
|
||||
return best;
|
||||
}
|
||||
|
||||
function getPublicBestDurationResult(stints, targetMs) {
|
||||
let best = null;
|
||||
(Array.isArray(stints) ? stints : []).forEach((stint) => {
|
||||
const paceLaps = stint.laps.filter((lap) => !lap.excludedFromPace);
|
||||
for (let startIndex = 0; startIndex < paceLaps.length; startIndex += 1) {
|
||||
const firstLap = paceLaps[startIndex];
|
||||
const startedAt = Math.max(0, Number(firstLap.timestamp || 0) - Number(firstLap.lapMs || 0));
|
||||
const cutoffAt = startedAt + targetMs;
|
||||
const counted = [];
|
||||
for (let index = startIndex; index < paceLaps.length; index += 1) {
|
||||
const lap = paceLaps[index];
|
||||
const lapStartedAt = Math.max(0, Number(lap.timestamp || 0) - Number(lap.lapMs || 0));
|
||||
if (lapStartedAt >= cutoffAt) break;
|
||||
counted.push(lap);
|
||||
}
|
||||
const lastLap = counted.at(-1);
|
||||
if (!lastLap || Number(lastLap.timestamp || 0) < cutoffAt) continue;
|
||||
const totalMs = Number(lastLap.timestamp || 0) - startedAt;
|
||||
const lapCount = counted.length;
|
||||
const candidate = {
|
||||
targetMs,
|
||||
lapCount,
|
||||
totalMs,
|
||||
averageLapMs: Math.round(counted.reduce((sum, lap) => sum + Number(lap.lapMs || 0), 0) / lapCount),
|
||||
};
|
||||
if (!best || candidate.lapCount > best.lapCount || (candidate.lapCount === best.lapCount && candidate.totalMs < best.totalMs)) best = candidate;
|
||||
}
|
||||
});
|
||||
return best;
|
||||
}
|
||||
|
||||
function normalizePublicLogoDataUrl(value) {
|
||||
const logo = String(value || "").trim();
|
||||
return logo.length <= 2_000_000 && /^data:image\/(?:png|jpeg|webp);base64,/i.test(logo) ? logo : "";
|
||||
@@ -192,4 +266,7 @@ module.exports = {
|
||||
hasPublicTrainingShare,
|
||||
normalizePublicTrainingSince,
|
||||
normalizePublicTrainingWindow,
|
||||
splitPublicTrainingStints,
|
||||
getPublicBestConsecutiveLaps,
|
||||
getPublicBestDurationResult,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user