Skip to content

Commit 1d76209

Browse files
committed
chore: split details in controllers
1 parent 1f17571 commit 1d76209

7 files changed

Lines changed: 176 additions & 173 deletions

File tree

app/controllers/api/v1/analytics/champions_controller.rb

Lines changed: 102 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -35,42 +35,59 @@ def details
3535
status: :bad_request)
3636
end
3737

38-
# Find matches for this champion (case-insensitive search)
39-
matches = PlayerMatchStat.where(player: player)
40-
.where('LOWER(champion) = ?', champion.downcase)
41-
.joins(:match)
42-
.includes(:match)
43-
.order('matches.game_start DESC')
44-
.limit(params[:limit] || 20)
38+
matches = fetch_champion_matches(player, champion)
4539

4640
if matches.empty?
4741
return render_error(message: "No matches found for champion #{champion}", code: 'NO_MATCHES',
4842
status: :not_found)
4943
end
5044

51-
# Calculate aggregate stats for this champion
45+
riot_service = RiotCdnService.new
5246
matches_array = matches.to_a
47+
48+
render_success({
49+
player: PlayerSerializer.render_as_hash(player),
50+
champion: champion,
51+
icon_url: riot_service.champion_icon_url(champion),
52+
aggregate_stats: build_aggregate_stats(matches, matches_array),
53+
matches: serialize_champion_matches(matches_array, riot_service)
54+
})
55+
rescue ActiveRecord::RecordNotFound
56+
render_error(message: 'Player not found', code: 'PLAYER_NOT_FOUND', status: :not_found)
57+
rescue StandardError => e
58+
Rails.logger.error("Error in champions#details: #{e.message}")
59+
Rails.logger.error(e.backtrace.join("\n"))
60+
render_error(message: "Failed to load champion details: #{e.message}", code: 'INTERNAL_ERROR',
61+
status: :internal_server_error)
62+
end
63+
64+
private
65+
66+
def fetch_champion_matches(player, champion)
67+
PlayerMatchStat.where(player: player)
68+
.where('LOWER(champion) = ?', champion.downcase)
69+
.joins(:match)
70+
.includes(:match)
71+
.order('matches.game_start DESC')
72+
.limit(params[:limit] || 20)
73+
end
74+
75+
def build_aggregate_stats(matches, matches_array)
5376
total_kills = matches_array.sum(&:kills)
5477
total_deaths = matches_array.sum(&:deaths)
5578
total_assists = matches_array.sum(&:assists)
5679
avg_kda = if matches_array.any?
57-
((total_kills + total_assists).to_f / [total_deaths,
58-
1].max).round(2)
80+
((total_kills + total_assists).to_f / [total_deaths, 1].max).round(2)
5981
else
6082
0
6183
end
84+
wins = matches_array.count { |m| m.match&.victory? }
6285

