2.1 : New
This commit is contained in:
@@ -725,6 +725,7 @@ class StatsService:
|
||||
metrics = [
|
||||
'basic_avg_rating', 'basic_avg_kd', 'basic_avg_kast', 'basic_avg_rws', 'basic_avg_adr',
|
||||
'basic_avg_headshot_kills', 'basic_headshot_rate', 'basic_avg_assisted_kill', 'basic_avg_awp_kill', 'basic_avg_jump_count',
|
||||
'basic_avg_knife_kill', 'basic_avg_zeus_kill', 'basic_zeus_pick_rate',
|
||||
'basic_avg_mvps', 'basic_avg_plants', 'basic_avg_defuses', 'basic_avg_flash_assists',
|
||||
'basic_avg_first_kill', 'basic_avg_first_death', 'basic_first_kill_rate', 'basic_first_death_rate',
|
||||
'basic_avg_kill_2', 'basic_avg_kill_3', 'basic_avg_kill_4', 'basic_avg_kill_5',
|
||||
@@ -745,6 +746,13 @@ class StatsService:
|
||||
# New: ECO & PACE
|
||||
'eco_avg_damage_per_1k', 'eco_rating_eco_rounds', 'eco_kd_ratio', 'eco_avg_rounds',
|
||||
'pace_avg_time_to_first_contact', 'pace_trade_kill_rate', 'pace_opening_kill_time', 'pace_avg_life_time',
|
||||
# New: ROUND (Round Dynamics)
|
||||
'rd_phase_kill_early_share', 'rd_phase_kill_mid_share', 'rd_phase_kill_late_share',
|
||||
'rd_phase_death_early_share', 'rd_phase_death_mid_share', 'rd_phase_death_late_share',
|
||||
'rd_firstdeath_team_first_death_win_rate', 'rd_invalid_death_rate',
|
||||
'rd_pressure_kpr_ratio', 'rd_matchpoint_kpr_ratio', 'rd_trade_response_10s_rate',
|
||||
'rd_pressure_perf_ratio', 'rd_matchpoint_perf_ratio',
|
||||
'rd_comeback_kill_share', 'map_stability_coef',
|
||||
# New: Party Size Stats
|
||||
'party_1_win_rate', 'party_1_rating', 'party_1_adr',
|
||||
'party_2_win_rate', 'party_2_rating', 'party_2_adr',
|
||||
@@ -766,7 +774,7 @@ class StatsService:
|
||||
# But here we just use L3 columns directly.
|
||||
|
||||
# Define metrics where LOWER is BETTER
|
||||
lower_is_better = ['pace_avg_time_to_first_contact', 'pace_opening_kill_time']
|
||||
lower_is_better = ['pace_avg_time_to_first_contact', 'pace_opening_kill_time', 'rd_invalid_death_rate', 'map_stability_coef']
|
||||
|
||||
result = {}
|
||||
|
||||
@@ -808,6 +816,141 @@ class StatsService:
|
||||
if m in legacy_map:
|
||||
result[legacy_map[m]] = result[m]
|
||||
|
||||
def build_roundtype_metric_distribution(metric_key, round_type, subkey):
|
||||
values2 = []
|
||||
for sid, p in stats_map.items():
|
||||
raw = p.get('rd_roundtype_split_json') or ''
|
||||
if not raw:
|
||||
continue
|
||||
try:
|
||||
obj = json.loads(raw) if isinstance(raw, str) else raw
|
||||
except:
|
||||
continue
|
||||
if not isinstance(obj, dict):
|
||||
continue
|
||||
bucket = obj.get(round_type)
|
||||
if not isinstance(bucket, dict):
|
||||
continue
|
||||
v = bucket.get(subkey)
|
||||
if v is None:
|
||||
continue
|
||||
try:
|
||||
v = float(v)
|
||||
except:
|
||||
continue
|
||||
values2.append(v)
|
||||
raw_target = stats_map.get(target_steam_id, {}).get('rd_roundtype_split_json') or ''
|
||||
target_val2 = None
|
||||
if raw_target:
|
||||
try:
|
||||
obj_t = json.loads(raw_target) if isinstance(raw_target, str) else raw_target
|
||||
if isinstance(obj_t, dict) and isinstance(obj_t.get(round_type), dict):
|
||||
tv = obj_t[round_type].get(subkey)
|
||||
if tv is not None:
|
||||
target_val2 = float(tv)
|
||||
except:
|
||||
target_val2 = None
|
||||
if not values2 or target_val2 is None:
|
||||
return None
|
||||
values2.sort(reverse=True)
|
||||
try:
|
||||
rank2 = values2.index(target_val2) + 1
|
||||
except ValueError:
|
||||
rank2 = len(values2)
|
||||
return {
|
||||
'val': target_val2,
|
||||
'rank': rank2,
|
||||
'total': len(values2),
|
||||
'min': min(values2),
|
||||
'max': max(values2),
|
||||
'avg': sum(values2) / len(values2),
|
||||
'inverted': False
|
||||
}
|
||||
|
||||
rt_kpr_types = ['pistol', 'reg', 'overtime']
|
||||
rt_perf_types = ['eco', 'rifle', 'fullbuy', 'overtime']
|
||||
for t in rt_kpr_types:
|
||||
result[f'rd_rt_kpr_{t}'] = build_roundtype_metric_distribution('rd_roundtype_split_json', t, 'kpr')
|
||||
for t in rt_perf_types:
|
||||
result[f'rd_rt_perf_{t}'] = build_roundtype_metric_distribution('rd_roundtype_split_json', t, 'perf')
|
||||
|
||||
top_weapon_rank_map = {}
|
||||
try:
|
||||
raw_tw = stats_map.get(target_steam_id, {}).get('rd_weapon_top_json') or '[]'
|
||||
tw_items = json.loads(raw_tw) if isinstance(raw_tw, str) else raw_tw
|
||||
weapons = []
|
||||
if isinstance(tw_items, list):
|
||||
for it in tw_items:
|
||||
if isinstance(it, dict) and it.get('weapon'):
|
||||
weapons.append(str(it.get('weapon')))
|
||||
weapons = weapons[:5]
|
||||
except Exception:
|
||||
weapons = []
|
||||
|
||||
if weapons:
|
||||
w_placeholders = ','.join('?' for _ in weapons)
|
||||
sql_w = f"""
|
||||
SELECT attacker_steam_id as steam_id_64,
|
||||
weapon,
|
||||
COUNT(*) as kills,
|
||||
SUM(is_headshot) as hs
|
||||
FROM fact_round_events
|
||||
WHERE event_type='kill'
|
||||
AND attacker_steam_id IN ({l2_placeholders})
|
||||
AND weapon IN ({w_placeholders})
|
||||
GROUP BY attacker_steam_id, weapon
|
||||
"""
|
||||
weapon_rows = query_db('l2', sql_w, active_roster_ids + weapons)
|
||||
per_weapon = {}
|
||||
for r in weapon_rows:
|
||||
sid = str(r['steam_id_64'])
|
||||
w = str(r['weapon'] or '')
|
||||
if not w:
|
||||
continue
|
||||
kills = int(r['kills'] or 0)
|
||||
hs = int(r['hs'] or 0)
|
||||
mp = stats_map.get(sid, {}).get('total_matches') or 0
|
||||
try:
|
||||
mp = float(mp)
|
||||
except Exception:
|
||||
mp = 0
|
||||
kpm = (kills / mp) if (kills > 0 and mp > 0) else None
|
||||
hs_rate = (hs / kills) if kills > 0 else None
|
||||
per_weapon.setdefault(w, {})[sid] = {"kpm": kpm, "hs_rate": hs_rate}
|
||||
|
||||
for w in weapons:
|
||||
d = per_weapon.get(w) or {}
|
||||
target_d = d.get(target_steam_id) or {}
|
||||
target_kpm = target_d.get("kpm")
|
||||
target_hs = target_d.get("hs_rate")
|
||||
|
||||
kpm_vals = [v.get("kpm") for v in d.values() if v.get("kpm") is not None]
|
||||
hs_vals = [v.get("hs_rate") for v in d.values() if v.get("hs_rate") is not None]
|
||||
|
||||
kpm_rank = None
|
||||
hs_rank = None
|
||||
if kpm_vals and target_kpm is not None:
|
||||
kpm_vals.sort(reverse=True)
|
||||
try:
|
||||
kpm_rank = kpm_vals.index(target_kpm) + 1
|
||||
except ValueError:
|
||||
kpm_rank = len(kpm_vals)
|
||||
if hs_vals and target_hs is not None:
|
||||
hs_vals.sort(reverse=True)
|
||||
try:
|
||||
hs_rank = hs_vals.index(target_hs) + 1
|
||||
except ValueError:
|
||||
hs_rank = len(hs_vals)
|
||||
|
||||
top_weapon_rank_map[w] = {
|
||||
"kpm_rank": kpm_rank,
|
||||
"kpm_total": len(kpm_vals),
|
||||
"hs_rank": hs_rank,
|
||||
"hs_total": len(hs_vals),
|
||||
}
|
||||
|
||||
result['top_weapon_rank_map'] = top_weapon_rank_map
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
|
||||
Reference in New Issue
Block a user