1.0.3: Enhanced match detail - Added h2h and roundhistory.

This commit is contained in:
2026-01-26 17:08:43 +08:00
parent f147b4d65a
commit 57fb6ce1f4
4 changed files with 574 additions and 157 deletions

View File

@@ -93,52 +93,87 @@ class StatsService:
if not active_roster_ids:
result_map = {}
else:
# 1. Get UIDs for Roster Members involved in these matches
# We query fact_match_players to ensure we get the UIDs actually used in these matches
roster_placeholders = ','.join('?' for _ in active_roster_ids)
# We cast steam_id_64 to TEXT to ensure match even if stored as int
our_result_sql = f"""
SELECT mp.match_id, mp.team_id, m.winner_team, COUNT(*) as our_count
FROM fact_match_players mp
JOIN fact_matches m ON mp.match_id = m.match_id
WHERE mp.match_id IN ({placeholders})
AND CAST(mp.steam_id_64 AS TEXT) IN ({roster_placeholders})
GROUP BY mp.match_id, mp.team_id
uid_sql = f"""
SELECT DISTINCT steam_id_64, uid
FROM fact_match_players
WHERE match_id IN ({placeholders})
AND CAST(steam_id_64 AS TEXT) IN ({roster_placeholders})
"""
combined_args_uid = match_ids + active_roster_ids
uid_rows = query_db('l2', uid_sql, combined_args_uid)
# Combine args: match_ids + roster_ids
combined_args = match_ids + active_roster_ids
our_rows = query_db('l2', our_result_sql, combined_args)
# Set of "Our UIDs" (as strings)
our_uids = set()
for r in uid_rows:
if r['uid']:
our_uids.add(str(r['uid']))
# Map match_id -> result ('win', 'loss', 'draw', 'mixed')
# 2. Get Group UIDs and Winner info from fact_match_teams
# We need to know which group contains our UIDs
teams_sql = f"""
SELECT fmt.match_id, fmt.group_id, fmt.group_uids, m.winner_team
FROM fact_match_teams fmt
JOIN fact_matches m ON fmt.match_id = m.match_id
WHERE fmt.match_id IN ({placeholders})
"""
teams_rows = query_db('l2', teams_sql, match_ids)
# 3. Determine Result per Match
result_map = {}
match_sides = {}
match_winners = {}
# Group data by match
match_groups = {} # match_id -> {group_id: [uids...], winner: int}
for r in our_rows:
for r in teams_rows:
mid = r['match_id']
if mid not in match_sides: match_sides[mid] = {}
match_sides[mid][r['team_id']] = r['our_count']
match_winners[mid] = r['winner_team']
gid = r['group_id']
uids_str = r['group_uids'] or ""
# Split and clean UIDs
uids = set(str(u).strip() for u in uids_str.split(',') if u.strip())
for mid, sides in match_sides.items():
winner = match_winners.get(mid)
if not winner:
result_map[mid] = 'draw'
continue
our_on_winner = sides.get(winner, 0)
loser = 2 if winner == 1 else 1
our_on_loser = sides.get(loser, 0)
if mid not in match_groups:
match_groups[mid] = {'groups': {}, 'winner': r['winner_team']}
if our_on_winner > 0 and our_on_loser == 0:
match_groups[mid]['groups'][gid] = uids
# Analyze
for mid, data in match_groups.items():
winner_gid = data['winner']
groups = data['groups']
our_in_winner = False
our_in_loser = False
# Check each group
for gid, uids in groups.items():
# Intersection of Our UIDs and Group UIDs
common = our_uids.intersection(uids)
if common:
if gid == winner_gid:
our_in_winner = True
else:
our_in_loser = True
if our_in_winner and not our_in_loser:
result_map[mid] = 'win'
elif our_on_loser > 0 and our_on_winner == 0:
elif our_in_loser and not our_in_winner:
result_map[mid] = 'loss'
elif our_on_winner > 0 and our_on_loser > 0:
result_map[mid] = 'mixed'
elif our_in_winner and our_in_loser:
result_map[mid] = 'mixed'
else:
result_map[mid] = None
# Fallback: If UID matching failed (maybe missing UIDs), try old team_id method?
# Or just leave it as None (safe)
pass
# Convert to dict to modify
matches = [dict(m) for m in matches]
for m in matches:
m['avg_elo'] = elo_map.get(m['match_id'], 0)
m['max_party'] = party_map.get(m['match_id'], 1)
m['our_result'] = result_map.get(m['match_id'])
# Convert to dict to modify
matches = [dict(m) for m in matches]
@@ -387,3 +422,78 @@ class StatsService:
"""
return query_db('l2', sql)
@staticmethod
def get_head_to_head_stats(match_id):
"""
Returns a matrix of kills between players.
List of {attacker_steam_id, victim_steam_id, kills}
"""
sql = """
SELECT attacker_steam_id, victim_steam_id, COUNT(*) as kills
FROM fact_round_events
WHERE match_id = ? AND event_type = 'kill'
GROUP BY attacker_steam_id, victim_steam_id
"""
return query_db('l2', sql, [match_id])
@staticmethod
def get_match_round_details(match_id):
"""
Returns a detailed dictionary of rounds, events, and economy.
{
round_num: {
info: {winner_side, win_reason_desc, end_time_stamp...},
events: [ {event_type, event_time, attacker..., weapon...}, ... ],
economy: { steam_id: {main_weapon, equipment_value...}, ... }
}
}
"""
# 1. Base Round Info
rounds_sql = "SELECT * FROM fact_rounds WHERE match_id = ? ORDER BY round_num"
rounds_rows = query_db('l2', rounds_sql, [match_id])
if not rounds_rows:
return {}
# 2. Events
events_sql = """
SELECT * FROM fact_round_events
WHERE match_id = ?
ORDER BY round_num, event_time
"""
events_rows = query_db('l2', events_sql, [match_id])
# 3. Economy (if avail)
eco_sql = """
SELECT * FROM fact_round_player_economy
WHERE match_id = ?
"""
eco_rows = query_db('l2', eco_sql, [match_id])
# Structure Data
result = {}
# Initialize rounds
for r in rounds_rows:
r_num = r['round_num']
result[r_num] = {
'info': dict(r),
'events': [],
'economy': {}
}
# Group events
for e in events_rows:
r_num = e['round_num']
if r_num in result:
result[r_num]['events'].append(dict(e))
# Group economy
for eco in eco_rows:
r_num = eco['round_num']
sid = eco['steam_id_64']
if r_num in result:
result[r_num]['economy'][sid] = dict(eco)
return result