63-
aggregate_stats = {
86+
{
6487
total_games: matches_array.count,
65-
wins: matches_array.count { |m| m.match&.victory? },
66-
losses: matches_array.count { |m| !m.match&.victory? },
67-
win_rate: if matches_array.any?
68-
(matches_array.count do |m|
69-
m.match&.victory?
70-
end.to_f / matches_array.count)
71-
else
72-
0
73-
end,
88+
wins: wins,
89+
losses: matches_array.count - wins,
90+
win_rate: matches_array.any? ? (wins.to_f / matches_array.count) : 0,
7491
avg_kda: avg_kda,
7592
avg_kills: matches_array.sum(&:kills).to_f / [matches_array.count, 1].max,
7693
avg_deaths: matches_array.sum(&:deaths).to_f / [matches_array.count, 1].max,
@@ -81,75 +98,64 @@ def details
8198
avg_gold_per_min: matches.average(:gold_per_min)&.round(0) || 0,
8299
avg_vision_score: matches.average(:vision_score)&.round(1) || 0
83100
}
101+
end
84102

85-
riot_service = RiotCdnService.new
103+
def serialize_champion_matches(matches_array, riot_service)
104+
matches_array.filter_map do |stat|
105+
next nil unless stat.match
86106

87-
matches_data = matches.map do |stat|
88-
next nil unless stat.match # Skip if match is nil
89-
90-
{
91-
match_id: stat.match.id,
92-
game_id: stat.match.riot_match_id,
93-
date: stat.match.game_start&.strftime('%Y-%m-%d %H:%M'),
94-
victory: stat.match.victory?,
95-
game_duration: stat.match.game_duration || 0,
96-
kda: stat.kda_display,
97-
kda_ratio: (stat.kda_ratio || 0).round(2),
98-
kills: stat.kills || 0,
99-
deaths: stat.deaths || 0,
100-
assists: stat.assists || 0,
101-
cs: stat.cs || 0,
102-
cs_per_min: (stat.cs_per_min || 0).round(1),
103-
damage_dealt: stat.damage_dealt_total || 0,
104-
damage_taken: stat.damage_taken || 0,
105-
gold_earned: stat.gold_earned || 0,
106-
gold_per_min: (stat.gold_per_min || 0).round(0),
107-
vision_score: stat.vision_score || 0,
108-
performance_score: stat.performance_score || 0,
109-
# Additional stats
110-
kill_participation: stat.kill_participation || 0,
111-
damage_share: stat.damage_share || 0,
112-
gold_share: stat.gold_share || 0,
113-
wards_placed: stat.wards_placed || 0,
114-
wards_destroyed: stat.wards_destroyed || 0,
115-
control_wards: stat.control_wards_purchased || 0,
116-
healing_done: stat.healing_done || 0,
117-
double_kills: stat.double_kills || 0,
118-
triple_kills: stat.triple_kills || 0,
119-
quadra_kills: stat.quadra_kills || 0,
120-
penta_kills: stat.penta_kills || 0,
121-
first_blood: stat.first_blood || false,
122-
first_tower: stat.first_tower || false,
123-
largest_killing_spree: stat.largest_killing_spree || 0,
124-
largest_multi_kill: stat.largest_multi_kill || 0,
125-
# Items, runes and spells
126-
items: (stat.items || []).map { |id| { id: id, icon_url: riot_service.item_icon_url(id) } },
127-
runes: (stat.runes || []).map { |id| { id: id, icon_url: riot_service.rune_icon_url(id) } },
128-
spells: [
129-
{ name: stat.summoner_spell_1, icon_url: riot_service.spell_icon_url(stat.summoner_spell_1&.to_i) },
130-
{ name: stat.summoner_spell_2, icon_url: riot_service.spell_icon_url(stat.summoner_spell_2&.to_i) }
131-
].select { |s| s[:name].present? },
132-
role: stat.role
133-
}
134-
end.compact
107+
build_match_entry(stat, riot_service)
108+
end
109+
end
135110

136-
render_success({
137-
player: PlayerSerializer.render_as_hash(player),
138-
champion: champion,
139-
icon_url: riot_service.champion_icon_url(champion),
140-
aggregate_stats: aggregate_stats,
141-
matches: matches_data
142-
})
143-
rescue ActiveRecord::RecordNotFound
144-
render_error(message: 'Player not found', code: 'PLAYER_NOT_FOUND', status: :not_found)
145-
rescue StandardError => e
146-
Rails.logger.error("Error in champions#details: #{e.message}")
147-
Rails.logger.error(e.backtrace.join("\n"))
148-
render_error(message: "Failed to load champion details: #{e.message}", code: 'INTERNAL_ERROR',
149-
status: :internal_server_error)
111+
def build_match_entry(stat, riot_service)
112+
{
113+
match_id: stat.match.id,
114+
game_id: stat.match.riot_match_id,
115+
date: stat.match.game_start&.strftime('%Y-%m-%d %H:%M'),
116+
victory: stat.match.victory?,
117+
game_duration: stat.match.game_duration || 0,
118+
kda: stat.kda_display,
119+
kda_ratio: (stat.kda_ratio || 0).round(2),
120+
kills: stat.kills || 0,
121+
deaths: stat.deaths || 0,
122+
assists: stat.assists || 0,
123+
cs: stat.cs || 0,
124+
cs_per_min: (stat.cs_per_min || 0).round(1),
125+
damage_dealt: stat.damage_dealt_total || 0,
126+
damage_taken: stat.damage_taken || 0,
127+
gold_earned: stat.gold_earned || 0,
128+
gold_per_min: (stat.gold_per_min || 0).round(0),
129+
vision_score: stat.vision_score || 0,
130+
performance_score: stat.performance_score || 0,
131+
kill_participation: stat.kill_participation || 0,
132+
damage_share: stat.damage_share || 0,
133+
gold_share: stat.gold_share || 0,
134+
wards_placed: stat.wards_placed || 0,
135+
wards_destroyed: stat.wards_destroyed || 0,
136+
control_wards: stat.control_wards_purchased || 0,
137+
healing_done: stat.healing_done || 0,
138+
double_kills: stat.double_kills || 0,
139+
triple_kills: stat.triple_kills || 0,
140+
quadra_kills: stat.quadra_kills || 0,
141+
penta_kills: stat.penta_kills || 0,
142+
first_blood: stat.first_blood || false,
143+
first_tower: stat.first_tower || false,
144+
largest_killing_spree: stat.largest_killing_spree || 0,
145+
largest_multi_kill: stat.largest_multi_kill || 0,
146+
items: (stat.items || []).map { |id| { id: id, icon_url: riot_service.item_icon_url(id) } },
147+
runes: (stat.runes || []).map { |id| { id: id, icon_url: riot_service.rune_icon_url(id) } },
148+
spells: build_spells(stat, riot_service),
149+
role: stat.role
150+
}
150151
end
151152

152-
private
153+
def build_spells(stat, riot_service)
154+
[
155+
{ name: stat.summoner_spell_1, icon_url: riot_service.spell_icon_url(stat.summoner_spell_1&.to_i) },
156+
{ name: stat.summoner_spell_2, icon_url: riot_service.spell_icon_url(stat.summoner_spell_2&.to_i) }
157+
].select { |s| s[:name].present? }
158+
end
153159

154160
def fetch_champion_stats(player)
155161
PlayerMatchStat.where(player: player)
@@ -196,21 +202,21 @@ def build_champion_data(player, champion_stats)
196202
player: PlayerSerializer.render_as_hash(player),
197203
champion_stats: champion_stats,
198204
top_champions: champion_stats.take(5),
199-
champion_diversity: {
200-
total_champions: champion_stats.count,
201-
highly_played: champion_stats.count { |c| c[:games_played] >= 10 },
202-
average_games: if champion_stats.empty?
203-
0
204-
else
205-
(champion_stats.sum do |c|
206-
c[:games_played]
207-
end / champion_stats.count.to_f).round(1)
208-
end
209-
}
205+
champion_diversity: build_champion_diversity(champion_stats)
210206
}
211207
end
212208

