Fix remaining dual-rating issues in email templates

- Email chart: single unified ELO chart instead of separate singles/doubles
- Email leaderboard: single unified leaderboard
- Removed duplicate top_doubles query (was identical to top_singles anyway)
This commit is contained in:
Split 2026-02-26 12:51:35 -05:00
parent 4fbb803a66
commit 534d293be4
4 changed files with 29 additions and 57 deletions

View File

@ -719,3 +719,4 @@ thread 'main' (234988) panicked at src/main.rs:52:10:
called `Result::unwrap()` on an `Err` value: Os { code: 48, kind: AddrInUse, message: "Address already in use" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Migration error: error returned from database: (code: 1) no such column: rating
Migration error: error returned from database: (code: 1) no such column: rating

View File

@ -1068,3 +1068,16 @@ Starting Pickleball ELO Tracker Server on port 3000...
Add Player: http://localhost:3000/players/new
🎾 Record Match: http://localhost:3000/matches/new
🏓 Pickleball ELO Tracker v3.0
==============================
Starting Pickleball ELO Tracker Server on port 3000...
✅ Server running at http://localhost:3000
📊 Leaderboard: http://localhost:3000/leaderboard
📜 Match History: http://localhost:3000/matches
👥 Players: http://localhost:3000/players
⚖️ Team Balancer: http://localhost:3000/balance
Add Player: http://localhost:3000/players/new
🎾 Record Match: http://localhost:3000/matches/new

Binary file not shown.

View File

@ -2766,14 +2766,14 @@ async fn send_daily_summary(
format!("https://quickchart.io/chart?c={}&w=500&h=250&bkg=white", encoded)
}
// Get singles/doubles history for email charts
let singles_email_history: Vec<(i64, String, String, f64)> = sqlx::query_as(
// Get unified ELO history for email chart (all match types)
let elo_email_history: Vec<(i64, String, String, f64)> = sqlx::query_as(
r#"SELECT m.id, strftime('%H:%M', datetime(m.timestamp, '-5 hours')) as time_str,
p.name, mp.rating_after
FROM matches m
JOIN match_participants mp ON m.id = mp.match_id
JOIN players p ON mp.player_id = p.id
WHERE date(m.timestamp) = ? AND m.match_type = 'singles'
WHERE date(m.timestamp) = ?
ORDER BY m.timestamp, p.name"#
)
.bind(&target_date)
@ -2781,32 +2781,16 @@ async fn send_daily_summary(
.await
.unwrap_or_default();
let doubles_email_history: Vec<(i64, String, String, f64)> = sqlx::query_as(
r#"SELECT m.id, strftime('%H:%M', datetime(m.timestamp, '-5 hours')) as time_str,
p.name, mp.rating_after
FROM matches m
JOIN match_participants mp ON m.id = mp.match_id
JOIN players p ON mp.player_id = p.id
WHERE date(m.timestamp) = ? AND m.match_type = 'doubles'
ORDER BY m.timestamp, p.name"#
)
.bind(&target_date)
.fetch_all(&state.pool)
.await
.unwrap_or_default();
let elo_chart_url = build_email_chart(&elo_email_history, &colors_email, "Unified ELO Rating");
let singles_chart_url = build_email_chart(&singles_email_history, &colors_email, "Singles Rating");
let doubles_chart_url = build_email_chart(&doubles_email_history, &colors_email, "Doubles Rating");
let charts_email_html = if !elo_chart_url.is_empty() {
format!(r#"<img src="{}" alt="ELO Rating Chart" style="width:100%;max-width:500px;margin-bottom:15px;">"#, elo_chart_url)
} else {
String::new()
};
let charts_email_html = format!(
r#"{}{}
"#,
if !singles_chart_url.is_empty() { format!(r#"<img src="{}" alt="Singles Rating Chart" style="width:100%;max-width:500px;margin-bottom:15px;">"#, singles_chart_url) } else { String::new() },
if !doubles_chart_url.is_empty() { format!(r#"<img src="{}" alt="Doubles Rating Chart" style="width:100%;max-width:500px;">"#, doubles_chart_url) } else { String::new() }
);
// Get leaderboard data
let top_singles: Vec<(String, f64)> = sqlx::query_as(
// Get unified leaderboard data
let top_players: Vec<(String, f64)> = sqlx::query_as(
r#"SELECT DISTINCT p.name, p.singles_rating
FROM players p
JOIN match_participants mp ON p.id = mp.player_id
@ -2816,24 +2800,7 @@ async fn send_daily_summary(
.await
.unwrap_or_default();
let top_doubles: Vec<(String, f64)> = sqlx::query_as(
r#"SELECT DISTINCT p.name, p.singles_rating
FROM players p
JOIN match_participants mp ON p.id = mp.player_id
ORDER BY p.singles_rating DESC LIMIT 5"#
)
.fetch_all(&state.pool)
.await
.unwrap_or_default();
let singles_html: String = top_singles.iter().enumerate()
.map(|(i, (name, rating))| {
let medal = match i { 0 => "🥇", 1 => "🥈", 2 => "🥉", _ => "" };
format!("<tr><td>{} {}. {}</td><td style='text-align:right;'>{:.0}</td></tr>", medal, i+1, name, rating)
})
.collect();
let doubles_html: String = top_doubles.iter().enumerate()
let leaderboard_html: String = top_players.iter().enumerate()
.map(|(i, (name, rating))| {
let medal = match i { 0 => "🥇", 1 => "🥈", 2 => "🥉", _ => "" };
format!("<tr><td>{} {}. {}</td><td style='text-align:right;'>{:.0}</td></tr>", medal, i+1, name, rating)
@ -2942,17 +2909,8 @@ async fn send_daily_summary(
{}
</table>
<h2 style="color: #003594; border-bottom: 2px solid #FFB81C; padding-bottom: 10px; margin-top: 30px;">📊 Leaderboard</h2>
<div style="display: flex; gap: 20px;">
<div style="flex: 1;">
<h3 style="color: #003594; font-size: 14px;">Singles</h3>
<table style="width: 100%;">{}</table>
</div>
<div style="flex: 1;">
<h3 style="color: #003594; font-size: 14px;">Doubles</h3>
<table style="width: 100%;">{}</table>
</div>
</div>
<h2 style="color: #003594; border-bottom: 2px solid #FFB81C; padding-bottom: 10px; margin-top: 30px;">📊 Leaderboard (Unified ELO)</h2>
<table style="width: 100%; max-width: 300px;">{}</table>
<h2 style="color: #003594; border-bottom: 2px solid #FFB81C; padding-bottom: 10px; margin-top: 30px;">🤝 Partner Synergy</h2>
<p style="color: #666; font-size: 12px; margin-bottom: 10px;">Win rate when partnered together (all-time doubles)</p>
@ -2964,7 +2922,7 @@ async fn send_daily_summary(
</div>
</body>
</html>
"#, target_date, matches_email_html, charts_email_html, players_email_html, singles_html, doubles_html, heatmap_email);
"#, target_date, matches_email_html, charts_email_html, players_email_html, leaderboard_html, heatmap_email);
// Send emails
use lettre::{Message, SmtpTransport, Transport};