updating the playtime frontend with claude only code to replace the one graph with a dumb-bell graph, having each individual map show its players at beginning versus end and also the overall impact of maps + their popularity

This commit is contained in:
jenz
2026-10-02 14:43:45 +02:00
parent 6019e6854d
commit 8a76454784
14 changed files with 3300 additions and 159 deletions
@@ -101,13 +101,19 @@ export interface MapPopulationPoint {
map_start_dt: string;
map_end_dt: string | null;
total_players: number;
// Population at the instant the map started, and at the instant it ended
// (or "now" for the still-running map) — the two endpoints of the
// dumbbell. Kept separate from total_players (which is reach — everyone
// who was ever connected at some point during the map, regardless of
// when) so the chart can show *when* a decline actually happened instead
// of implying it was caused by whichever map came right after.
start_players: number;
end_players: number;
}
export async function getMapPopulationSeries(limit = 40, before?: string): Promise<MapPopulationPoint[]> {
// Total DISTINCT players who were connected at any point during each map's
// full session (not just at its start) — one data point per map. Fetched
// most-recent-first (for the LIMIT), then the caller should reverse it
// back to chronological order for display.
// One data point per map, fetched most-recent-first (for the LIMIT), then
// the caller should reverse it back to chronological order for display.
// `before` (a DB-formatted datetime string) lets the caller page further
// back into history than the initial batch, for pan-to-load-more charts.
const rows = await query<MapPopulationPoint>(
@@ -115,7 +121,15 @@ export async function getMapPopulationSeries(limit = 40, before?: string): Promi
(SELECT COUNT(DISTINCT s.steamid) FROM playtime_display_sessions s
WHERE s.session_start_dt < COALESCE(${NEXT_MAP_START('m')}, NOW())
AND (s.session_end_dt IS NULL OR s.session_end_dt > m.map_start_dt)
) AS total_players
) AS total_players,
(SELECT COUNT(DISTINCT s.steamid) FROM playtime_display_sessions s
WHERE s.session_start_dt <= m.map_start_dt
AND (s.session_end_dt IS NULL OR s.session_end_dt > m.map_start_dt)
) AS start_players,
(SELECT COUNT(DISTINCT s.steamid) FROM playtime_display_sessions s
WHERE s.session_start_dt <= COALESCE(${NEXT_MAP_START('m')}, NOW())
AND (s.session_end_dt IS NULL OR s.session_end_dt > COALESCE(${NEXT_MAP_START('m')}, NOW()))
) AS end_players
FROM playtime_display_map_history m
WHERE ${REAL_MAP_PERIOD}
${before ? 'AND m.map_start_dt < ?' : ''}
@@ -126,6 +140,14 @@ export async function getMapPopulationSeries(limit = 40, before?: string): Promi
return rows.reverse();
}
// getMapImpactLeaderboard moved to lib/mapImpact.ts — the SQL version here
// used per-row correlated subqueries (effectively O(map_rows * sessions))
// which was fine on a small dataset but became extremely slow at real
// production size. Re-exported from here so existing imports don't need to
// change. See lib/mapImpact.ts for the full explanation.
export { getMapImpactLeaderboard } from '@/lib/mapImpact';
export type { MapImpactSummary } from '@/lib/mapImpact';
export interface CountrySummary {
current_country: string | null; // null represents the "Unknown" bucket
player_count: number;