@@ -117,6 +117,13 @@ def create_scouting_target_from_player(previous_org_name:, removal_reason:)
117117 target = ScoutingTarget . find_or_initialize_by ( riot_puuid : player . riot_puuid ) if player . riot_puuid . present?
118118 target ||= ScoutingTarget . new
119119
120+ # Calculate performance data
121+ recent_perf = calculate_recent_performance ( player )
122+ champion_stats = calculate_champion_stats ( player )
123+
124+ # Merge champion stats into recent performance
125+ recent_perf [ :champion_stats ] = champion_stats
126+
120127 # Update global player data
121128 target . assign_attributes (
122129 summoner_name : player . summoner_name ,
@@ -126,7 +133,9 @@ def create_scouting_target_from_player(previous_org_name:, removal_reason:)
126133 current_tier : player . solo_queue_tier ,
127134 current_rank : player . solo_queue_rank ,
128135 current_lp : player . solo_queue_lp ,
129- champion_pool : player . champion_pool ,
136+ champion_pool : calculate_champion_pool_from_stats ( player ) ,
137+ recent_performance : recent_perf ,
138+ performance_trend : calculate_performance_trend ( player ) ,
130139 playstyle : extract_playstyle_from_notes ( player . notes ) ,
131140 twitter_handle : player . twitter_handle ,
132141 status : 'free_agent' ,
@@ -160,6 +169,137 @@ def build_free_agent_notes(previous_org_name, removal_reason, existing_notes = n
160169 notes . join ( "\n \n " )
161170 end
162171
172+ # Calculate champion pool from player's actual match statistics
173+ # Prioritizes champions from champion_pools table, falls back to player_match_stats
174+ # @param player [Player] The player to calculate champion pool for
175+ # @return [Array<String>] Array of champion names (up to 10)
176+ def calculate_champion_pool_from_stats ( player )
177+ # First, try to get from champion_pools table (most reliable)
178+ champions_from_pool = player . champion_pools
179+ . order ( games_played : :desc , average_kda : :desc )
180+ . limit ( 10 )
181+ . pluck ( :champion )
182+
183+ return champions_from_pool if champions_from_pool . any?
184+
185+ # Fallback: get from player_match_stats
186+ champions_from_stats = player . player_match_stats
187+ . group ( :champion )
188+ . order ( 'COUNT(*) DESC' )
189+ . limit ( 10 )
190+ . pluck ( :champion )
191+
192+ return champions_from_stats if champions_from_stats . any?
193+
194+ # Last resort: use the champion_pool array attribute if it exists
195+ player . champion_pool . presence || [ ]
196+ end
197+
198+ # Calculate champion statistics with winrate per champion
199+ # @param player [Player] The player to calculate champion stats for
200+ # @param limit [Integer] Number of recent games to analyze (default: 50)
201+ # @return [Array<Hash>] Array of champion stats with name, games, wins, winrate
202+ def calculate_champion_stats ( player , limit : 50 )
203+ recent_stats = player . player_match_stats
204+ . joins ( :match )
205+ . order ( 'matches.game_start DESC' )
206+ . limit ( limit )
207+
208+ return [ ] if recent_stats . empty?
209+
210+ # Group by champion
211+ champion_data = recent_stats . group_by ( &:champion )
212+
213+ champion_data . map do |champion , stats |
214+ games = stats . count
215+ wins = stats . count { |s | s . match &.victory? }
216+ win_rate = games . zero? ? 0.0 : ( ( wins . to_f / games ) * 100 ) . round ( 1 )
217+
218+ {
219+ champion : champion ,
220+ games : games ,
221+ wins : wins ,
222+ win_rate : win_rate
223+ }
224+ end . sort_by { |c | -c [ :games ] } . take ( 10 ) # Top 10 most played
225+ end
226+
227+ # Calculate recent performance statistics from last 50 games
228+ # @param player [Player] The player to calculate performance for
229+ # @param limit [Integer] Number of recent games to analyze (default: 50)
230+ # @return [Hash] Performance statistics
231+ def calculate_recent_performance ( player , limit : 50 )
232+ recent_stats = player . player_match_stats
233+ . joins ( :match )
234+ . order ( 'matches.game_start DESC' )
235+ . limit ( limit )
236+
237+ return { } if recent_stats . empty?
238+
239+ total_games = recent_stats . count
240+ wins = recent_stats . count { |stat | stat . match &.victory? }
241+
242+ # Calculate KDA manually since it's a virtual method
243+ total_kills = recent_stats . sum ( :kills )
244+ total_deaths = recent_stats . sum ( :deaths )
245+ total_assists = recent_stats . sum ( :assists )
246+ avg_kda = total_deaths . zero? ? total_kills + total_assists : ( ( total_kills + total_assists ) . to_f / total_deaths ) . round ( 2 )
247+
248+ # Calculate averages only for non-null values
249+ damage_shares = recent_stats . pluck ( :damage_share ) . compact
250+ kill_participations = recent_stats . pluck ( :kill_participation ) . compact
251+
252+ {
253+ games_played : total_games ,
254+ wins : wins ,
255+ losses : total_games - wins ,
256+ win_rate : total_games . zero? ? 0.0 : ( ( wins . to_f / total_games ) * 100 ) . round ( 1 ) ,
257+ avg_kda : avg_kda ,
258+ avg_cs_per_min : recent_stats . average ( :cs_per_min ) &.to_f &.round ( 1 ) || 0.0 ,
259+ avg_vision_score : recent_stats . average ( :vision_score ) &.to_f &.round ( 1 ) || 0.0 ,
260+ avg_damage_share : damage_shares . any? ? ( damage_shares . sum / damage_shares . size ) . round ( 1 ) : 0.0 ,
261+ avg_kill_participation : kill_participations . any? ? ( kill_participations . sum / kill_participations . size ) . round ( 1 ) : 0.0 ,
262+ last_game_date : recent_stats . first &.match &.game_start &.to_date
263+ }
264+ end
265+
266+ # Calculate performance trend based on recent games
267+ # @param player [Player] The player to calculate trend for
268+ # @param limit [Integer] Number of recent games to analyze (default: 50)
269+ # @return [String] 'improving', 'stable', or 'declining'
270+ def calculate_performance_trend ( player , limit : 50 )
271+ recent_stats = player . player_match_stats
272+ . joins ( :match )
273+ . order ( 'matches.game_start DESC' )
274+ . limit ( limit )
275+
276+ return 'stable' if recent_stats . count < 20
277+
278+ # Split into two halves
279+ mid_point = recent_stats . count / 2
280+ recent_half = recent_stats . first ( mid_point )
281+ older_half = recent_stats . last ( mid_point )
282+
283+ recent_wr = calculate_win_rate ( recent_half )
284+ older_wr = calculate_win_rate ( older_half )
285+
286+ if recent_wr > older_wr + 10
287+ 'improving'
288+ elsif recent_wr < older_wr - 10
289+ 'declining'
290+ else
291+ 'stable'
292+ end
293+ end
294+
295+ # Helper to calculate win rate from a collection of stats
296+ def calculate_win_rate ( stats )
297+ return 0 if stats . empty?
298+
299+ wins = stats . count { |stat | stat . match &.victory? }
300+ ( wins . to_f / stats . count * 100 ) . round ( 1 )
301+ end
302+
163303 # Extract playstyle from player notes
164304 def extract_playstyle_from_notes ( notes )
165305 return nil if notes . blank?
0 commit comments