player win/loss streaks

This commit is contained in:
SowinskiBraeden committed 2026-03-21 00:34:32 -07:00
1 parent e727e0d781
commit 8452bdae73
3 files changed
+176 -1

No files matched your search

+4
View File
@@ -828,6 +828,10 @@ td a:hover {
background: rgba(255, 255, 255, 0.07);
}
.chart-frame-small {
min-height: 260px;
}
@media (max-width: 980px) {
.site-shell {
width: min(100% - 24px, 100%);
+79
View File
@@ -76,6 +76,14 @@ class PlayerStats:
total_cash_out_cents: int
total_net_cents: int
roi_pct: float
current_win_streak: int
current_loss_streak: int
longest_win_streak: int
longest_loss_streak: int
best_session_date: str | None
best_session_net_cents: int
worst_session_date: str | None
worst_session_net_cents: int
rank_change: int = 0
@@ -189,6 +197,68 @@ def build_session_summaries(events: list[EventRow]) -> list[SessionSummary]:
return sessions
def summarize_player_runs(entries: list[SessionEntry]) -> dict[str, int | str | None]:
ordered_entries = sorted(entries, key=lambda entry: entry.session_date)
longest_win_streak = 0
longest_loss_streak = 0
current_run_wins = 0
current_run_losses = 0
best_entry: SessionEntry | None = None
worst_entry: SessionEntry | None = None
for entry in ordered_entries:
net = entry.net_cents
if best_entry is None or net > best_entry.net_cents:
best_entry = entry
if worst_entry is None or net < worst_entry.net_cents:
worst_entry = entry
if net > 0:
current_run_wins += 1
current_run_losses = 0
elif net < 0:
current_run_losses += 1
current_run_wins = 0
else:
current_run_wins = 0
current_run_losses = 0
longest_win_streak = max(longest_win_streak, current_run_wins)
longest_loss_streak = max(longest_loss_streak, current_run_losses)
current_win_streak = 0
current_loss_streak = 0
for entry in reversed(ordered_entries):
net = entry.net_cents
if net > 0:
if current_loss_streak > 0:
break
current_win_streak += 1
elif net < 0:
if current_win_streak > 0:
break
current_loss_streak += 1
else:
break
return {
"current_win_streak": current_win_streak,
"current_loss_streak": current_loss_streak,
"longest_win_streak": longest_win_streak,
"longest_loss_streak": longest_loss_streak,
"best_session_date": best_entry.session_date if best_entry else None,
"best_session_net_cents": best_entry.net_cents if best_entry else 0,
"worst_session_date": worst_entry.session_date if worst_entry else None,
"worst_session_net_cents": worst_entry.net_cents if worst_entry else 0,
}
def build_leaderboard(sessions: list[SessionSummary]) -> list[PlayerStats]:
player_entries: dict[str, list[SessionEntry]] = defaultdict(list)
for session in sessions:
@@ -198,6 +268,7 @@ def build_leaderboard(sessions: list[SessionSummary]) -> list[PlayerStats]:
leaderboard: list[PlayerStats] = []
for player_name, entries in player_entries.items():
nets = [entry.net_cents for entry in entries]
run_summary = summarize_player_runs(entries)
wins = [value for value in nets if value > 0]
losses = [value for value in nets if value < 0]
total_buy_in = sum(entry.buy_in_cents for entry in entries)
@@ -232,6 +303,14 @@ def build_leaderboard(sessions: list[SessionSummary]) -> list[PlayerStats]:
total_cash_out_cents=total_cash_out,
total_net_cents=total_net,
roi_pct=roi_pct,
current_win_streak=run_summary["current_win_streak"],
current_loss_streak=run_summary["current_loss_streak"],
longest_win_streak=run_summary["longest_win_streak"],
longest_loss_streak=run_summary["longest_loss_streak"],
best_session_date=run_summary["best_session_date"],
best_session_net_cents=run_summary["best_session_net_cents"],
worst_session_date=run_summary["worst_session_date"],
worst_session_net_cents=run_summary["worst_session_net_cents"],
)
)
+93 -1
View File
@@ -16,9 +16,24 @@
<article class="panel stat-card"><span>Buy-ins</span><strong>{{ player.total_buy_in_cents | money }}</strong></article>
<article class="panel stat-card"><span>Cash-outs</span><strong>{{ player.total_cash_out_cents | money }}</strong></article>
<article class="panel stat-card"><span>Sessions</span><strong>{{ player.sessions_played }}</strong></article>
<article class="panel stat-card">
<span>Current Streak</span>
<strong>
{% if player.current_win_streak > 0 %}
W{{ player.current_win_streak }}
{% elif player.current_loss_streak > 0 %}
L{{ player.current_loss_streak }}
{% else %}
-
{% endif %}
</strong>
</article>
<article class="panel stat-card"><span>Longest win streak</span><strong>{{ player.longest_win_streak }}</strong></article>
<article class="panel stat-card"><span>Longest loss streak</span><strong>{{ player.longest_loss_streak }}</strong></article>
</section>
<section class="grid two-col">
<section class="grid one-col">
<div class="panel chart-panel">
<div class="panel-header">
<div>
@@ -30,6 +45,9 @@
<canvas id="playerCumulativeChart"></canvas>
</div>
</div>
</section>
<section class="grid two-col">
<div class="panel chart-panel">
<div class="panel-header">
<div>
@@ -41,6 +59,17 @@
<canvas id="playerSessionChart"></canvas>
</div>
</div>
<div class="panel chart-panel">
<div class="panel-header">
<div>
<p class="eyebrow">Breakdown</p>
<h2>Result mix</h2>
</div>
</div>
<div class="chart-frame chart-frame-small">
<canvas id="playerBreakdownChart"></canvas>
</div>
</div>
</section>
<section class="panel">
@@ -60,6 +89,28 @@
<tr><th>Average Loss</th><td>{{ player.avg_loss_cents | money }}</td></tr>
<tr><th>Biggest Win</th><td>{{ player.biggest_win_cents | money }}</td></tr>
<tr><th>Biggest Loss</th><td>{{ player.biggest_loss_cents | money }}</td></tr>
<tr><th>Current Win Streak</th><td>{{ player.current_win_streak }}</td></tr>
<tr><th>Current Loss Streak</th><td>{{ player.current_loss_streak }}</td></tr>
<tr><th>Longest Win Streak</th><td>{{ player.longest_win_streak }}</td></tr>
<tr><th>Longest Loss Streak</th><td>{{ player.longest_loss_streak }}</td></tr>
<tr>
<th>Best Session</th>
<td>
{{ player.best_session_net_cents | money }}
{% if player.best_session_date %}
<span class="muted-text small">on {{ player.best_session_date | pretty_date }}</span>
{% endif %}
</td>
</tr>
<tr>
<th>Worst Session</th>
<td>
{{ player.worst_session_net_cents | money }}
{% if player.worst_session_date %}
<span class="muted-text small">on {{ player.worst_session_date | pretty_date }}</span>
{% endif %}
</td>
</tr>
</tbody>
</table>
</div>
@@ -138,4 +189,45 @@
});
}
</script>
<script>
const breakdownCtx = document.getElementById('playerBreakdownChart');
if (breakdownCtx) {
new Chart(breakdownCtx, {
type: 'doughnut',
data: {
labels: ['Wins', 'Losses', 'Break-even'],
datasets: [{
data: [
{{ player.winning_sessions }},
{{ player.losing_sessions }},
{{ player.break_even_sessions }}
],
backgroundColor: [
'#59d98e',
'#f06c72',
'#e4bf65'
],
borderWidth: 0,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
color: textMuted,
usePointStyle: true,
boxWidth: 10,
boxHeight: 10,
padding: 18,
},
},
},
},
});
}
</script>
{% endblock %}