from flask import Blueprint, render_template, request, Response from web.services.stats_service import StatsService from web.config import Config import json bp = Blueprint('matches', __name__, url_prefix='/matches') @bp.route('/') def index(): page = request.args.get('page', 1, type=int) map_name = request.args.get('map') date_from = request.args.get('date_from') # Fetch summary stats (for the dashboard) summary_stats = StatsService.get_team_stats_summary() matches, total = StatsService.get_matches(page, Config.ITEMS_PER_PAGE, map_name, date_from) total_pages = (total + Config.ITEMS_PER_PAGE - 1) // Config.ITEMS_PER_PAGE return render_template('matches/list.html', matches=matches, total=total, page=page, total_pages=total_pages, summary_stats=summary_stats) @bp.route('/') def detail(match_id): match = StatsService.get_match_detail(match_id) if not match: return "Match not found", 404 players = StatsService.get_match_players(match_id) # Convert sqlite3.Row objects to dicts to allow modification players = [dict(p) for p in players] rounds = StatsService.get_match_rounds(match_id) # --- Roster Identification --- # Fetch active roster to identify "Our Team" players from web.services.web_service import WebService lineups = WebService.get_lineups() # Assume we use the first/active lineup active_roster_ids = [] if lineups: try: active_roster_ids = json.loads(lineups[0]['player_ids_json']) except: pass # Mark roster players (Ensure strict string comparison) roster_set = set(str(uid) for uid in active_roster_ids) for p in players: p['is_in_roster'] = str(p['steam_id_64']) in roster_set # --- Party Size Calculation --- # Only calculate party size for OUR ROSTER members. # Group roster members by match_team_id roster_parties = {} # match_team_id -> count of roster members for p in players: if p['is_in_roster']: mtid = p.get('match_team_id') if mtid and mtid > 0: key = f"tid_{mtid}" roster_parties[key] = roster_parties.get(key, 0) + 1 # Assign party size ONLY to roster members for p in players: if p['is_in_roster']: mtid = p.get('match_team_id') if mtid and mtid > 0: p['party_size'] = roster_parties.get(f"tid_{mtid}", 1) else: p['party_size'] = 1 # Solo roster player else: p['party_size'] = 0 # Hide party info for non-roster players # Organize players by Side (team_id) # team_id 1 = Team 1, team_id 2 = Team 2 # Note: group_id 1/2 usually corresponds to Team 1/2. # Fallback to team_id if group_id is missing or 0 (legacy data compatibility) team1_players = [p for p in players if p.get('group_id') == 1] team2_players = [p for p in players if p.get('group_id') == 2] # If group_id didn't work (empty lists), try team_id grouping (if team_id is 1/2 only) if not team1_players and not team2_players: team1_players = [p for p in players if p['team_id'] == 1] team2_players = [p for p in players if p['team_id'] == 2] # Explicitly sort by Rating DESC team1_players.sort(key=lambda x: x.get('rating', 0) or 0, reverse=True) team2_players.sort(key=lambda x: x.get('rating', 0) or 0, reverse=True) # New Data for Enhanced Detail View h2h_stats = StatsService.get_head_to_head_stats(match_id) round_details = StatsService.get_match_round_details(match_id) # Convert H2H stats to a more usable format (nested dict) # h2h_matrix[attacker_id][victim_id] = kills h2h_matrix = {} if h2h_stats: for row in h2h_stats: a_id = row['attacker_steam_id'] v_id = row['victim_steam_id'] kills = row['kills'] if a_id not in h2h_matrix: h2h_matrix[a_id] = {} h2h_matrix[a_id][v_id] = kills # Create a mapping of SteamID -> Username for the template # We can use the players list we already have player_name_map = {} for p in players: sid = p.get('steam_id_64') name = p.get('username') if sid and name: player_name_map[str(sid)] = name return render_template('matches/detail.html', match=match, team1_players=team1_players, team2_players=team2_players, rounds=rounds, h2h_matrix=h2h_matrix, round_details=round_details, player_name_map=player_name_map) @bp.route('//raw') def raw_json(match_id): match = StatsService.get_match_detail(match_id) if not match: return "Match not found", 404 # Construct a raw object from available raw fields data = { 'round_list': json.loads(match['round_list_raw']) if match['round_list_raw'] else None, 'leetify_data': json.loads(match['leetify_data_raw']) if match['leetify_data_raw'] else None } return Response(json.dumps(data, indent=2, ensure_ascii=False), mimetype='application/json')