diff --git a/app/actions/players/profile.rb b/app/actions/players/profile.rb new file mode 100644 index 0000000..302fc6f --- /dev/null +++ b/app/actions/players/profile.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module EuchreCamp + module Actions + module Players + class Profile < EuchreCamp::Action + include Deps[players_repo: 'repositories.players'] + + def handle(request, response) + player_id = request.params[:id] + player = players_repo.by_id(player_id) + + # Get partnership statistics + partnerships = EuchreCamp::Services::PartnershipTracker.get_player_partnerships(player_id) + + # Get recent games (last 10) + recent_games = get_recent_games(player_id) + + response.render(view, + player: player, + partnerships: partnerships, + recent_games: recent_games + ) + end + + private + + def get_recent_games(player_id) + rom = EuchreCamp::App["persistence.rom"] + + # Get matches where this player participated + matches = rom.relations[:matches] + .where( + Sequel.|( + { team_1_p1_id: player_id }, + { team_1_p2_id: player_id }, + { team_2_p1_id: player_id }, + { team_2_p2_id: player_id } + ) + ) + .order(Sequel.desc(:played_at)) + .limit(10) + .to_a + + # Get player names for display + all_players = rom.relations[:players].to_a.to_h { |p| [p[:id], p] } + + matches.map do |match| + team_1_players = [ + all_players[match[:team_1_p1_id]], + all_players[match[:team_1_p2_id]] + ] + team_2_players = [ + all_players[match[:team_2_p1_id]], + all_players[match[:team_2_p2_id]] + ] + + # Determine if this player won + player_on_team_1 = match[:team_1_p1_id] == player_id || match[:team_1_p2_id] == player_id + won = player_on_team_1 ? (match[:team_1_score] > match[:team_2_score]) : (match[:team_2_score] > match[:team_1_score]) + + { + date: match[:played_at] || match[:created_at], + team_1: team_1_players, + team_2: team_2_players, + score: "#{match[:team_1_score]}-#{match[:team_2_score]}", + won: won, + player_partner: player_on_team_1 ? team_1_players.find { |p| p[:id] != player_id } : team_2_players.find { |p| p[:id] != player_id } + } + end + end + end + end + end +end diff --git a/app/actions/players/schedule.rb b/app/actions/players/schedule.rb new file mode 100644 index 0000000..0cc48fd --- /dev/null +++ b/app/actions/players/schedule.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +module EuchreCamp + module Actions + module Players + class Schedule < EuchreCamp::Action + include Deps[players_repo: 'repositories.players'] + + def handle(request, response) + player_id = request.params[:id] + player = players_repo.by_id(player_id) + + # Get upcoming matches for this player + upcoming_matches = get_upcoming_matches(player_id) + + # Get active tournaments for this player + active_tournaments = get_active_tournaments(player_id) + + # Get tournament schedule + tournament_schedule = get_tournament_schedule(player_id) + + response.render(view, + player: player, + upcoming_matches: upcoming_matches, + active_tournaments: active_tournaments, + tournament_schedule: tournament_schedule + ) + end + + private + + def get_upcoming_matches(player_id) + rom = EuchreCamp::App["persistence.rom"] + + # Get upcoming matches where this player participates + matches = rom.relations[:matches] + .where( + Sequel.|( + { team_1_p1_id: player_id }, + { team_1_p2_id: player_id }, + { team_2_p1_id: player_id }, + { team_2_p2_id: player_id } + ) + ) + .where(status: 'scheduled') + .order(:played_at) + .limit(10) + .to_a + + # Get player names for display + all_players = rom.relations[:players].to_a.to_h { |p| [p[:id], p] } + + matches.map do |match| + team_1_players = [ + all_players[match[:team_1_p1_id]], + all_players[match[:team_1_p2_id]] + ] + team_2_players = [ + all_players[match[:team_2_p1_id]], + all_players[match[:team_2_p2_id]] + ] + + player_on_team_1 = match[:team_1_p1_id] == player_id || match[:team_1_p2_id] == player_id + + { + id: match[:id], + date: match[:played_at], + team_1: team_1_players, + team_2: team_2_players, + opponent_team: player_on_team_1 ? team_2_players : team_1_players, + player_team: player_on_team_1 ? team_1_players : team_2_players, + event_id: match[:event_id] + } + end + end + + def get_active_tournaments(player_id) + rom = EuchreCamp::App["persistence.rom"] + + # Get tournaments where player is participating + event_ids = rom.relations[:event_participants] + .where(player_id: player_id) + .select(:event_id) + .to_a + .map { |e| e[:event_id] } + + return [] if event_ids.empty? + + # Get active tournaments + rom.relations[:events] + .where(id: event_ids, status: 'active') + .to_a + end + + def get_tournament_schedule(player_id) + rom = EuchreCamp::App["persistence.rom"] + + # Get all tournaments where player participates + event_ids = rom.relations[:event_participants] + .where(player_id: player_id) + .select(:event_id) + .to_a + .map { |e| e[:event_id] } + + return [] if event_ids.empty? + + # Get tournaments and their schedules + tournaments = rom.relations[:events] + .where(id: event_ids) + .to_a + + tournaments.map do |tournament| + # Get tournament rounds + rounds = rom.relations[:tournament_rounds] + .where(event_id: tournament[:id]) + .order(:round_number) + .to_a + + { + tournament: tournament, + rounds: rounds + } + end + end + end + end + end +end diff --git a/app/templates/players/profile.html.erb b/app/templates/players/profile.html.erb new file mode 100644 index 0000000..ac49488 --- /dev/null +++ b/app/templates/players/profile.html.erb @@ -0,0 +1,94 @@ +