213-
private
209+
def build_champion_diversity(champion_stats)
210+
{
211+
total_champions: champion_stats.count,
212+
highly_played: champion_stats.count { |c| c[:games_played] >= 10 },
213+
average_games: champion_stats.empty? ? 0 : average_games_per_champion(champion_stats)
214+
}
215+
end
216+
217+
def average_games_per_champion(champion_stats)
218+
(champion_stats.sum { |c| c[:games_played] } / champion_stats.count.to_f).round(1)
219+
end
214220

215221
def calculate_mastery_grade(win_rate, avg_kda)
216222
score = (win_rate * 100 * 0.6) + ((avg_kda || 0) * 10 * 0.4)

app/controllers/api/v1/scouting/players_controller.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,9 @@ def apply_basic_filters(targets)
256256
# Filter by watchlist fields if in watchlist mode
257257
if params[:my_watchlist] == 'true'
258258
targets = targets.where(scouting_watchlists: { priority: params[:priority] }) if params[:priority].present?
259-
targets = targets.where(scouting_watchlists: { assigned_to_id: params[:assigned_to_id] }) if params[:assigned_to_id].present?
259+
if params[:assigned_to_id].present?
260+
targets = targets.where(scouting_watchlists: { assigned_to_id: params[:assigned_to_id] })
261+
end
260262
end
261263

262264
targets

app/modules/analytics/services/performance_analytics_service.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,10 @@ def calculate_kill_participation(stats)
308308
player_participation = player_kills + player_assists
309309

310310
# Team's total kills in this match (all players from same org)
311-
team_kills = PlayerMatchStat.joins(:player)
312-
.where(match_id: stat.match_id, players: { organization_id: player.organization_id })
313-
.sum(:kills)
311+
team_kills = PlayerMatchStat
312+
.joins(:player)
313+
.where(match_id: stat.match_id, players: { organization_id: player.organization_id })
314+
.sum(:kills)
314315

315316
# Calculate KP% for this match
316317
if team_kills > 0

0 commit comments

Comments
 (0)