Skip to content

Commit da18903

Browse files
committed
refactor(analytics): improve team comparison controller code quality
Refactored the Analytics::Controllers::TeamComparisonController to: - Reduce method complexity (AbcSize, CyclomaticComplexity) - Break down large methods into smaller, focused methods - Add comprehensive class documentation - Extract common calculations into reusable methods - Improve code readability and maintainability - Fix all MultilineBlockChain violations
1 parent c2ca78f commit da18903

1 file changed

Lines changed: 136 additions & 92 deletions

File tree

Lines changed: 136 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,136 @@
1-
# frozen_string_literal: true
2-
3-
module Analytics
4-
module Controllers
5-
class TeamComparisonController < Api::V1::BaseController
6-
def index
7-
players = organization_scoped(Player).active.includes(:player_match_stats)
8-
9-
matches = organization_scoped(Match)
10-
if params[:start_date].present? && params[:end_date].present?
11-
matches = matches.in_date_range(params[:start_date], params[:end_date])
12-
else
13-
matches = matches.recent(30)
14-
end
15-
16-
comparison_data = {
17-
players: players.map do |player|
18-
stats = PlayerMatchStat.where(player: player, match: matches)
19-
next if stats.empty?
20-
21-
{
22-
player: PlayerSerializer.render_as_hash(player),
23-
games_played: stats.count,
24-
kda: calculate_kda(stats),
25-
avg_damage: stats.average(:total_damage_dealt)&.round(0) || 0,
26-
avg_gold: stats.average(:gold_earned)&.round(0) || 0,
27-
avg_cs: stats.average('minions_killed + jungle_minions_killed')&.round(1) || 0,
28-
avg_vision_score: stats.average(:vision_score)&.round(1) || 0,
29-
avg_performance_score: stats.average(:performance_score)&.round(1) || 0,
30-
multikills: {
31-
double: stats.sum(:double_kills),
32-
triple: stats.sum(:triple_kills),
33-
quadra: stats.sum(:quadra_kills),
34-
penta: stats.sum(:penta_kills)
35-
}
36-
}
37-
end.compact.sort_by { |p| -p[:avg_performance_score] },
38-
team_averages: calculate_team_averages(matches),
39-
role_rankings: calculate_role_rankings(players, matches)
40-
}
41-
42-
render_success(comparison_data)
43-
end
44-
45-
private
46-
47-
def calculate_kda(stats)
48-
total_kills = stats.sum(:kills)
49-
total_deaths = stats.sum(:deaths)
50-
total_assists = stats.sum(:assists)
51-
52-
deaths = total_deaths.zero? ? 1 : total_deaths
53-
((total_kills + total_assists).to_f / deaths).round(2)
54-
end
55-
56-
def calculate_team_averages(matches)
57-
all_stats = PlayerMatchStat.where(match: matches)
58-
59-
{
60-
avg_kda: calculate_kda(all_stats),
61-
avg_damage: all_stats.average(:total_damage_dealt)&.round(0) || 0,
62-
avg_gold: all_stats.average(:gold_earned)&.round(0) || 0,
63-
avg_cs: all_stats.average('minions_killed + jungle_minions_killed')&.round(1) || 0,
64-
avg_vision_score: all_stats.average(:vision_score)&.round(1) || 0
65-
}
66-
end
67-
68-
def calculate_role_rankings(players, matches)
69-
rankings = {}
70-
71-
%w[top jungle mid adc support].each do |role|
72-
role_players = players.where(role: role)
73-
role_data = role_players.map do |player|
74-
stats = PlayerMatchStat.where(player: player, match: matches)
75-
next if stats.empty?
76-
77-
{
78-
player_id: player.id,
79-
summoner_name: player.summoner_name,
80-
avg_performance: stats.average(:performance_score)&.round(1) || 0,
81-
games: stats.count
82-
}
83-
end.compact.sort_by { |p| -p[:avg_performance] }
84-
85-
rankings[role] = role_data
86-
end
87-
88-
rankings
89-
end
90-
end
91-
end
92-
end
1+
# frozen_string_literal: true
2+
3+
module Analytics
4+
module Controllers
5+
# Controller for team performance comparison and analytics
6+
# Provides endpoints to compare player statistics, team averages, and role rankings
7+
class TeamComparisonController < Api::V1::BaseController
8+
def index
9+
players = fetch_active_players
10+
matches = fetch_filtered_matches
11+
12+
comparison_data = build_comparison_data(players, matches)
13+
render_success(comparison_data)
14+
end
15+
16+
private
17+
18+
def fetch_active_players
19+
organization_scoped(Player).active.includes(:player_match_stats)
20+
end
21+
22+
def fetch_filtered_matches
23+
matches = organization_scoped(Match)
24+
apply_date_range_filter(matches)
25+
end
26+
27+
def apply_date_range_filter(matches)
28+
return matches.in_date_range(params[:start_date], params[:end_date]) if date_range_provided?
29+
30+
matches.recent(30)
31+
end
32+
33+
def date_range_provided?
34+
params[:start_date].present? && params[:end_date].present?
35+
end
36+
37+
def build_comparison_data(players, matches)
38+
{
39+
players: build_player_comparisons(players, matches),
40+
team_averages: calculate_team_averages(matches),
41+
role_rankings: calculate_role_rankings(players, matches)
42+
}
43+
end
44+
45+
def build_player_comparisons(players, matches)
46+
player_stats = players.map { |player| build_player_stats(player, matches) }
47+
sorted_player_stats = player_stats.compact
48+
sorted_player_stats.sort_by { |p| -p[:avg_performance_score] }
49+
end
50+
51+
def build_player_stats(player, matches)
52+
stats = PlayerMatchStat.where(player: player, match: matches)
53+
return nil if stats.empty?
54+
55+
{
56+
player: PlayerSerializer.render_as_hash(player),
57+
games_played: stats.count,
58+
kda: calculate_kda(stats),
59+
avg_damage: calculate_average(stats, :total_damage_dealt, 0),
60+
avg_gold: calculate_average(stats, :gold_earned, 0),
61+
avg_cs: calculate_cs_average(stats),
62+
avg_vision_score: calculate_average(stats, :vision_score, 1),
63+
avg_performance_score: calculate_average(stats, :performance_score, 1),
64+
multikills: build_multikills_hash(stats)
65+
}
66+
end
67+
68+
def calculate_average(stats, column, precision)
69+
stats.average(column)&.round(precision) || 0
70+
end
71+
72+
def calculate_cs_average(stats)
73+
stats.average('minions_killed + jungle_minions_killed')&.round(1) || 0
74+
end
75+
76+
def build_multikills_hash(stats)
77+
{
78+
double: stats.sum(:double_kills),
79+
triple: stats.sum(:triple_kills),
80+
quadra: stats.sum(:quadra_kills),
81+
penta: stats.sum(:penta_kills)
82+
}
83+
end
84+
85+
def calculate_kda(stats)
86+
total_kills = stats.sum(:kills)
87+
total_deaths = stats.sum(:deaths)
88+
total_assists = stats.sum(:assists)
89+
90+
deaths = total_deaths.zero? ? 1 : total_deaths
91+
((total_kills + total_assists).to_f / deaths).round(2)
92+
end
93+
94+
def calculate_team_averages(matches)
95+
all_stats = PlayerMatchStat.where(match: matches)
96+
97+
{
98+
avg_kda: calculate_kda(all_stats),
99+
avg_damage: calculate_average(all_stats, :total_damage_dealt, 0),
100+
avg_gold: calculate_average(all_stats, :gold_earned, 0),
101+
avg_cs: calculate_cs_average(all_stats),
102+
avg_vision_score: calculate_average(all_stats, :vision_score, 1)
103+
}
104+
end
105+
106+
def calculate_role_rankings(players, matches)
107+
rankings = {}
108+
109+
%w[top jungle mid adc support].each do |role|
110+
rankings[role] = calculate_role_ranking(players, matches, role)
111+
end
112+
113+
rankings
114+
end
115+
116+
def calculate_role_ranking(players, matches, role)
117+
role_players = players.where(role: role)
118+
role_data = role_players.map { |player| build_role_player_stats(player, matches) }
119+
sorted_data = role_data.compact
120+
sorted_data.sort_by { |p| -p[:avg_performance] }
121+
end
122+
123+
def build_role_player_stats(player, matches)
124+
stats = PlayerMatchStat.where(player: player, match: matches)
125+
return nil if stats.empty?
126+
127+
{
128+
player_id: player.id,
129+
summoner_name: player.summoner_name,
130+
avg_performance: stats.average(:performance_score)&.round(1) || 0,
131+
games: stats.count
132+
}
133+
end
134+
end
135+
end
136+
end

0 commit comments

Comments
 (0)