Player Profile: <%= player.name %>

+ +
+
+
+ ELO Rating + <%= player.current_elo %> +
+
+ Total Games + <%= recent_games.count %> +
+
+ Win Rate + <%= (recent_games.count > 0 ? (recent_games.count { |g| g[:won] }.to_f / recent_games.count * 100).round(1) : 0) %>% +
+
+
+ +
+ +

Partnership Performance

+ +<% if partnerships.any? %> +
+ + + + + + + + + + + + + <% partnerships.each do |p| %> + + + + + + + + + <% end %> + +
PartnerGamesWin RateELO ChangeAvg/MatchLast Played
<%= p[:partner][:name] %><%= p[:games_played] %> + <%= (p[:win_rate] * 100).round(1) %>% +
+ <% if p[:games_played] < 15 %> + (low confidence) + <% end %> +
+
<%= p[:total_elo_change] > 0 ? "+" : "" %><%= p[:total_elo_change] %><%= p[:avg_elo_change] > 0 ? "+" : "" %><%= p[:avg_elo_change] %><%= p[:last_played]&.strftime('%Y-%m-%d') || '-' %>
+
+<% else %> +

No partnership data available yet.

+<% end %> + +
+ +

Recent Games

+ +<% if recent_games.any? %> +
+ <% recent_games.each do |game| %> +
+
+ <%= game[:date]&.strftime('%Y-%m-%d %H:%M') || 'N/A' %> +
+
+
+ <%= game[:team_1].map { |p| p[:name] }.join(' + ') %> +
+
+ <%= game[:score] %> +
+
+ <%= game[:team_2].map { |p| p[:name] }.join(' + ') %> +
+
+
+ Partner: <%= game[:player_partner][:name] %> +
+
+ <% end %> +
+<% else %> +

No games played yet.

+<% end %> + +

Back to Rankings

diff --git a/app/templates/players/schedule.html.erb b/app/templates/players/schedule.html.erb new file mode 100644 index 0000000..f34360a --- /dev/null +++ b/app/templates/players/schedule.html.erb @@ -0,0 +1,90 @@ +

<%= player.name %>'s Schedule

+ +
+ +
+

Upcoming Matches

+ + <% if upcoming_matches.any? %> +
+ <% upcoming_matches.each do |match| %> +
+
+ <%= match[:date]&.strftime('%a, %b %d') || 'TBD' %> + <%= match[:date]&.strftime('%I:%M %p') %> +
+
+
+
+ <%= match[:player_team].map { |p| p[:name] }.join(' + ') %> +
+
vs
+
+ <%= match[:opponent_team].map { |p| p[:name] }.join(' + ') %> +
+
+
+ +
+ <% end %> +
+ <% else %> +

No upcoming matches scheduled.

+ <% end %> +
+ + +
+

Active Tournaments

+ + <% if active_tournaments.any? %> +
+ <% active_tournaments.each do |tournament| %> +
+
+

<%= tournament[:name] %>

+

<%= tournament[:format].capitalize %> Tournament

+
+
+ <%= tournament[:status].capitalize %> +
+ +
+ <% end %> +
+ <% else %> +

No active tournaments.

+ <% end %> +
+ + +
+

Tournament Schedule

+ + <% if tournament_schedule.any? %> +
+ <% tournament_schedule.each do |schedule| %> +
+

<%= schedule[:tournament][:name] %>

+
+ <% schedule[:rounds].each do |round| %> +
+ Round <%= round[:round_number] %> + <%= round[:status] %> +
+ <% end %> +
+
+ <% end %> +
+ <% else %> +

No tournament schedule available.

+ <% end %> +
+
+ +

Back to Profile