feat: per-league eligible sessions and break-even threshold settings (2.5.0)

Add two configurable leaderboard settings per league, replacing the global
constants with per-row DB columns:

- eligible_min_sessions (default 3): minimum sessions before a player ranks
  on the main leaderboard rather than showing as provisional
- break_even_cents (default 100 = $1.00): sessions within ±this of $0 net
  are classified as break-even rather than a win or loss

Changes:
- Migration 0003 adds both columns with server defaults for existing leagues
- League DB model gains eligible_min_sessions and break_even_cents columns
- net_result_bucket() and build_leaderboard() accept optional tolerance param
- summarize_player_runs() passes tolerance to net_result_bucket for streaks
- League leaderboard route reads values from the league row instead of config
- League settings form exposes both fields with validation (1-100 sessions,
  $0.00-$100.00 threshold)
This commit is contained in:
SowinskiBraeden committed 2026-06-26 21:37:47 -07:00
1 parent 0e34600714
commit 2fc7bab183
7 files changed
+98 -31

No files matched your search

+14
View File
@@ -28,6 +28,20 @@
<option value="public" {{ 'selected' if form.visibility == 'public' else '' }}>Public — leaderboard is visible to anyone</option>
</select>
</label>
<div style="margin-top:8px;padding-top:20px;border-top:1px solid var(--border);">
<p class="eyebrow">Leaderboard</p>
<p class="muted-text" style="margin-top:4px;margin-bottom:0;font-size:.82rem;">Controls how players qualify and how sessions are classified.</p>
</div>
<div class="session-create-row" style="grid-template-columns:1fr 1fr;">
<label>
<span>Sessions to rank <span class="info-tip" tabindex="0"><svg width="13" height="13" viewBox="0 0 13 13" fill="none" aria-hidden="true"><circle cx="6.5" cy="6.5" r="5.5" stroke="currentColor" stroke-width="1.2"/><path d="M6.5 6v3.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/><circle cx="6.5" cy="4" r=".7" fill="currentColor"/></svg><span class="info-tip__bubble">Minimum number of sessions a player must have played to appear on the ranked leaderboard. Players below this threshold are shown as provisional.</span></span></span>
<input type="number" name="eligible_min_sessions" value="{{ form.eligible_min_sessions }}" min="1" max="100" step="1" required>
</label>
<label>
<span>Break-even threshold ($) <span class="info-tip" tabindex="0"><svg width="13" height="13" viewBox="0 0 13 13" fill="none" aria-hidden="true"><circle cx="6.5" cy="6.5" r="5.5" stroke="currentColor" stroke-width="1.2"/><path d="M6.5 6v3.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/><circle cx="6.5" cy="4" r=".7" fill="currentColor"/></svg><span class="info-tip__bubble">A session within ±this amount of $0 is counted as a break-even rather than a win or loss. Default is $1.00.</span></span></span>
<input type="number" name="break_even_dollars" value="{{ form.break_even_dollars }}" min="0" max="100" step="0.50" required>
</label>
</div>
<div>
<button class="btn btn--primary" type="submit">Save changes</button>
</div>