Updated streak-percentile to count all players from the last 30 days (or

all active streaks if players have a greater than 30 day streak)
This commit is contained in:
George Powell
2026-02-22 00:25:08 -05:00
parent 3036264d44
commit bd36f29419
2 changed files with 42 additions and 12 deletions

View File

@@ -119,8 +119,7 @@
{/if} {/if}
{#if streakPercentile !== null} {#if streakPercentile !== null}
<p class="text-sm mt-4 text-gray-700 font-triodion"> <p class="text-sm mt-4 text-gray-700 font-triodion">
Only {streakPercentile}% of players have a streak of {streak} {streakPercentile <= 50 ? "Only " : ""}{streakPercentile}% of players have a streak of {streak} or greater.
or greater.
</p> </p>
{/if} {/if}
{/if} {/if}

View File

@@ -38,13 +38,30 @@ export const GET: RequestHandler = async ({ url }) => {
} }
} }
// Calculate the current streak for each user // Calculate the current streak for each user.
const streaks: number[] = []; // Start from today; if the user hasn't played today yet, try yesterday so
// that streaks aren't zeroed out mid-day before the player has had a chance
// to complete today's puzzle.
const yesterday = new Date(`${localDate}T00:00:00`);
yesterday.setDate(yesterday.getDate() - 1);
const yesterdayStr = yesterday.toLocaleDateString('en-CA');
const thirtyDaysAgo = new Date(`${localDate}T00:00:00`);
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const thirtyDaysAgoStr = thirtyDaysAgo.toLocaleDateString('en-CA');
// For each user, compute their current streak and whether they've played
// within the last 30 days. "Eligible players" = active streak OR recent play.
const userStats: { streak: number; isEligible: boolean }[] = [];
for (const [, dates] of byUser) { for (const [, dates] of byUser) {
// dates are already desc-sorted // dates are already desc-sorted
const dateSet = new Set(dates); const dateSet = new Set(dates);
// Pick the most recent anchor: today if played, otherwise yesterday
const anchor = dateSet.has(localDate) ? localDate : yesterdayStr;
let streak = 0; let streak = 0;
let cursor = new Date(`${localDate}T00:00:00`); let cursor = new Date(`${anchor}T00:00:00`);
while (true) { while (true) {
const dateStr = cursor.toLocaleDateString('en-CA'); const dateStr = cursor.toLocaleDateString('en-CA');
@@ -53,20 +70,34 @@ export const GET: RequestHandler = async ({ url }) => {
cursor.setDate(cursor.getDate() - 1); cursor.setDate(cursor.getDate() - 1);
} }
streaks.push(streak); const hasRecentPlay = dates.some((d) => d >= thirtyDaysAgoStr);
userStats.push({ streak, isEligible: streak >= 1 || hasRecentPlay });
} }
// Only count users who have an active streak (streak >= 1) const eligiblePlayers = userStats.filter((u) => u.isEligible);
const activeStreaks = streaks.filter((s) => s >= 1);
if (activeStreaks.length === 0) { if (eligiblePlayers.length === 0) {
console.log('[streak-percentile] No eligible players found, returning 100th percentile');
return json({ percentile: 100 }); return json({ percentile: 100 });
} }
// Percentage of active-streak users who have a streak >= targetStreak // Percentage of eligible players who have a streak >= targetStreak
const atOrAbove = activeStreaks.filter((s) => s >= targetStreak).length; const atOrAbove = eligiblePlayers.filter((u) => u.streak >= targetStreak).length;
const raw = (atOrAbove / activeStreaks.length) * 100; const raw = (atOrAbove / eligiblePlayers.length) * 100;
const percentile = raw < 1 ? Math.round(raw * 100) / 100 : Math.round(raw); const percentile = raw < 1 ? Math.round(raw * 100) / 100 : Math.round(raw);
console.log('[streak-percentile]', {
localDate,
targetStreak,
totalUsers: byUser.size,
totalRows: rows.length,
eligiblePlayers: eligiblePlayers.length,
activeStreaks: userStats.filter((u) => u.streak >= 1).length,
recentPlayers: userStats.filter((u) => u.isEligible).length,
atOrAbove,
raw,
percentile,
});
return json({ percentile }); return json({ percentile });
}; };