import sqlite3 import pandas as pd import numpy as np import os DB_L2_PATH = r'd:\Documents\trae_projects\yrtv\database\L2\L2_Main.sqlite' def get_db_connection(): conn = sqlite3.connect(DB_L2_PATH) conn.row_factory = sqlite3.Row return conn def load_data_and_calculate(conn, min_matches=5): print("Loading Basic Stats...") # 1. Basic Stats query_basic = """ SELECT steam_id_64, COUNT(*) as matches_played, AVG(rating) as avg_rating, AVG(kd_ratio) as avg_kd, AVG(adr) as avg_adr, AVG(kast) as avg_kast, SUM(first_kill) as total_fk, SUM(first_death) as total_fd, SUM(clutch_1v1) + SUM(clutch_1v2) + SUM(clutch_1v3) + SUM(clutch_1v4) + SUM(clutch_1v5) as total_clutches, SUM(throw_harm) as total_util_dmg, SUM(flash_time) as total_flash_time, SUM(flash_enemy) as total_flash_enemy FROM fact_match_players GROUP BY steam_id_64 HAVING COUNT(*) >= ? """ df_basic = pd.read_sql_query(query_basic, conn, params=(min_matches,)) valid_ids = tuple(df_basic['steam_id_64'].tolist()) if not valid_ids: print("No players found.") return None placeholders = ','.join(['?'] * len(valid_ids)) # 2. Side Stats (T/CT) via Economy Table (which has side info) print("Loading Side Stats via Round Map...") # Map each round+player to a side query_side_map = f""" SELECT match_id, round_num, steam_id_64, side FROM fact_round_player_economy WHERE steam_id_64 IN ({placeholders}) """ try: df_sides = pd.read_sql_query(query_side_map, conn, params=valid_ids) # Get all Kills query_kills = f""" SELECT match_id, round_num, attacker_steam_id as steam_id_64, COUNT(*) as kills FROM fact_round_events WHERE event_type = 'kill' AND attacker_steam_id IN ({placeholders}) GROUP BY match_id, round_num, attacker_steam_id """ df_kills = pd.read_sql_query(query_kills, conn, params=valid_ids) # Merge to get Kills per Side df_merged = df_kills.merge(df_sides, on=['match_id', 'round_num', 'steam_id_64'], how='inner') # Aggregate side_stats = df_merged.groupby(['steam_id_64', 'side'])['kills'].sum().unstack(fill_value=0) side_stats.columns = [f'kills_{c.lower()}' for c in side_stats.columns] # Also need deaths to calc KD (approx) # Assuming deaths are in events as victim query_deaths = f""" SELECT match_id, round_num, victim_steam_id as steam_id_64, COUNT(*) as deaths FROM fact_round_events WHERE event_type = 'kill' AND victim_steam_id IN ({placeholders}) GROUP BY match_id, round_num, victim_steam_id """ df_deaths = pd.read_sql_query(query_deaths, conn, params=valid_ids) df_merged_d = df_deaths.merge(df_sides, on=['match_id', 'round_num', 'steam_id_64'], how='inner') side_stats_d = df_merged_d.groupby(['steam_id_64', 'side'])['deaths'].sum().unstack(fill_value=0) side_stats_d.columns = [f'deaths_{c.lower()}' for c in side_stats_d.columns] # Combine df_side_final = side_stats.join(side_stats_d).fillna(0) df_side_final['ct_kd'] = df_side_final.get('kills_ct', 0) / df_side_final.get('deaths_ct', 1).replace(0, 1) df_side_final['t_kd'] = df_side_final.get('kills_t', 0) / df_side_final.get('deaths_t', 1).replace(0, 1) except Exception as e: print(f"Side stats failed: {e}") df_side_final = pd.DataFrame({'steam_id_64': list(valid_ids)}) # 3. PTL (Pistol) via Rounds 1 and 13 print("Loading Pistol Stats via Rounds...") query_pistol_kills = f""" SELECT ev.attacker_steam_id as steam_id_64, COUNT(*) as pistol_kills FROM fact_round_events ev WHERE ev.attacker_steam_id IN ({placeholders}) AND ev.event_type = 'kill' AND ev.round_num IN (1, 13) GROUP BY ev.attacker_steam_id """ df_ptl = pd.read_sql_query(query_pistol_kills, conn, params=valid_ids) # 4. HPS print("Loading HPS Stats...") query_close = f""" SELECT mp.steam_id_64, AVG(mp.rating) as close_match_rating FROM fact_match_players mp JOIN fact_matches m ON mp.match_id = m.match_id WHERE mp.steam_id_64 IN ({placeholders}) AND ABS(m.score_team1 - m.score_team2) <= 3 GROUP BY mp.steam_id_64 """ df_hps = pd.read_sql_query(query_close, conn, params=valid_ids) # 5. STA query_sta = f""" SELECT mp.steam_id_64, mp.rating, mp.is_win FROM fact_match_players mp WHERE mp.steam_id_64 IN ({placeholders}) """ df_matches = pd.read_sql_query(query_sta, conn, params=valid_ids) sta_data = [] for pid, group in df_matches.groupby('steam_id_64'): rating_std = group['rating'].std() win_rating = group[group['is_win']==1]['rating'].mean() loss_rating = group[group['is_win']==0]['rating'].mean() sta_data.append({'steam_id_64': pid, 'rating_std': rating_std, 'win_rating': win_rating, 'loss_rating': loss_rating}) df_sta = pd.DataFrame(sta_data) # --- Merge All --- df = df_basic.merge(df_side_final, on='steam_id_64', how='left') df = df.merge(df_hps, on='steam_id_64', how='left') df = df.merge(df_ptl, on='steam_id_64', how='left').fillna(0) df = df.merge(df_sta, on='steam_id_64', how='left') return df def normalize_series(series): min_v = series.min() max_v = series.max() if pd.isna(min_v) or pd.isna(max_v) or min_v == max_v: return pd.Series([50]*len(series), index=series.index) return (series - min_v) / (max_v - min_v) * 100 def calculate_scores(df): df = df.copy() # BAT df['n_rating'] = normalize_series(df['avg_rating']) df['n_kd'] = normalize_series(df['avg_kd']) df['n_adr'] = normalize_series(df['avg_adr']) df['n_kast'] = normalize_series(df['avg_kast']) df['score_BAT'] = 0.4*df['n_rating'] + 0.3*df['n_kd'] + 0.2*df['n_adr'] + 0.1*df['n_kast'] # STA df['n_std'] = normalize_series(df['rating_std'].fillna(0)) df['n_win_r'] = normalize_series(df['win_rating'].fillna(0)) df['n_loss_r'] = normalize_series(df['loss_rating'].fillna(0)) df['score_STA'] = 0.5*(100 - df['n_std']) + 0.25*df['n_win_r'] + 0.25*df['n_loss_r'] # UTIL df['n_util_dmg'] = normalize_series(df['total_util_dmg'] / df['matches_played']) df['n_flash'] = normalize_series(df['total_flash_time'] / df['matches_played']) df['score_UTIL'] = 0.6*df['n_util_dmg'] + 0.4*df['n_flash'] # T/CT (Calculated from Event Logs) df['n_ct_kd'] = normalize_series(df['ct_kd'].fillna(0)) df['n_t_kd'] = normalize_series(df['t_kd'].fillna(0)) df['score_TCT'] = 0.5*df['n_ct_kd'] + 0.5*df['n_t_kd'] # HPS df['n_clutch'] = normalize_series(df['total_clutches'] / df['matches_played']) df['n_close_r'] = normalize_series(df['close_match_rating'].fillna(0)) df['score_HPS'] = 0.5*df['n_clutch'] + 0.5*df['n_close_r'] # PTL df['n_pistol'] = normalize_series(df['pistol_kills'] / df['matches_played']) df['score_PTL'] = df['n_pistol'] return df def main(): conn = get_db_connection() try: df = load_data_and_calculate(conn) if df is None: return # Debug: Print raw stats for checking T/CT issue print("\n--- Raw T/CT Stats Sample ---") if 'ct_kd' in df.columns: print(df[['steam_id_64', 'ct_kd', 't_kd']].head()) else: print("CT/KD columns missing") results = calculate_scores(df) print("\n--- Final Dimension Scores (Top 5 by BAT) ---") cols = ['steam_id_64', 'score_BAT', 'score_STA', 'score_UTIL', 'score_TCT', 'score_HPS', 'score_PTL'] print(results[cols].sort_values('score_BAT', ascending=False).head(5)) except Exception as e: print(f"Error: {e}") import traceback traceback.print_exc() finally: conn.close() if __name__ == "__main__": main()