HASH 59dff7423e3c
DATE 2025-08-28
SUBJECT nix: add player/team leaderboard scripts
FILES 3 CHANGED
HASH 59dff7423e3c
DATE 2025-08-28
SUBJECT nix: add player/team leaderboard
scripts
FILES 3 CHANGED
┌─ nix/apps/default.nix ─────────────────────────────────────────────────────┐
│ diff --git a/nix/apps/default.nix b/nix/apps/default.nix │
│ index e4b1e03..2cf3100 100644 │
│ --- a/nix/apps/default.nix │
│ +++ b/nix/apps/default.nix │
│ @@ -1,11 +1,6 @@ │
│ { │
│ lib, │
│ - classes, │
│ - encounter, │
│ - buffs, │
│ - debuffs, │
│ inputs, │
│ - trinket, │
│ api, │
│ simulation, │
│ ... │
│ @@ -15,23 +10,25 @@ │
│ getDB = pkgs.callPackage ./getDB.nix {inherit inputs;}; │
│ getCMLeaders = import ./challenge-mode-leaderboard.nix {inherit api writers p │
│ ython3Packages;}; │
│ parseCMs = import ./challenge-mode-parser.nix {inherit api writers python3Pac │
│ kages;}; │
│ + teamLeaderboards = import ./team-leaderboard-generator.nix {inherit writers p │
│ ython3Packages;}; │
│ + playerLeaderboards = import ./player-leaderboard-generator.nix {inherit write │
│ rs python3Packages;}; │
│ # Convert simulation data to apps │
│ simulationApps = │
│ - lib.mapAttrs (name: sim: { │
│ + lib.mapAttrs (_name: sim: { │
│ type = "app"; │
│ program = "${sim.script}/bin/${sim.metadata.output}-aggregator"; │
│ }) │
│ (simulation.generateMassSimulations pkgs); │
│ │
│ raceComparisonApps = │
│ - lib.mapAttrs (name: raceComp: { │
│ + lib.mapAttrs (_name: raceComp: { │
│ type = "app"; │
│ program = "${raceComp.script}/bin/${raceComp.metadata.output}-aggregator" │
│ ; │
│ }) │
│ (simulation.generateRaceComparisons pkgs); │
│ │
│ trinketComparisonApps = │
│ - lib.mapAttrs (name: trinketComp: { │
│ + lib.mapAttrs (_name: trinketComp: { │
│ type = "app"; │
│ program = "${trinketComp.script}/bin/${trinketComp.metadata.output}-aggre │
│ gator"; │
│ }) │
│ @@ -58,6 +55,14 @@ │
│ type = "app"; │
│ program = "${parseCMs}/bin/cm-leaderboard-parser"; │
│ }; │
│ + teamLeaderboards = { │
│ + type = "app"; │
│ + program = "${teamLeaderboards}/bin/team-leaderboard-generator"; │
│ + }; │
│ + playerLeaderboards = { │
│ + type = "app"; │
│ + program = "${playerLeaderboards}/bin/player-leaderboard-generator"; │
│ + }; │
│ testGroupSim = { │
│ type = "app"; │
│ program = "${simulation.generateTestGroupSim pkgs}/bin/test-group-sim"; │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ nix/apps/default.nix ───────────────┐
│ diff --git a/nix/apps/default.nix b/nix/apps │
│ /default.nix │
│ index e4b1e03..2cf3100 100644 │
│ --- a/nix/apps/default.nix │
│ +++ b/nix/apps/default.nix │
│ @@ -1,11 +1,6 @@ │
│ { │
│ lib, │
│ - classes, │
│ - encounter, │
│ - buffs, │
│ - debuffs, │
│ inputs, │
│ - trinket, │
│ api, │
│ simulation, │
│ ... │
│ @@ -15,23 +10,25 @@ │
│ getDB = pkgs.callPackage ./getDB.nix {i │
│ nherit inputs;}; │
│ getCMLeaders = import ./challenge-mode- │
│ leaderboard.nix {inherit api writers python3 │
│ Packages;}; │
│ parseCMs = import ./challenge-mode-pars │
│ er.nix {inherit api writers python3Packages; │
│ }; │
│ + teamLeaderboards = import ./team-leader │
│ board-generator.nix {inherit writers python3 │
│ Packages;}; │
│ + playerLeaderboards = import ./player-le │
│ aderboard-generator.nix {inherit writers pyt │
│ hon3Packages;}; │
│ # Convert simulation data to apps │
│ simulationApps = │
│ - lib.mapAttrs (name: sim: { │
│ + lib.mapAttrs (_name: sim: { │
│ type = "app"; │
│ program = "${sim.script}/bin/${sim. │
│ metadata.output}-aggregator"; │
│ }) │
│ (simulation.generateMassSimulations p │
│ kgs); │
│ │
│ raceComparisonApps = │
│ - lib.mapAttrs (name: raceComp: { │
│ + lib.mapAttrs (_name: raceComp: { │
│ type = "app"; │
│ program = "${raceComp.script}/bin/$ │
│ {raceComp.metadata.output}-aggregator"; │
│ }) │
│ (simulation.generateRaceComparisons p │
│ kgs); │
│ │
│ trinketComparisonApps = │
│ - lib.mapAttrs (name: trinketComp: { │
│ + lib.mapAttrs (_name: trinketComp: { │
│ type = "app"; │
│ program = "${trinketComp.script}/bi │
│ n/${trinketComp.metadata.output}-aggregator" │
│ ; │
│ }) │
│ @@ -58,6 +55,14 @@ │
│ type = "app"; │
│ program = "${parseCMs}/bin/cm-lea │
│ derboard-parser"; │
│ }; │
│ + teamLeaderboards = { │
│ + type = "app"; │
│ + program = "${teamLeaderboards}/bi │
│ n/team-leaderboard-generator"; │
│ + }; │
│ + playerLeaderboards = { │
│ + type = "app"; │
│ + program = "${playerLeaderboards}/ │
│ bin/player-leaderboard-generator"; │
│ + }; │
│ testGroupSim = { │
│ type = "app"; │
│ program = "${simulation.generateT │
│ estGroupSim pkgs}/bin/test-group-sim"; │
└──────────────────────────────────────────────┘
┌─ nix/apps/player-leaderboard-generator.nix ────────────────────────────────┐
│ diff --git a/nix/apps/player-leaderboard-generator.nix b/nix/apps/player-leaderboa │
│ rd-generator.nix │
│ new file mode 100644 │
│ index 0000000..a2f4a52 │
│ --- /dev/null │
│ +++ b/nix/apps/player-leaderboard-generator.nix │
│ @@ -0,0 +1,333 @@ │
│ +{ │
│ + writers, │
│ + python3Packages, │
│ + ... │
│ +}: let │
│ + playerLeaderboardScript = │
│ + writers.writePython3Bin "player-leaderboard-generator" { │
│ + libraries = [python3Packages.requests]; │
│ + doCheck = false; │
│ + } │
│ + '' │
│ + import os │
│ + import json │
│ + import glob │
│ + from pathlib import Path │
│ + from collections import defaultdict │
│ + import sys │
│ + │
│ + INPUT_ROOT = "./web/public/data/challenge-mode" │
│ + GLOBAL_LEADERBOARD_ROOT = "./web/public/data/leaderboards/global" │
│ + OUTPUT_ROOT = "./web/public/data/player-leaderboards" │
│ + TOP_N_PLAYERS = 250 │
│ + │
│ + def get_player_id(member): │
│ + # extract player ID from member data │
│ + return member.get("id") or member.get("profile", {}).get("id", 0) │
│ + │
│ + def get_player_name(member): │
│ + # extract player name from member data │
│ + return member.get("name") or member.get("profile", {}).get("name", "Unk │
│ nown") │
│ + │
│ + def get_player_realm(member): │
│ + # extract player realm from member data │
│ + return member.get("realm_slug") or member.get("profile", {}).get("realm │
│ ", {}).get("slug", "unknown") │
│ + │
│ + def load_global_rankings(): │
│ + # load global rankings for all dungeons │
│ + global_rankings = {} │
│ + │
│ + for dungeon_slug in ['gate-of-the-setting-sun', 'mogu-shan-palace', 'sc │
│ arlet-halls', │
│ + 'scarlet-monastery', 'scholomance', 'shado-pan-mon │
│ astery', │
│ + 'siege-of-niuzao-temple', 'stormstout-brewery', 't │
│ emple-of-the-jade-serpent']: │
│ + global_file = os.path.join(GLOBAL_LEADERBOARD_ROOT, dungeon_slug, ' │
│ leaderboard.json') │
│ + if os.path.exists(global_file): │
│ + try: │
│ + with open(global_file, 'r', encoding='utf-8') as f: │
│ + data = json.load(f) │
│ + global_rankings[dungeon_slug] = {} │
│ + │
│ + for run in data.get('leaderboard', []): │
│ + # create unique identifier for this run │
│ + duration = run['duration'] │
│ + timestamp = run['completed_timestamp'] │
│ + member_names = tuple(sorted([m['name'] for m in run │
│ ['members']])) │
│ + run_key = (duration, timestamp, member_names) │
│ + global_rankings[dungeon_slug][run_key] = run.get('r │
│ anking', 0) │
│ + except (json.JSONDecodeError, IOError) as e: │
│ + print(f"Warning: Could not load global rankings for {dungeo │
│ n_slug}: {e}") │
│ + │
│ + return global_rankings │
│ + │
│ + def lookup_global_ranking(run_data, global_rankings): │
│ + # look up the global ranking for a run │
│ + dungeon_slug = run_data['dungeon_slug'] │
│ + if dungeon_slug not in global_rankings: │
│ + return "~" │
│ + │
│ + duration = run_data['duration'] │
│ + timestamp = run_data['completed_timestamp'] │
│ + member_names = tuple(sorted(run_data['member_names'])) │
│ + run_key = (duration, timestamp, member_names) │
│ + │
│ + # return global ranking if found otherwise ~ │
│ + return global_rankings[dungeon_slug].get(run_key, "~") │
│ + │
│ + def analyze_players(): │
│ + # analyze all challenge mode data to identify individual player perform │
│ ance │
│ + print("Starting player analysis...") │
│ + │
│ + print("Loading global rankings...") │
│ + global_rankings = load_global_rankings() │
│ + │
│ + # track player runs: player_id -> dungeon_slug -> list of runs │
│ + player_runs = defaultdict(lambda: defaultdict(list)) │
│ + available_dungeons = set() │
│ + all_players_data = {} │
│ + │
│ + search_path = os.path.join(INPUT_ROOT, "**", "*.json") │
│ + leaderboard_files = glob.glob(search_path, recursive=True) │
│ + │
│ + if not leaderboard_files: │
│ + print(f"FATAL: No leaderboard JSON files found in {os.path.abspath( │
│ INPUT_ROOT)}", file=sys.stderr) │
│ + print("Please run the challenge mode parser first.", file=sys.stder │
│ r) │
│ + sys.exit(1) │
│ + │
│ + print(f"Found {len(leaderboard_files)} leaderboard files to analyze.") │
│ + │
│ + # first pass: collect all runs per player │
│ + for file_path in leaderboard_files: │
│ + path = Path(file_path) │
│ + parts = path.parts │
│ + try: │
│ + region = parts[-4] │
│ + realm_slug = parts[-3] │
│ + dungeon_slug = parts[-2] │
│ + except IndexError: │
│ + print(f"Warning: Could not parse path structure for {file_path} │
│ . Skipping.") │
│ + continue │
│ + │
│ + try: │
│ + with open(file_path, 'r', encoding='utf-8') as f: │
│ + data = json.load(f) │
│ + except (json.JSONDecodeError, IOError) as e: │
│ + print(f"Warning: Could not read or parse {file_path}. Skipping. │
│ Error: {e}") │
│ + continue │
│ + │
│ + # extract dungeon name and track available dungeons │
│ + map_name = data.get("map", {}).get("name", {}) │
│ + dungeon_name = map_name.get("en_US", dungeon_slug) if isinstance(ma │
│ p_name, dict) else map_name │
│ + available_dungeons.add(dungeon_slug) │
│ + │
│ + runs = data.get("leading_groups", []) │
│ + │
│ + for run in runs: │
│ + members = run.get("members", []) │
│ + if len(members) != 5: │
│ + continue │
│ + │
│ + # process each player in this run │
│ + for member in members: │
│ + player_id = get_player_id(member) │
│ + if player_id == 0: │
│ + continue │
│ + │
│ + # store player data │
│ + if player_id not in all_players_data: │
│ + all_players_data[player_id] = { │
│ + "name": get_player_name(member), │
│ + "realm_slug": get_player_realm(member), │
│ + "specs": [] │
│ + } │
│ + # add spec_id if it exists │
│ + spec_id = member.get("spec_id") │
│ + if spec_id and spec_id not in all_players_data[player_id][" │
│ specs"]: │
│ + all_players_data[player_id]["specs"].append(spec_id) │
│ + │
│ + # create run data for this player │
│ + run_data = { │
│ + "duration": run["duration"], │
│ + "completed_timestamp": run["completed_timestamp"], │
│ + "ranking": run.get("ranking", 0), │
│ + "region": region, │
│ + "realm_slug": realm_slug, │
│ + "dungeon_name": dungeon_name, │
│ + "dungeon_slug": dungeon_slug, │
│ + "members": members, │
│ + "member_names": [get_player_name(m) for m in members] │
│ + } │
│ + │
│ + # store this run for the player │
│ + player_runs[player_id][dungeon_slug].append(run_data) │
│ + │
│ + print(f"Identified {len(player_runs)} unique players.") │
│ + print(f"Available dungeons: {sorted(available_dungeons)}") │
│ + │
│ + # second pass: for each player find their best time per dungeon │
│ + print("Analyzing player performance across all dungeons...") │
│ + qualified_players = [] │
│ + players_analyzed = 0 │
│ + players_with_complete_coverage = 0 │
│ + │
│ + for player_id, dungeon_data in player_runs.items(): │
│ + players_analyzed += 1 │
│ + dungeons_completed = set(dungeon_data.keys()) │
│ + │
│ + # requirement: player must have runs in all available dungeons │
│ + if dungeons_completed != available_dungeons: │
│ + continue │
│ + │
│ + players_with_complete_coverage += 1 │
│ + │
│ + # find best run for each dungeon │
│ + best_runs_per_dungeon = {} │
│ + │
│ + for dungeon_slug, runs in dungeon_data.items(): │
│ + # deduplicate runs within this dungeon │
│ + unique_runs = [] │
│ + seen_dungeon_runs = set() │
│ + for run in runs: │
│ + member_names_sorted = tuple(sorted(run["member_names"])) │
│ + run_id = (run["duration"], run["completed_timestamp"], memb │
│ er_names_sorted) │
│ + if run_id not in seen_dungeon_runs: │
│ + seen_dungeon_runs.add(run_id) │
│ + unique_runs.append(run) │
│ + │
│ + # sort unique runs by duration to get best time for this player │
│ + sorted_runs = sorted(unique_runs, key=lambda x: x["duration"]) │
│ + best_run = sorted_runs[0] │
│ + │
│ + # look up global ranking for this run │
│ + global_ranking = lookup_global_ranking(best_run, global_ranking │
│ s) │
│ + │
│ + # store full member data with spec information │
│ + all_members_data = [] │
│ + for member in best_run["members"]: │
│ + member_name = get_player_name(member) │
│ + all_members_data.append({ │
│ + "name": member_name, │
│ + "realm_slug": get_player_realm(member), │
│ + "spec_id": member.get("spec_id"), │
│ + "id": get_player_id(member) │
│ + }) │
│ + │
│ + best_runs_per_dungeon[dungeon_slug] = { │
│ + "duration": best_run["duration"], │
│ + "dungeon_name": best_run["dungeon_name"], │
│ + "ranking": global_ranking, │
│ + "completed_timestamp": best_run["completed_timestamp"], │
│ + "region": best_run["region"], │
│ + "realm_slug": best_run["realm_slug"], │
│ + "team_members": [name for name in best_run["member_names"] │
│ if name != all_players_data[player_id]["name"]], │
│ + "all_members": all_members_data │
│ + } │
│ + │
│ + # calculate combined best time across all dungeons │
│ + combined_best_time = sum(run["duration"] for run in best_runs_per_d │
│ ungeon.values()) │
│ + │
│ + # calculate player statistics │
│ + regions_played = set() │
│ + total_runs = 0 │
│ + spec_frequency = {} │
│ + │
│ + for runs in dungeon_data.values(): │
│ + for run in runs: │
│ + regions_played.add(run["region"]) │
│ + total_runs += 1 │
│ + │
│ + player_info = all_players_data[player_id] │
│ + │
│ + # count spec frequency based on best runs per dungeon only │
│ + for dungeon_slug, best_run_data in best_runs_per_dungeon.items(): │
│ + # find this players spec in the all_members data for this best │
│ run │
│ + for member in best_run_data.get("all_members", []): │
│ + if member["name"] == player_info["name"]: │
│ + spec_id = member.get("spec_id") │
│ + if spec_id: │
│ + spec_frequency[spec_id] = spec_frequency.get(spec_i │
│ d, 0) + 1 │
│ + break │
│ + │
│ + # determine most played spec based on best runs only │
│ + most_played_spec = max(spec_frequency.items(), key=lambda x: x[1])[ │
│ 0] if spec_frequency else None │
│ + │
│ + qualified_players.append({ │
│ + "player_id": player_id, │
│ + "name": player_info["name"], │
│ + "realm_slug": player_info["realm_slug"], │
│ + "main_spec_id": most_played_spec, │
│ + "dungeons_completed": len(dungeons_completed), │
│ + "total_runs": total_runs, │
│ + "combined_best_time": combined_best_time, │
│ + "average_best_time": combined_best_time / len(dungeons_complete │
│ d), │
│ + "regions_played": list(regions_played), │
│ + "best_runs_per_dungeon": best_runs_per_dungeon │
│ + }) │
│ + │
│ + print(f"Analysis results:") │
│ + print(f" Players analyzed: {players_analyzed}") │
│ + print(f" Players with complete coverage: {players_with_complete_covera │
│ ge}") │
│ + print(f" Final qualifying players: {len(qualified_players)}") │
│ + │
│ + return qualified_players │
│ + │
│ + def generate_player_leaderboard(players): │
│ + # generate player leaderboard file sorted by combined best time │
│ + if not players: │
│ + print("No qualifying players found.") │
│ + return │
│ + │
│ + print(f"Generating player leaderboard...") │
│ + os.makedirs(OUTPUT_ROOT, exist_ok=True) │
│ + │
│ + # sort by combined best times across all dungeons │
│ + players_by_combined = sorted(players, key=lambda x: x["combined_best_ti │
│ me"])[:TOP_N_PLAYERS] │
│ + │
│ + # add rankings │
│ + for i, player in enumerate(players_by_combined): │
│ + player["ranking"] = i + 1 │
│ + │
│ + output_file = os.path.join(OUTPUT_ROOT, "best-overall.json") │
│ + with open(output_file, 'w', encoding='utf-8') as f: │
│ + json.dump({ │
│ + "title": "Best Players Overall", │
│ + "description": "Individual players ranked by their combined bes │
│ t times across all 9 dungeons (complete coverage required)", │
│ + "generated_timestamp": int(__import__('time').time() * 1000), │
│ + "total_players": len(players), │
│ + "leaderboard": players_by_combined │
│ + }, f, separators=(',', ':')) │
│ + │
│ + print(f" Generated best-overall.json with {len(players_by_combined)} p │
│ layers") │
│ + │
│ + # generate summary statistics │
│ + summary_file = os.path.join(OUTPUT_ROOT, "summary.json") │
│ + with open(summary_file, 'w', encoding='utf-8') as f: │
│ + json.dump({ │
│ + "total_players_analyzed": len(players), │
│ + "players_with_complete_coverage": len(players), │
│ + "total_runs_processed": sum(p["total_runs"] for p in players), │
│ + "average_runs_per_player": sum(p["total_runs"] for p in players │
│ ) / len(players) if players else 0, │
│ + "most_active_player_runs": max(p["total_runs"] for p in players │
│ ) if players else 0, │
│ + "generated_timestamp": int(__import__('time').time() * 1000) │
│ + }, f, separators=(',', ':')) │
│ + │
│ + def main(): │
│ + print("=== WoW Challenge Mode Player Leaderboard Generator ===") │
│ + print(f"Top players per leaderboard: {TOP_N_PLAYERS}") │
│ + print() │
│ + │
│ + # analyze players from challenge mode data │
│ + players = analyze_players() │
│ + │
│ + # generate leaderboard file │
│ + generate_player_leaderboard(players) │
│ + │
│ + print(f"\nPlayer leaderboard generated in: {os.path.abspath(OUTPUT_ROOT │
│ )}") │
│ + print("Available leaderboard:") │
│ + print(" - best-overall.json: Players by combined best time across all │
│ dungeons") │
│ + print(" - summary.json: Analysis statistics") │
│ + │
│ + if __name__ == "__main__": │
│ + main() │
│ + ''; │
│ +in │
│ + playerLeaderboardScript │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...yer-leaderboard-generator.nix ───┐
│ diff --git a/nix/apps/player-leaderboard-gen │
│ erator.nix b/nix/apps/player-leaderboard-gen │
│ erator.nix │
│ new file mode 100644 │
│ index 0000000..a2f4a52 │
│ --- /dev/null │
│ +++ b/nix/apps/player-leaderboard-generator. │
│ nix │
│ @@ -0,0 +1,333 @@ │
│ +{ │
│ + writers, │
│ + python3Packages, │
│ + ... │
│ +}: let │
│ + playerLeaderboardScript = │
│ + writers.writePython3Bin "player-leaderb │
│ oard-generator" { │
│ + libraries = [python3Packages.requests │
│ ]; │
│ + doCheck = false; │
│ + } │
│ + '' │
│ + import os │
│ + import json │
│ + import glob │
│ + from pathlib import Path │
│ + from collections import defaultdict │
│ + import sys │
│ + │
│ + INPUT_ROOT = "./web/public/data/chall │
│ enge-mode" │
│ + GLOBAL_LEADERBOARD_ROOT = "./web/publ │
│ ic/data/leaderboards/global" │
│ + OUTPUT_ROOT = "./web/public/data/play │
│ er-leaderboards" │
│ + TOP_N_PLAYERS = 250 │
│ + │
│ + def get_player_id(member): │
│ + # extract player ID from member d │
│ ata │
│ + return member.get("id") or member │
│ .get("profile", {}).get("id", 0) │
│ + │
│ + def get_player_name(member): │
│ + # extract player name from member │
│ data │
│ + return member.get("name") or memb │
│ er.get("profile", {}).get("name", "Unknown") │
│ + │
│ + def get_player_realm(member): │
│ + # extract player realm from membe │
│ r data │
│ + return member.get("realm_slug") o │
│ r member.get("profile", {}).get("realm", {}) │
│ .get("slug", "unknown") │
│ + │
│ + def load_global_rankings(): │
│ + # load global rankings for all du │
│ ngeons │
│ + global_rankings = {} │
│ + │
│ + for dungeon_slug in ['gate-of-the │
│ -setting-sun', 'mogu-shan-palace', 'scarlet- │
│ halls', │
│ + 'scarlet-mon │
│ astery', 'scholomance', 'shado-pan-monastery │
│ ', │
│ + 'siege-of-ni │
│ uzao-temple', 'stormstout-brewery', 'temple- │
│ of-the-jade-serpent']: │
│ + global_file = os.path.join(GL │
│ OBAL_LEADERBOARD_ROOT, dungeon_slug, 'leader │
│ board.json') │
│ + if os.path.exists(global_file │
│ ): │
│ + try: │
│ + with open(global_file │
│ , 'r', encoding='utf-8') as f: │
│ + data = json.load( │
│ f) │
│ + global_rankings[d │
│ ungeon_slug] = {} │
│ + │
│ + for run in data.g │
│ et('leaderboard', []): │
│ + # create uniq │
│ ue identifier for this run │
│ + duration = ru │
│ n['duration'] │
│ + timestamp = r │
│ un['completed_timestamp'] │
│ + member_names │
│ = tuple(sorted([m['name'] for m in run['memb │
│ ers']])) │
│ + run_key = (du │
│ ration, timestamp, member_names) │
│ + global_rankin │
│ gs[dungeon_slug][run_key] = run.get('ranking │
│ ', 0) │
│ + except (json.JSONDecodeEr │
│ ror, IOError) as e: │
│ + print(f"Warning: Coul │
│ d not load global rankings for {dungeon_slug │
│ }: {e}") │
│ + │
│ + return global_rankings │
│ + │
│ + def lookup_global_ranking(run_data, g │
│ lobal_rankings): │
│ + # look up the global ranking for │
│ a run │
│ + dungeon_slug = run_data['dungeon_ │
│ slug'] │
│ + if dungeon_slug not in global_ran │
│ kings: │
│ + return "~" │
│ + │
│ + duration = run_data['duration'] │
│ + timestamp = run_data['completed_t │
│ imestamp'] │
│ + member_names = tuple(sorted(run_d │
│ ata['member_names'])) │
│ + run_key = (duration, timestamp, m │
│ ember_names) │
│ + │
│ + # return global ranking if found │
│ otherwise ~ │
│ + return global_rankings[dungeon_sl │
│ ug].get(run_key, "~") │
│ + │
│ + def analyze_players(): │
│ + # analyze all challenge mode data │
│ to identify individual player performance │
│ + print("Starting player analysis.. │
│ .") │
│ + │
│ + print("Loading global rankings... │
│ ") │
│ + global_rankings = load_global_ran │
│ kings() │
│ + │
│ + # track player runs: player_id -> │
│ dungeon_slug -> list of runs │
│ + player_runs = defaultdict(lambda: │
│ defaultdict(list)) │
│ + available_dungeons = set() │
│ + all_players_data = {} │
│ + │
│ + search_path = os.path.join(INPUT_ │
│ ROOT, "**", "*.json") │
│ + leaderboard_files = glob.glob(sea │
│ rch_path, recursive=True) │
│ + │
│ + if not leaderboard_files: │
│ + print(f"FATAL: No leaderboard │
│ JSON files found in {os.path.abspath(INPUT_ │
│ ROOT)}", file=sys.stderr) │
│ + print("Please run the challen │
│ ge mode parser first.", file=sys.stderr) │
│ + sys.exit(1) │
│ + │
│ + print(f"Found {len(leaderboard_fi │
│ les)} leaderboard files to analyze.") │
│ + │
│ + # first pass: collect all runs pe │
│ r player │
│ + for file_path in leaderboard_file │
│ s: │
│ + path = Path(file_path) │
│ + parts = path.parts │
│ + try: │
│ + region = parts[-4] │
│ + realm_slug = parts[-3] │
│ + dungeon_slug = parts[-2] │
│ + except IndexError: │
│ + print(f"Warning: Could no │
│ t parse path structure for {file_path}. Skip │
│ ping.") │
│ + continue │
│ + │
│ + try: │
│ + with open(file_path, 'r', │
│ encoding='utf-8') as f: │
│ + data = json.load(f) │
│ + except (json.JSONDecodeError, │
│ IOError) as e: │
│ + print(f"Warning: Could no │
│ t read or parse {file_path}. Skipping. Error │
│ : {e}") │
│ + continue │
│ + │
│ + # extract dungeon name and tr │
│ ack available dungeons │
│ + map_name = data.get("map", {} │
│ ).get("name", {}) │
│ + dungeon_name = map_name.get(" │
│ en_US", dungeon_slug) if isinstance(map_name │
│ , dict) else map_name │
│ + available_dungeons.add(dungeo │
│ n_slug) │
│ + │
│ + runs = data.get("leading_grou │
│ ps", []) │
│ + │
│ + for run in runs: │
│ + members = run.get("member │
│ s", []) │
│ + if len(members) != 5: │
│ + continue │
│ + │
│ + # process each player in │
│ this run │
│ + for member in members: │
│ + player_id = get_playe │
│ r_id(member) │
│ + if player_id == 0: │
│ + continue │
│ + │
│ + # store player data │
│ + if player_id not in a │
│ ll_players_data: │
│ + all_players_data[ │
│ player_id] = { │
│ + "name": get_p │
│ layer_name(member), │
│ + "realm_slug": │
│ get_player_realm(member), │
│ + "specs": [] │
│ + } │
│ + # add spec_id if it e │
│ xists │
│ + spec_id = member.get( │
│ "spec_id") │
│ + if spec_id and spec_i │
│ d not in all_players_data[player_id]["specs" │
│ ]: │
│ + all_players_data[ │
│ player_id]["specs"].append(spec_id) │
│ + │
│ + # create run data for │
│ this player │
│ + run_data = { │
│ + "duration": run[" │
│ duration"], │
│ + "completed_timest │
│ amp": run["completed_timestamp"], │
│ + "ranking": run.ge │
│ t("ranking", 0), │
│ + "region": region, │
│ + "realm_slug": rea │
│ lm_slug, │
│ + "dungeon_name": d │
│ ungeon_name, │
│ + "dungeon_slug": d │
│ ungeon_slug, │
│ + "members": member │
│ s, │
│ + "member_names": [ │
│ get_player_name(m) for m in members] │
│ + } │
│ + │
│ + # store this run for │
│ the player │
│ + player_runs[player_id │
│ ][dungeon_slug].append(run_data) │
│ + │
│ + print(f"Identified {len(player_ru │
│ ns)} unique players.") │
│ + print(f"Available dungeons: {sort │
│ ed(available_dungeons)}") │
│ + │
│ + # second pass: for each player fi │
│ nd their best time per dungeon │
│ + print("Analyzing player performan │
│ ce across all dungeons...") │
│ + qualified_players = [] │
│ + players_analyzed = 0 │
│ + players_with_complete_coverage = │
│ 0 │
│ + │
│ + for player_id, dungeon_data in pl │
│ ayer_runs.items(): │
│ + players_analyzed += 1 │
│ + dungeons_completed = set(dung │
│ eon_data.keys()) │
│ + │
│ + # requirement: player must ha │
│ ve runs in all available dungeons │
│ + if dungeons_completed != avai │
│ lable_dungeons: │
│ + continue │
│ + │
│ + players_with_complete_coverag │
│ e += 1 │
│ + │
│ + # find best run for each dung │
│ eon │
│ + best_runs_per_dungeon = {} │
│ + │
│ + for dungeon_slug, runs in dun │
│ geon_data.items(): │
│ + # deduplicate runs within │
│ this dungeon │
│ + unique_runs = [] │
│ + seen_dungeon_runs = set() │
│ + for run in runs: │
│ + member_names_sorted = │
│ tuple(sorted(run["member_names"])) │
│ + run_id = (run["durati │
│ on"], run["completed_timestamp"], member_nam │
│ es_sorted) │
│ + if run_id not in seen │
│ _dungeon_runs: │
│ + seen_dungeon_runs │
│ .add(run_id) │
│ + unique_runs.appen │
│ d(run) │
│ + │
│ + # sort unique runs by dur │
│ ation to get best time for this player │
│ + sorted_runs = sorted(uniq │
│ ue_runs, key=lambda x: x["duration"]) │
│ + best_run = sorted_runs[0] │
│ + │
│ + # look up global ranking │
│ for this run │
│ + global_ranking = lookup_g │
│ lobal_ranking(best_run, global_rankings) │
│ + │
│ + # store full member data │
│ with spec information │
│ + all_members_data = [] │
│ + for member in best_run["m │
│ embers"]: │
│ + member_name = get_pla │
│ yer_name(member) │
│ + all_members_data.appe │
│ nd({ │
│ + "name": member_na │
│ me, │
│ + "realm_slug": get │
│ _player_realm(member), │
│ + "spec_id": member │
│ .get("spec_id"), │
│ + "id": get_player_ │
│ id(member) │
│ + }) │
│ + │
│ + best_runs_per_dungeon[dun │
│ geon_slug] = { │
│ + "duration": best_run[ │
│ "duration"], │
│ + "dungeon_name": best_ │
│ run["dungeon_name"], │
│ + "ranking": global_ran │
│ king, │
│ + "completed_timestamp" │
│ : best_run["completed_timestamp"], │
│ + "region": best_run["r │
│ egion"], │
│ + "realm_slug": best_ru │
│ n["realm_slug"], │
│ + "team_members": [name │
│ for name in best_run["member_names"] if nam │
│ e != all_players_data[player_id]["name"]], │
│ + "all_members": all_me │
│ mbers_data │
│ + } │
│ + │
│ + # calculate combined best tim │
│ e across all dungeons │
│ + combined_best_time = sum(run[ │
│ "duration"] for run in best_runs_per_dungeon │
│ .values()) │
│ + │
│ + # calculate player statistics │
│ + regions_played = set() │
│ + total_runs = 0 │
│ + spec_frequency = {} │
│ + │
│ + for runs in dungeon_data.valu │
│ es(): │
│ + for run in runs: │
│ + regions_played.add(ru │
│ n["region"]) │
│ + total_runs += 1 │
│ + │
│ + player_info = all_players_dat │
│ a[player_id] │
│ + │
│ + # count spec frequency based │
│ on best runs per dungeon only │
│ + for dungeon_slug, best_run_da │
│ ta in best_runs_per_dungeon.items(): │
│ + # find this players spec │
│ in the all_members data for this best run │
│ + for member in best_run_da │
│ ta.get("all_members", []): │
│ + if member["name"] == │
│ player_info["name"]: │
│ + spec_id = member. │
│ get("spec_id") │
│ + if spec_id: │
│ + spec_frequenc │
│ y[spec_id] = spec_frequency.get(spec_id, 0) │
│ + 1 │
│ + break │
│ + │
│ + # determine most played spec │
│ based on best runs only │
│ + most_played_spec = max(spec_f │
│ requency.items(), key=lambda x: x[1])[0] if │
│ spec_frequency else None │
│ + │
│ + qualified_players.append({ │
│ + "player_id": player_id, │
│ + "name": player_info["name │
│ "], │
│ + "realm_slug": player_info │
│ ["realm_slug"], │
│ + "main_spec_id": most_play │
│ ed_spec, │
│ + "dungeons_completed": len │
│ (dungeons_completed), │
│ + "total_runs": total_runs, │
│ + "combined_best_time": com │
│ bined_best_time, │
│ + "average_best_time": comb │
│ ined_best_time / len(dungeons_completed), │
│ + "regions_played": list(re │
│ gions_played), │
│ + "best_runs_per_dungeon": │
│ best_runs_per_dungeon │
│ + }) │
│ + │
│ + print(f"Analysis results:") │
│ + print(f" Players analyzed: {play │
│ ers_analyzed}") │
│ + print(f" Players with complete c │
│ overage: {players_with_complete_coverage}") │
│ + print(f" Final qualifying player │
│ s: {len(qualified_players)}") │
│ + │
│ + return qualified_players │
│ + │
│ + def generate_player_leaderboard(playe │
│ rs): │
│ + # generate player leaderboard fil │
│ e sorted by combined best time │
│ + if not players: │
│ + print("No qualifying players │
│ found.") │
│ + return │
│ + │
│ + print(f"Generating player leaderb │
│ oard...") │
│ + os.makedirs(OUTPUT_ROOT, exist_ok │
│ =True) │
│ + │
│ + # sort by combined best times acr │
│ oss all dungeons │
│ + players_by_combined = sorted(play │
│ ers, key=lambda x: x["combined_best_time"])[ │
│ :TOP_N_PLAYERS] │
│ + │
│ + # add rankings │
│ + for i, player in enumerate(player │
│ s_by_combined): │
│ + player["ranking"] = i + 1 │
│ + │
│ + output_file = os.path.join(OUTPUT │
│ _ROOT, "best-overall.json") │
│ + with open(output_file, 'w', encod │
│ ing='utf-8') as f: │
│ + json.dump({ │
│ + "title": "Best Players Ov │
│ erall", │
│ + "description": "Individua │
│ l players ranked by their combined best time │
│ s across all 9 dungeons (complete coverage r │
│ equired)", │
│ + "generated_timestamp": in │
│ t(__import__('time').time() * 1000), │
│ + "total_players": len(play │
│ ers), │
│ + "leaderboard": players_by │
│ _combined │
│ + }, f, separators=(',', ':')) │
│ + │
│ + print(f" Generated best-overall. │
│ json with {len(players_by_combined)} players │
│ ") │
│ + │
│ + # generate summary statistics │
│ + summary_file = os.path.join(OUTPU │
│ T_ROOT, "summary.json") │
│ + with open(summary_file, 'w', enco │
│ ding='utf-8') as f: │
│ + json.dump({ │
│ + "total_players_analyzed": │
│ len(players), │
│ + "players_with_complete_co │
│ verage": len(players), │
│ + "total_runs_processed": s │
│ um(p["total_runs"] for p in players), │
│ + "average_runs_per_player" │
│ : sum(p["total_runs"] for p in players) / le │
│ n(players) if players else 0, │
│ + "most_active_player_runs" │
│ : max(p["total_runs"] for p in players) if p │
│ layers else 0, │
│ + "generated_timestamp": in │
│ t(__import__('time').time() * 1000) │
│ + }, f, separators=(',', ':')) │
│ + │
│ + def main(): │
│ + print("=== WoW Challenge Mode Pla │
│ yer Leaderboard Generator ===") │
│ + print(f"Top players per leaderboa │
│ rd: {TOP_N_PLAYERS}") │
│ + print() │
│ + │
│ + # analyze players from challenge │
│ mode data │
│ + players = analyze_players() │
│ + │
│ + # generate leaderboard file │
│ + generate_player_leaderboard(playe │
│ rs) │
│ + │
│ + print(f"\nPlayer leaderboard gene │
│ rated in: {os.path.abspath(OUTPUT_ROOT)}") │
│ + print("Available leaderboard:") │
│ + print(" - best-overall.json: Pla │
│ yers by combined best time across all dungeo │
│ ns") │
│ + print(" - summary.json: Analysis │
│ statistics") │
│ + │
│ + if __name__ == "__main__": │
│ + main() │
│ + ''; │
│ +in │
│ + playerLeaderboardScript │
└──────────────────────────────────────────────┘
┌─ nix/apps/team-leaderboard-generator.nix ──────────────────────────────────┐
│ diff --git a/nix/apps/team-leaderboard-generator.nix b/nix/apps/team-leaderboard-g │
│ enerator.nix │
│ new file mode 100644 │
│ index 0000000..5de1d01 │
│ --- /dev/null │
│ +++ b/nix/apps/team-leaderboard-generator.nix │
│ @@ -0,0 +1,549 @@ │
│ +{ │
│ + writers, │
│ + python3Packages, │
│ + ... │
│ +}: let │
│ + teamLeaderboardScript = │
│ + writers.writePython3Bin "team-leaderboard-generator" { │
│ + libraries = [python3Packages.requests]; │
│ + doCheck = false; │
│ + } │
│ + '' │
│ + import os │
│ + import json │
│ + import glob │
│ + from pathlib import Path │
│ + from itertools import combinations │
│ + from collections import defaultdict │
│ + import sys │
│ + │
│ + INPUT_ROOT = "./web/public/data/challenge-mode" │
│ + GLOBAL_LEADERBOARD_ROOT = "./web/public/data/leaderboards/global" │
│ + OUTPUT_ROOT = "./web/public/data/team-leaderboards" │
│ + MIN_TEAM_RUNS = 2 │
│ + TOP_N_TEAMS = 50 │
│ + │
│ + def get_player_id(member): │
│ + # extract player ID from member data │
│ + return member.get("id") or member.get("profile", {}).get("id", 0) │
│ + │
│ + def get_player_name(member): │
│ + # extract player name from member data │
│ + return member.get("name") or member.get("profile", {}).get("name", "Unk │
│ nown") │
│ + │
│ + def get_player_realm(member): │
│ + # extract player realm from member data │
│ + return member.get("realm_slug") or member.get("profile", {}).get("realm │
│ ", {}).get("slug", "unknown") │
│ + │
│ + def generate_team_cores(members): │
│ + # generate all possible 3-player core combinations from a 5-player team │
│ + player_ids = [get_player_id(member) for member in members] │
│ + core_combinations = list(combinations(sorted(player_ids), 3)) │
│ + return core_combinations │
│ + │
│ + def create_team_signature(core_ids): │
│ + # create a unique signature for a 3-player core team │
│ + return "-".join(map(str, sorted(core_ids))) │
│ + │
│ + def load_global_rankings(): │
│ + # load global rankings for all dungeons │
│ + global_rankings = {} │
│ + │
│ + for dungeon_slug in ['gate-of-the-setting-sun', 'mogu-shan-palace', 'sc │
│ arlet-halls', │
│ + 'scarlet-monastery', 'scholomance', 'shado-pan-mon │
│ astery', │
│ + 'siege-of-niuzao-temple', 'stormstout-brewery', 't │
│ emple-of-the-jade-serpent']: │
│ + global_file = os.path.join(GLOBAL_LEADERBOARD_ROOT, dungeon_slug, ' │
│ leaderboard.json') │
│ + if os.path.exists(global_file): │
│ + try: │
│ + with open(global_file, 'r', encoding='utf-8') as f: │
│ + data = json.load(f) │
│ + global_rankings[dungeon_slug] = {} │
│ + │
│ + for run in data.get('leaderboard', []): │
│ + # create unique identifier for this run │
│ + duration = run['duration'] │
│ + timestamp = run['completed_timestamp'] │
│ + member_names = tuple(sorted([m['name'] for m in run │
│ ['members']])) │
│ + run_key = (duration, timestamp, member_names) │
│ + global_rankings[dungeon_slug][run_key] = run.get('r │
│ anking', 0) │
│ + except (json.JSONDecodeError, IOError) as e: │
│ + print(f"Warning: Could not load global rankings for {dungeo │
│ n_slug}: {e}") │
│ + │
│ + return global_rankings │
│ + │
│ + def lookup_global_ranking(run_data, global_rankings): │
│ + # look up the global ranking for a run │
│ + dungeon_slug = run_data['dungeon_slug'] │
│ + if dungeon_slug not in global_rankings: │
│ + return "~" │
│ + │
│ + duration = run_data['duration'] │
│ + timestamp = run_data['completed_timestamp'] │
│ + member_names = tuple(sorted(run_data['member_names'])) │
│ + run_key = (duration, timestamp, member_names) │
│ + │
│ + # return global ranking if found otherwise ~ │
│ + return global_rankings[dungeon_slug].get(run_key, "~") │
│ + │
│ + def get_team_info(core_ids, all_members_data): │
│ + # get team information for a core team │
│ + team_info = [] │
│ + for player_id in core_ids: │
│ + if player_id in all_members_data: │
│ + member_data = all_members_data[player_id] │
│ + team_member = { │
│ + "id": player_id, │
│ + "name": member_data["name"], │
│ + "realm_slug": member_data["realm_slug"] │
│ + } │
│ + # add spec info if available │
│ + if member_data.get("specs"): │
│ + team_member["spec_id"] = member_data["specs"][0] │
│ + team_info.append(team_member) │
│ + else: │
│ + team_info.append({ │
│ + "id": player_id, │
│ + "name": "Unknown", │
│ + "realm_slug": "unknown" │
│ + }) │
│ + return team_info │
│ + │
│ + def deduplicate_overlapping_teams(teams, all_members_data): │
│ + # merge teams that share most of their best times │
│ + remaining_teams = teams.copy() │
│ + deduplicated = [] │
│ + │
│ + while remaining_teams: │
│ + current_team = remaining_teams.pop(0) │
│ + current_best_times = current_team["best_runs_per_dungeon"] │
│ + │
│ + # Find teams that share most best times with current team │
│ + similar_teams = [current_team] │
│ + non_similar = [] │
│ + │
│ + for other_team in remaining_teams: │
│ + other_best_times = other_team["best_runs_per_dungeon"] │
│ + │
│ + # Count how many dungeons have identical best times │
│ + matching_dungeons = 0 │
│ + total_dungeons = min(len(current_best_times), len(other_best_ti │
│ mes)) │
│ + │
│ + for dungeon_slug in current_best_times: │
│ + if dungeon_slug in other_best_times: │
│ + if current_best_times[dungeon_slug]["duration"] == othe │
│ r_best_times[dungeon_slug]["duration"]: │
│ + matching_dungeons += 1 │
│ + │
│ + # If they share 6+ identical times out of 9 dungeons, consider │
│ them same team │
│ + match_percentage = matching_dungeons / total_dungeons if total_ │
│ dungeons > 0 else 0 │
│ + if matching_dungeons >= 6 and match_percentage >= 0.6: │
│ + similar_teams.append(other_team) │
│ + else: │
│ + non_similar.append(other_team) │
│ + │
│ + remaining_teams = non_similar │
│ + │
│ + if len(similar_teams) == 1: │
│ + # No similar teams found │
│ + deduplicated.append(current_team) │
│ + else: │
│ + # Merge similar teams (same underlying team with different 3-pl │
│ ayer core perspectives) │
│ + # Collect all members and data │
│ + all_core_members = set() │
│ + all_extended_members = set() │
│ + all_runs = [] │
│ + regions_played = set() │
│ + total_runs = 0 │
│ + best_combined_time = float('inf') │
│ + best_team_runs = None │
│ + │
│ + for team in similar_teams: │
│ + # Collect core members │
│ + core_ids = list(map(int, team["team_signature"].split("-")) │
│ ) │
│ + all_core_members.update(core_ids) │
│ + │
│ + # Collect extended roster │
│ + for member in team["extended_roster"]: │
│ + all_extended_members.add(member["name"] + "@" + member[ │
│ "realm_slug"]) │
│ + │
│ + # Collect runs and other data │
│ + all_runs.extend(team["all_runs"]) │
│ + regions_played.update(team["regions_played"]) │
│ + total_runs += team["total_runs"] │
│ + │
│ + # Use the best performance among similar teams │
│ + if team["combined_best_time"] < best_combined_time: │
│ + best_combined_time = team["combined_best_time"] │
│ + best_team_runs = team["best_runs_per_dungeon"] │
│ + │
│ + # Create merged team - limit core to 3 most consistent players │
│ across best runs │
│ + # Count participation in best runs across all similar teams │
│ + player_best_participation = defaultdict(int) │
│ + for team in similar_teams: │
│ + for run_data in team["best_runs_per_dungeon"].values(): │
│ + # Find actual run to count participation │
│ + for dungeon_runs in [team["all_runs"]]: # Use all_runs │
│ from team │
│ + for run in dungeon_runs: │
│ + if run["duration"] == run_data["duration"] and │
│ run["completed_timestamp"] == run_data["completed_timestamp"]: │
│ + for member_id in run["member_ids"]: │
│ + player_best_participation[member_id] += │
│ 1 │
│ + break │
│ + │
│ + # Select top 3 most consistent players as merged core │
│ + top_players = sorted(player_best_participation.items(), key=lam │
│ bda x: x[1], reverse=True)[:3] │
│ + merged_core_ids = sorted([player_id for player_id, count in top │
│ _players]) │
│ + merged_core_info = get_team_info(merged_core_ids, all_members_d │
│ ata) │
│ + │
│ + # Create extended roster from players who appear in the merged │
│ team's best runs │
│ + merged_extended_roster_ids = set() │
│ + for run_data in best_team_runs.values(): │
│ + # Find players who participated in this best run across all │
│ similar teams │
│ + for team in similar_teams: │
│ + for run in team["all_runs"]: │
│ + if run["duration"] == run_data["duration"] and run[ │
│ "completed_timestamp"] == run_data["completed_timestamp"]: │
│ + merged_extended_roster_ids.update(run["member_i │
│ ds"]) │
│ + break │
│ + │
│ + merged_extended_roster = [] │
│ + for member_id in sorted(merged_extended_roster_ids): │
│ + if member_id in all_members_data: │
│ + member_data = all_members_data[member_id] │
│ + roster_member = { │
│ + "name": member_data["name"], │
│ + "realm_slug": member_data["realm_slug"] │
│ + } │
│ + # Add spec info if available (use first spec if multipl │
│ e) │
│ + if member_data.get("specs"): │
│ + roster_member["spec_id"] = member_data["specs"][0] │
│ + merged_extended_roster.append(roster_member) │
│ + │
│ + merged_sig = create_team_signature(merged_core_ids) │
│ + │
│ + merged_team = { │
│ + "team_signature": merged_sig, │
│ + "core_members": merged_core_info, │
│ + "extended_roster": merged_extended_roster, │
│ + "dungeons_completed": similar_teams[0]["dungeons_completed" │
│ ], │
│ + "total_runs": total_runs // len(similar_teams), │
│ + "combined_best_time": best_combined_time, │
│ + "average_best_time": best_combined_time / similar_teams[0][ │
│ "dungeons_completed"], │
│ + "regions_played": list(regions_played), │
│ + "best_runs_per_dungeon": best_team_runs, │
│ + "all_runs": sorted(all_runs, key=lambda x: x["duration"]) │
│ + } │
│ + │
│ + deduplicated.append(merged_team) │
│ + │
│ + return deduplicated │
│ + │
│ + def analyze_teams(): │
│ + # analyze all challenge mode data to identify unique teams and their op │
│ timal 3-player cores │
│ + print("Starting team analysis...") │
│ + │
│ + print("Loading global rankings...") │
│ + global_rankings = load_global_rankings() │
│ + │
│ + # first pass: collect all runs and group by extended rosters │
│ + roster_runs = defaultdict(lambda: defaultdict(list)) │
│ + available_dungeons = set() │
│ + all_members_data = {} │
│ + │
│ + search_path = os.path.join(INPUT_ROOT, "**", "*.json") │
│ + leaderboard_files = glob.glob(search_path, recursive=True) │
│ + │
│ + if not leaderboard_files: │
│ + print(f"FATAL: No leaderboard JSON files found in {os.path.abspath( │
│ INPUT_ROOT)}", file=sys.stderr) │
│ + print("Please run the challenge mode parser first.", file=sys.stder │
│ r) │
│ + sys.exit(1) │
│ + │
│ + print(f"Found {len(leaderboard_files)} leaderboard files to analyze.") │
│ + │
│ + # First pass: collect all runs and identify unique extended rosters │
│ + extended_rosters = {} # roster_sig -> set of all player_ids who have r │
│ un together │
│ + │
│ + for file_path in leaderboard_files: │
│ + path = Path(file_path) │
│ + parts = path.parts │
│ + try: │
│ + region = parts[-4] │
│ + realm_slug = parts[-3] │
│ + dungeon_slug = parts[-2] │
│ + except IndexError: │
│ + print(f"Warning: Could not parse path structure for {file_path} │
│ . Skipping.") │
│ + continue │
│ + │
│ + try: │
│ + with open(file_path, 'r', encoding='utf-8') as f: │
│ + data = json.load(f) │
│ + except (json.JSONDecodeError, IOError) as e: │
│ + print(f"Warning: Could not read or parse {file_path}. Skipping. │
│ Error: {e}") │
│ + continue │
│ + │
│ + # Extract dungeon name and track available dungeons │
│ + map_name = data.get("map", {}).get("name", {}) │
│ + dungeon_name = map_name.get("en_US", dungeon_slug) if isinstance(ma │
│ p_name, dict) else map_name │
│ + available_dungeons.add(dungeon_slug) │
│ + │
│ + runs = data.get("leading_groups", []) │
│ + │
│ + for run in runs: │
│ + members = run.get("members", []) │
│ + if len(members) != 5: │
│ + continue │
│ + │
│ + # Store member data for later lookup (track all specs they've p │
│ layed) │
│ + for member in members: │
│ + player_id = get_player_id(member) │
│ + if player_id not in all_members_data: │
│ + all_members_data[player_id] = { │
│ + "name": get_player_name(member), │
│ + "realm_slug": get_player_realm(member), │
│ + "specs": [] │
│ + } │
│ + # Add spec_id if it exists │
│ + spec_id = member.get("spec_id") │
│ + if spec_id and spec_id not in all_members_data[player_id][" │
│ specs"]: │
│ + all_members_data[player_id]["specs"].append(spec_id) │
│ + │
│ + # Create run data │
│ + member_ids = [get_player_id(m) for m in members] │
│ + run_data = { │
│ + "duration": run["duration"], │
│ + "completed_timestamp": run["completed_timestamp"], │
│ + "ranking": run.get("ranking", 0), │
│ + "region": region, │
│ + "realm_slug": realm_slug, │
│ + "dungeon_name": dungeon_name, │
│ + "dungeon_slug": dungeon_slug, │
│ + "members": members, │
│ + "member_ids": member_ids, │
│ + "member_names": [get_player_name(m) for m in members] │
│ + } │
│ + │
│ + # Track extended rosters - players who run together consistentl │
│ y │
│ + # Group runs by overlapping player combinations to identify ext │
│ ended teams │
│ + found_roster = None │
│ + current_players = set(member_ids) │
│ + │
│ + # Check if this run matches any existing extended roster │
│ + for roster_sig, roster_players in extended_rosters.items(): │
│ + # If 3+ players overlap, consider this part of the same ext │
│ ended roster │
│ + overlap = len(current_players.intersection(roster_players)) │
│ + if overlap >= 3: │
│ + found_roster = roster_sig │
│ + # Add new players to the extended roster │
│ + extended_rosters[roster_sig].update(current_players) │
│ + break │
│ + │
│ + # If no matching roster found, create new one │
│ + if found_roster is None: │
│ + roster_sig = create_team_signature(sorted(member_ids)) │
│ + extended_rosters[roster_sig] = current_players.copy() │
│ + found_roster = roster_sig │
│ + │
│ + # Store run for this extended roster │
│ + roster_runs[found_roster][dungeon_slug].append(run_data) │
│ + │
│ + print(f"Identified {len(extended_rosters)} unique extended rosters.") │
│ + print(f"Available dungeons: {sorted(available_dungeons)}") │
│ + │
│ + # Second pass: For each extended roster, identify best 3-player core an │
│ d performance │
│ + print("Analyzing extended rosters to identify consistent 3-player cores │
│ ...") │
│ + qualified_teams = [] │
│ + rosters_analyzed = 0 │
│ + rosters_with_complete_coverage = 0 │
│ + │
│ + for roster_sig, dungeon_data in roster_runs.items(): │
│ + rosters_analyzed += 1 │
│ + dungeons_completed = set(dungeon_data.keys()) │
│ + │
│ + # REQUIREMENT: Roster must have runs in ALL available dungeons │
│ + if dungeons_completed != available_dungeons: │
│ + continue │
│ + │
│ + rosters_with_complete_coverage += 1 │
│ + │
│ + # Find best run for each dungeon and track player participation in │
│ best runs │
│ + best_runs_per_dungeon = {} │
│ + player_participation_in_best = defaultdict(int) │
│ + │
│ + for dungeon_slug, runs in dungeon_data.items(): │
│ + # Deduplicate runs within this dungeon (same run across differe │
│ nt realms) │
│ + unique_runs = [] │
│ + seen_dungeon_runs = set() │
│ + for run in runs: │
│ + member_names_sorted = tuple(sorted(run["member_names"])) │
│ + run_id = (run["duration"], run["completed_timestamp"], memb │
│ er_names_sorted) │
│ + if run_id not in seen_dungeon_runs: │
│ + seen_dungeon_runs.add(run_id) │
│ + unique_runs.append(run) │
│ + │
│ + # Sort unique runs by duration to get best time for this roster │
│ in this dungeon │
│ + sorted_runs = sorted(unique_runs, key=lambda x: x["duration"]) │
│ + best_run = sorted_runs[0] │
│ + │
│ + # Look up global ranking for this run │
│ + global_ranking = lookup_global_ranking(best_run, global_ranking │
│ s) │
│ + │
│ + best_runs_per_dungeon[dungeon_slug] = { │
│ + "duration": best_run["duration"], │
│ + "dungeon_name": best_run["dungeon_name"], │
│ + "ranking": global_ranking, # Use global ranking instead of │
│ realm ranking │
│ + "completed_timestamp": best_run["completed_timestamp"], │
│ + "region": best_run["region"], │
│ + "realm_slug": best_run["realm_slug"], │
│ + "members": best_run["member_names"] │
│ + } │
│ + │
│ + # Track which players appear in the best runs (for core identif │
│ ication) │
│ + for player_id in best_run["member_ids"]: │
│ + player_participation_in_best[player_id] += 1 │
│ + │
│ + # Identify the 3-player core: players who appear in the most best r │
│ uns │
│ + total_dungeons = len(dungeons_completed) │
│ + participation_sorted = sorted(player_participation_in_best.items(), │
│ key=lambda x: x[1], reverse=True) │
│ + │
│ + # Take top 3 players who appear in the most best runs as the core │
│ + if len(participation_sorted) >= 3: │
│ + core_ids = sorted([pid for pid, count in participation_sorted[: │
│ 3]]) │
│ + else: │
│ + continue # Not enough consistent players │
│ + │
│ + # Get extended roster (only players who appear in the best runs per │
│ dungeon) │
│ + extended_roster_ids = set() │
│ + for run_data in best_runs_per_dungeon.values(): │
│ + # Find the actual run to get member_ids │
│ + for dungeon_slug, runs in dungeon_data.items(): │
│ + for run in runs: │
│ + if run["duration"] == run_data["duration"] and run["com │
│ pleted_timestamp"] == run_data["completed_timestamp"]: │
│ + extended_roster_ids.update(run["member_ids"]) │
│ + break │
│ + │
│ + # REQUIREMENT: Roster must have minimum number of total runs │
│ + total_runs = sum(len(runs) for runs in dungeon_data.values()) │
│ + if total_runs < MIN_TEAM_RUNS: │
│ + continue │
│ + │
│ + # Create core team info │
│ + core_info = get_team_info(core_ids, all_members_data) │
│ + │
│ + # Create extended roster info (only players who contributed to best │
│ times) │
│ + extended_roster = [] │
│ + for player_id in sorted(extended_roster_ids): │
│ + if player_id in all_members_data: │
│ + member_data = all_members_data[player_id] │
│ + roster_member = { │
│ + "name": member_data["name"], │
│ + "realm_slug": member_data["realm_slug"] │
│ + } │
│ + # Add spec info if available (use first spec if multiple) │
│ + if member_data.get("specs"): │
│ + roster_member["spec_id"] = member_data["specs"][0] │
│ + extended_roster.append(roster_member) │
│ + │
│ + # Calculate combined best time across ALL dungeons │
│ + combined_best_time = sum(run["duration"] for run in best_runs_per_d │
│ ungeon.values()) │
│ + │
│ + # Calculate team statistics │
│ + regions_played = set() │
│ + all_team_runs = [] │
│ + seen_runs = set() # Track unique runs by (duration, timestamp) │
│ + for runs in dungeon_data.values(): │
│ + for run in runs: │
│ + regions_played.add(run["region"]) │
│ + # Create unique identifier for run deduplication (include m │
│ ember names for cross-realm duplicates) │
│ + member_names_sorted = tuple(sorted(run["member_names"])) │
│ + run_id = (run["duration"], run["completed_timestamp"], memb │
│ er_names_sorted) │
│ + if run_id not in seen_runs: │
│ + seen_runs.add(run_id) │
│ + all_team_runs.append(run) │
│ + │
│ + # Use core signature for uniqueness │
│ + core_sig = create_team_signature(core_ids) │
│ + │
│ + qualified_teams.append({ │
│ + "team_signature": core_sig, │
│ + "core_members": core_info, │
│ + "extended_roster": extended_roster, │
│ + "dungeons_completed": len(dungeons_completed), │
│ + "total_runs": total_runs, │
│ + "combined_best_time": combined_best_time, │
│ + "average_best_time": combined_best_time / len(dungeons_complete │
│ d), │
│ + "regions_played": list(regions_played), │
│ + "best_runs_per_dungeon": best_runs_per_dungeon, │
│ + "all_runs": sorted(all_team_runs, key=lambda x: x["duration"]) │
│ + }) │
│ + │
│ + print(f"Analysis results:") │
│ + print(f" Extended rosters analyzed: {rosters_analyzed}") │
│ + print(f" Rosters with complete coverage: {rosters_with_complete_covera │
│ ge}") │
│ + print(f" Final qualifying teams: {len(qualified_teams)}") │
│ + │
│ + return qualified_teams, all_members_data │
│ + │
│ + def generate_team_leaderboard(teams): │
│ + """Generate team leaderboard file sorted by combined best time""" │
│ + if not teams: │
│ + print("No qualifying teams found.") │
│ + return │
│ + │
│ + print(f"Generating team leaderboard...") │
│ + os.makedirs(OUTPUT_ROOT, exist_ok=True) │
│ + │
│ + # Sort by combined best times across ALL dungeons (primary ranking) │
│ + teams_by_combined = sorted(teams, key=lambda x: x["combined_best_time"] │
│ )[:TOP_N_TEAMS] │
│ + │
│ + # Add rankings │
│ + for i, team in enumerate(teams_by_combined): │
│ + team["ranking"] = i + 1 │
│ + │
│ + output_file = os.path.join(OUTPUT_ROOT, "best-overall.json") │
│ + with open(output_file, 'w', encoding='utf-8') as f: │
│ + json.dump({ │
│ + "title": "Best Teams Overall", │
│ + "description": "Teams ranked by their combined best times acros │
│ s all 9 dungeons (complete coverage required)", │
│ + "generated_timestamp": int(__import__('time').time() * 1000), │
│ + "total_teams": len(teams), │
│ + "min_runs_required": MIN_TEAM_RUNS, │
│ + "leaderboard": teams_by_combined │
│ + }, f, separators=(',', ':')) │
│ + │
│ + print(f" Generated best-overall.json with {len(teams_by_combined)} tea │
│ ms") │
│ + │
│ + # Generate summary statistics │
│ + summary_file = os.path.join(OUTPUT_ROOT, "summary.json") │
│ + with open(summary_file, 'w', encoding='utf-8') as f: │
│ + json.dump({ │
│ + "total_teams_analyzed": len(teams), │
│ + "teams_with_complete_coverage": len(teams), # All teams in the │
│ list have complete coverage │
│ + "total_runs_processed": sum(t["total_runs"] for t in teams) // │
│ 10, # Divide by 10 since each run creates 10 team combinations │
│ + "average_runs_per_team": sum(t["total_runs"] for t in teams) / │
│ len(teams) if teams else 0, │
│ + "most_active_team_runs": max(t["total_runs"] for t in teams) if │
│ teams else 0, │
│ + "generated_timestamp": int(__import__('time').time() * 1000) │
│ + }, f, separators=(',', ':')) │
│ + │
│ + def main(): │
│ + print("=== WoW Challenge Mode Team Leaderboard Generator ===") │
│ + print(f"Minimum runs required per team: {MIN_TEAM_RUNS}") │
│ + print(f"Top teams per leaderboard: {TOP_N_TEAMS}") │
│ + print() │
│ + │
│ + # Analyze teams from challenge mode data │
│ + teams, all_members_data = analyze_teams() │
│ + │
│ + # Deduplicate overlapping teams (same underlying team with different co │
│ re perspectives) │
│ + teams = deduplicate_overlapping_teams(teams, all_members_data) │
│ + print(f"After deduplication: {len(teams)} unique teams") │
│ + │
│ + # Generate leaderboard file │
│ + generate_team_leaderboard(teams) │
│ + │
│ + print(f"\nTeam leaderboard generated in: {os.path.abspath(OUTPUT_ROOT)} │
│ ") │
│ + print("Available files:") │
│ + print(" - best-overall.json: Teams by combined best time across all du │
│ ngeons") │
│ + print(" - summary.json: Analysis statistics") │
│ + │
│ + if __name__ == "__main__": │
│ + main() │
│ + ''; │
│ +in │
│ + teamLeaderboardScript │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...eam-leaderboard-generator.nix ───┐
│ diff --git a/nix/apps/team-leaderboard-gener │
│ ator.nix b/nix/apps/team-leaderboard-generat │
│ or.nix │
│ new file mode 100644 │
│ index 0000000..5de1d01 │
│ --- /dev/null │
│ +++ b/nix/apps/team-leaderboard-generator.ni │
│ x │
│ @@ -0,0 +1,549 @@ │
│ +{ │
│ + writers, │
│ + python3Packages, │
│ + ... │
│ +}: let │
│ + teamLeaderboardScript = │
│ + writers.writePython3Bin "team-leaderboa │
│ rd-generator" { │
│ + libraries = [python3Packages.requests │
│ ]; │
│ + doCheck = false; │
│ + } │
│ + '' │
│ + import os │
│ + import json │
│ + import glob │
│ + from pathlib import Path │
│ + from itertools import combinations │
│ + from collections import defaultdict │
│ + import sys │
│ + │
│ + INPUT_ROOT = "./web/public/data/chall │
│ enge-mode" │
│ + GLOBAL_LEADERBOARD_ROOT = "./web/publ │
│ ic/data/leaderboards/global" │
│ + OUTPUT_ROOT = "./web/public/data/team │
│ -leaderboards" │
│ + MIN_TEAM_RUNS = 2 │
│ + TOP_N_TEAMS = 50 │
│ + │
│ + def get_player_id(member): │
│ + # extract player ID from member d │
│ ata │
│ + return member.get("id") or member │
│ .get("profile", {}).get("id", 0) │
│ + │
│ + def get_player_name(member): │
│ + # extract player name from member │
│ data │
│ + return member.get("name") or memb │
│ er.get("profile", {}).get("name", "Unknown") │
│ + │
│ + def get_player_realm(member): │
│ + # extract player realm from membe │
│ r data │
│ + return member.get("realm_slug") o │
│ r member.get("profile", {}).get("realm", {}) │
│ .get("slug", "unknown") │
│ + │
│ + def generate_team_cores(members): │
│ + # generate all possible 3-player │
│ core combinations from a 5-player team │
│ + player_ids = [get_player_id(membe │
│ r) for member in members] │
│ + core_combinations = list(combinat │
│ ions(sorted(player_ids), 3)) │
│ + return core_combinations │
│ + │
│ + def create_team_signature(core_ids): │
│ + # create a unique signature for a │
│ 3-player core team │
│ + return "-".join(map(str, sorted(c │
│ ore_ids))) │
│ + │
│ + def load_global_rankings(): │
│ + # load global rankings for all du │
│ ngeons │
│ + global_rankings = {} │
│ + │
│ + for dungeon_slug in ['gate-of-the │
│ -setting-sun', 'mogu-shan-palace', 'scarlet- │
│ halls', │
│ + 'scarlet-mon │
│ astery', 'scholomance', 'shado-pan-monastery │
│ ', │
│ + 'siege-of-ni │
│ uzao-temple', 'stormstout-brewery', 'temple- │
│ of-the-jade-serpent']: │
│ + global_file = os.path.join(GL │
│ OBAL_LEADERBOARD_ROOT, dungeon_slug, 'leader │
│ board.json') │
│ + if os.path.exists(global_file │
│ ): │
│ + try: │
│ + with open(global_file │
│ , 'r', encoding='utf-8') as f: │
│ + data = json.load( │
│ f) │
│ + global_rankings[d │
│ ungeon_slug] = {} │
│ + │
│ + for run in data.g │
│ et('leaderboard', []): │
│ + # create uniq │
│ ue identifier for this run │
│ + duration = ru │
│ n['duration'] │
│ + timestamp = r │
│ un['completed_timestamp'] │
│ + member_names │
│ = tuple(sorted([m['name'] for m in run['memb │
│ ers']])) │
│ + run_key = (du │
│ ration, timestamp, member_names) │
│ + global_rankin │
│ gs[dungeon_slug][run_key] = run.get('ranking │
│ ', 0) │
│ + except (json.JSONDecodeEr │
│ ror, IOError) as e: │
│ + print(f"Warning: Coul │
│ d not load global rankings for {dungeon_slug │
│ }: {e}") │
│ + │
│ + return global_rankings │
│ + │
│ + def lookup_global_ranking(run_data, g │
│ lobal_rankings): │
│ + # look up the global ranking for │
│ a run │
│ + dungeon_slug = run_data['dungeon_ │
│ slug'] │
│ + if dungeon_slug not in global_ran │
│ kings: │
│ + return "~" │
│ + │
│ + duration = run_data['duration'] │
│ + timestamp = run_data['completed_t │
│ imestamp'] │
│ + member_names = tuple(sorted(run_d │
│ ata['member_names'])) │
│ + run_key = (duration, timestamp, m │
│ ember_names) │
│ + │
│ + # return global ranking if found │
│ otherwise ~ │
│ + return global_rankings[dungeon_sl │
│ ug].get(run_key, "~") │
│ + │
│ + def get_team_info(core_ids, all_membe │
│ rs_data): │
│ + # get team information for a core │
│ team │
│ + team_info = [] │
│ + for player_id in core_ids: │
│ + if player_id in all_members_d │
│ ata: │
│ + member_data = all_members │
│ _data[player_id] │
│ + team_member = { │
│ + "id": player_id, │
│ + "name": member_data[" │
│ name"], │
│ + "realm_slug": member_ │
│ data["realm_slug"] │
│ + } │
│ + # add spec info if availa │
│ ble │
│ + if member_data.get("specs │
│ "): │
│ + team_member["spec_id" │
│ ] = member_data["specs"][0] │
│ + team_info.append(team_mem │
│ ber) │
│ + else: │
│ + team_info.append({ │
│ + "id": player_id, │
│ + "name": "Unknown", │
│ + "realm_slug": "unknow │
│ n" │
│ + }) │
│ + return team_info │
│ + │
│ + def deduplicate_overlapping_teams(tea │
│ ms, all_members_data): │
│ + # merge teams that share most of │
│ their best times │
│ + remaining_teams = teams.copy() │
│ + deduplicated = [] │
│ + │
│ + while remaining_teams: │
│ + current_team = remaining_team │
│ s.pop(0) │
│ + current_best_times = current_ │
│ team["best_runs_per_dungeon"] │
│ + │
│ + # Find teams that share most │
│ best times with current team │
│ + similar_teams = [current_team │
│ ] │
│ + non_similar = [] │
│ + │
│ + for other_team in remaining_t │
│ eams: │
│ + other_best_times = other_ │
│ team["best_runs_per_dungeon"] │
│ + │
│ + # Count how many dungeons │
│ have identical best times │
│ + matching_dungeons = 0 │
│ + total_dungeons = min(len( │
│ current_best_times), len(other_best_times)) │
│ + │
│ + for dungeon_slug in curre │
│ nt_best_times: │
│ + if dungeon_slug in ot │
│ her_best_times: │
│ + if current_best_t │
│ imes[dungeon_slug]["duration"] == other_best │
│ _times[dungeon_slug]["duration"]: │
│ + matching_dung │
│ eons += 1 │
│ + │
│ + # If they share 6+ identi │
│ cal times out of 9 dungeons, consider them s │
│ ame team │
│ + match_percentage = matchi │
│ ng_dungeons / total_dungeons if total_dungeo │
│ ns > 0 else 0 │
│ + if matching_dungeons >= 6 │
│ and match_percentage >= 0.6: │
│ + similar_teams.append( │
│ other_team) │
│ + else: │
│ + non_similar.append(ot │
│ her_team) │
│ + │
│ + remaining_teams = non_similar │
│ + │
│ + if len(similar_teams) == 1: │
│ + # No similar teams found │
│ + deduplicated.append(curre │
│ nt_team) │
│ + else: │
│ + # Merge similar teams (sa │
│ me underlying team with different 3-player c │
│ ore perspectives) │
│ + # Collect all members and │
│ data │
│ + all_core_members = set() │
│ + all_extended_members = se │
│ t() │
│ + all_runs = [] │
│ + regions_played = set() │
│ + total_runs = 0 │
│ + best_combined_time = floa │
│ t('inf') │
│ + best_team_runs = None │
│ + │
│ + for team in similar_teams │
│ : │
│ + # Collect core member │
│ s │
│ + core_ids = list(map(i │
│ nt, team["team_signature"].split("-"))) │
│ + all_core_members.upda │
│ te(core_ids) │
│ + │
│ + # Collect extended ro │
│ ster │
│ + for member in team["e │
│ xtended_roster"]: │
│ + all_extended_memb │
│ ers.add(member["name"] + "@" + member["realm │
│ _slug"]) │
│ + │
│ + # Collect runs and ot │
│ her data │
│ + all_runs.extend(team[ │
│ "all_runs"]) │
│ + regions_played.update │
│ (team["regions_played"]) │
│ + total_runs += team["t │
│ otal_runs"] │
│ + │
│ + # Use the best perfor │
│ mance among similar teams │
│ + if team["combined_bes │
│ t_time"] < best_combined_time: │
│ + best_combined_tim │
│ e = team["combined_best_time"] │
│ + best_team_runs = │
│ team["best_runs_per_dungeon"] │
│ + │
│ + # Create merged team - li │
│ mit core to 3 most consistent players across │
│ best runs │
│ + # Count participation in │
│ best runs across all similar teams │
│ + player_best_participation │
│ = defaultdict(int) │
│ + for team in similar_teams │
│ : │
│ + for run_data in team[ │
│ "best_runs_per_dungeon"].values(): │
│ + # Find actual run │
│ to count participation │
│ + for dungeon_runs │
│ in [team["all_runs"]]: # Use all_runs from │
│ team │
│ + for run in du │
│ ngeon_runs: │
│ + if run["d │
│ uration"] == run_data["duration"] and run["c │
│ ompleted_timestamp"] == run_data["completed_ │
│ timestamp"]: │
│ + for m │
│ ember_id in run["member_ids"]: │
│ + p │
│ layer_best_participation[member_id] += 1 │
│ + break │
│ + │
│ + # Select top 3 most consi │
│ stent players as merged core │
│ + top_players = sorted(play │
│ er_best_participation.items(), key=lambda x: │
│ x[1], reverse=True)[:3] │
│ + merged_core_ids = sorted( │
│ [player_id for player_id, count in top_playe │
│ rs]) │
│ + merged_core_info = get_te │
│ am_info(merged_core_ids, all_members_data) │
│ + │
│ + # Create extended roster │
│ from players who appear in the merged team's │
│ best runs │
│ + merged_extended_roster_id │
│ s = set() │
│ + for run_data in best_team │
│ _runs.values(): │
│ + # Find players who pa │
│ rticipated in this best run across all simil │
│ ar teams │
│ + for team in similar_t │
│ eams: │
│ + for run in team[" │
│ all_runs"]: │
│ + if run["durat │
│ ion"] == run_data["duration"] and run["compl │
│ eted_timestamp"] == run_data["completed_time │
│ stamp"]: │
│ + merged_ex │
│ tended_roster_ids.update(run["member_ids"]) │
│ + break │
│ + │
│ + merged_extended_roster = │
│ [] │
│ + for member_id in sorted(m │
│ erged_extended_roster_ids): │
│ + if member_id in all_m │
│ embers_data: │
│ + member_data = all │
│ _members_data[member_id] │
│ + roster_member = { │
│ + "name": membe │
│ r_data["name"], │
│ + "realm_slug": │
│ member_data["realm_slug"] │
│ + } │
│ + # Add spec info i │
│ f available (use first spec if multiple) │
│ + if member_data.ge │
│ t("specs"): │
│ + roster_member │
│ ["spec_id"] = member_data["specs"][0] │
│ + merged_extended_r │
│ oster.append(roster_member) │
│ + │
│ + merged_sig = create_team_ │
│ signature(merged_core_ids) │
│ + │
│ + merged_team = { │
│ + "team_signature": mer │
│ ged_sig, │
│ + "core_members": merge │
│ d_core_info, │
│ + "extended_roster": me │
│ rged_extended_roster, │
│ + "dungeons_completed": │
│ similar_teams[0]["dungeons_completed"], │
│ + "total_runs": total_r │
│ uns // len(similar_teams), │
│ + "combined_best_time": │
│ best_combined_time, │
│ + "average_best_time": │
│ best_combined_time / similar_teams[0]["dunge │
│ ons_completed"], │
│ + "regions_played": lis │
│ t(regions_played), │
│ + "best_runs_per_dungeo │
│ n": best_team_runs, │
│ + "all_runs": sorted(al │
│ l_runs, key=lambda x: x["duration"]) │
│ + } │
│ + │
│ + deduplicated.append(merge │
│ d_team) │
│ + │
│ + return deduplicated │
│ + │
│ + def analyze_teams(): │
│ + # analyze all challenge mode data │
│ to identify unique teams and their optimal │
│ 3-player cores │
│ + print("Starting team analysis..." │
│ ) │
│ + │
│ + print("Loading global rankings... │
│ ") │
│ + global_rankings = load_global_ran │
│ kings() │
│ + │
│ + # first pass: collect all runs an │
│ d group by extended rosters │
│ + roster_runs = defaultdict(lambda: │
│ defaultdict(list)) │
│ + available_dungeons = set() │
│ + all_members_data = {} │
│ + │
│ + search_path = os.path.join(INPUT_ │
│ ROOT, "**", "*.json") │
│ + leaderboard_files = glob.glob(sea │
│ rch_path, recursive=True) │
│ + │
│ + if not leaderboard_files: │
│ + print(f"FATAL: No leaderboard │
│ JSON files found in {os.path.abspath(INPUT_ │
│ ROOT)}", file=sys.stderr) │
│ + print("Please run the challen │
│ ge mode parser first.", file=sys.stderr) │
│ + sys.exit(1) │
│ + │
│ + print(f"Found {len(leaderboard_fi │
│ les)} leaderboard files to analyze.") │
│ + │
│ + # First pass: collect all runs an │
│ d identify unique extended rosters │
│ + extended_rosters = {} # roster_s │
│ ig -> set of all player_ids who have run tog │
│ ether │
│ + │
│ + for file_path in leaderboard_file │
│ s: │
│ + path = Path(file_path) │
│ + parts = path.parts │
│ + try: │
│ + region = parts[-4] │
│ + realm_slug = parts[-3] │
│ + dungeon_slug = parts[-2] │
│ + except IndexError: │
│ + print(f"Warning: Could no │
│ t parse path structure for {file_path}. Skip │
│ ping.") │
│ + continue │
│ + │
│ + try: │
│ + with open(file_path, 'r', │
│ encoding='utf-8') as f: │
│ + data = json.load(f) │
│ + except (json.JSONDecodeError, │
│ IOError) as e: │
│ + print(f"Warning: Could no │
│ t read or parse {file_path}. Skipping. Error │
│ : {e}") │
│ + continue │
│ + │
│ + # Extract dungeon name and tr │
│ ack available dungeons │
│ + map_name = data.get("map", {} │
│ ).get("name", {}) │
│ + dungeon_name = map_name.get(" │
│ en_US", dungeon_slug) if isinstance(map_name │
│ , dict) else map_name │
│ + available_dungeons.add(dungeo │
│ n_slug) │
│ + │
│ + runs = data.get("leading_grou │
│ ps", []) │
│ + │
│ + for run in runs: │
│ + members = run.get("member │
│ s", []) │
│ + if len(members) != 5: │
│ + continue │
│ + │
│ + # Store member data for l │
│ ater lookup (track all specs they've played) │
│ + for member in members: │
│ + player_id = get_playe │
│ r_id(member) │
│ + if player_id not in a │
│ ll_members_data: │
│ + all_members_data[ │
│ player_id] = { │
│ + "name": get_p │
│ layer_name(member), │
│ + "realm_slug": │
│ get_player_realm(member), │
│ + "specs": [] │
│ + } │
│ + # Add spec_id if it e │
│ xists │
│ + spec_id = member.get( │
│ "spec_id") │
│ + if spec_id and spec_i │
│ d not in all_members_data[player_id]["specs" │
│ ]: │
│ + all_members_data[ │
│ player_id]["specs"].append(spec_id) │
│ + │
│ + # Create run data │
│ + member_ids = [get_player_ │
│ id(m) for m in members] │
│ + run_data = { │
│ + "duration": run["dura │
│ tion"], │
│ + "completed_timestamp" │
│ : run["completed_timestamp"], │
│ + "ranking": run.get("r │
│ anking", 0), │
│ + "region": region, │
│ + "realm_slug": realm_s │
│ lug, │
│ + "dungeon_name": dunge │
│ on_name, │
│ + "dungeon_slug": dunge │
│ on_slug, │
│ + "members": members, │
│ + "member_ids": member_ │
│ ids, │
│ + "member_names": [get_ │
│ player_name(m) for m in members] │
│ + } │
│ + │
│ + # Track extended rosters │
│ - players who run together consistently │
│ + # Group runs by overlappi │
│ ng player combinations to identify extended │
│ teams │
│ + found_roster = None │
│ + current_players = set(mem │
│ ber_ids) │
│ + │
│ + # Check if this run match │
│ es any existing extended roster │
│ + for roster_sig, roster_pl │
│ ayers in extended_rosters.items(): │
│ + # If 3+ players overl │
│ ap, consider this part of the same extended │
│ roster │
│ + overlap = len(current │
│ _players.intersection(roster_players)) │
│ + if overlap >= 3: │
│ + found_roster = ro │
│ ster_sig │
│ + # Add new players │
│ to the extended roster │
│ + extended_rosters[ │
│ roster_sig].update(current_players) │
│ + break │
│ + │
│ + # If no matching roster f │
│ ound, create new one │
│ + if found_roster is None: │
│ + roster_sig = create_t │
│ eam_signature(sorted(member_ids)) │
│ + extended_rosters[rost │
│ er_sig] = current_players.copy() │
│ + found_roster = roster │
│ _sig │
│ + │
│ + # Store run for this exte │
│ nded roster │
│ + roster_runs[found_roster] │
│ [dungeon_slug].append(run_data) │
│ + │
│ + print(f"Identified {len(extended_ │
│ rosters)} unique extended rosters.") │
│ + print(f"Available dungeons: {sort │
│ ed(available_dungeons)}") │
│ + │
│ + # Second pass: For each extended │
│ roster, identify best 3-player core and perf │
│ ormance │
│ + print("Analyzing extended rosters │
│ to identify consistent 3-player cores...") │
│ + qualified_teams = [] │
│ + rosters_analyzed = 0 │
│ + rosters_with_complete_coverage = │
│ 0 │
│ + │
│ + for roster_sig, dungeon_data in r │
│ oster_runs.items(): │
│ + rosters_analyzed += 1 │
│ + dungeons_completed = set(dung │
│ eon_data.keys()) │
│ + │
│ + # REQUIREMENT: Roster must ha │
│ ve runs in ALL available dungeons │
│ + if dungeons_completed != avai │
│ lable_dungeons: │
│ + continue │
│ + │
│ + rosters_with_complete_coverag │
│ e += 1 │
│ + │
│ + # Find best run for each dung │
│ eon and track player participation in best r │
│ uns │
│ + best_runs_per_dungeon = {} │
│ + player_participation_in_best │
│ = defaultdict(int) │
│ + │
│ + for dungeon_slug, runs in dun │
│ geon_data.items(): │
│ + # Deduplicate runs within │
│ this dungeon (same run across different rea │
│ lms) │
│ + unique_runs = [] │
│ + seen_dungeon_runs = set() │
│ + for run in runs: │
│ + member_names_sorted = │
│ tuple(sorted(run["member_names"])) │
│ + run_id = (run["durati │
│ on"], run["completed_timestamp"], member_nam │
│ es_sorted) │
│ + if run_id not in seen │
│ _dungeon_runs: │
│ + seen_dungeon_runs │
│ .add(run_id) │
│ + unique_runs.appen │
│ d(run) │
│ + │
│ + # Sort unique runs by dur │
│ ation to get best time for this roster in th │
│ is dungeon │
│ + sorted_runs = sorted(uniq │
│ ue_runs, key=lambda x: x["duration"]) │
│ + best_run = sorted_runs[0] │
│ + │
│ + # Look up global ranking │
│ for this run │
│ + global_ranking = lookup_g │
│ lobal_ranking(best_run, global_rankings) │
│ + │
│ + best_runs_per_dungeon[dun │
│ geon_slug] = { │
│ + "duration": best_run[ │
│ "duration"], │
│ + "dungeon_name": best_ │
│ run["dungeon_name"], │
│ + "ranking": global_ran │
│ king, # Use global ranking instead of realm │
│ ranking │
│ + "completed_timestamp" │
│ : best_run["completed_timestamp"], │
│ + "region": best_run["r │
│ egion"], │
│ + "realm_slug": best_ru │
│ n["realm_slug"], │
│ + "members": best_run[" │
│ member_names"] │
│ + } │
│ + │
│ + # Track which players app │
│ ear in the best runs (for core identificatio │
│ n) │
│ + for player_id in best_run │
│ ["member_ids"]: │
│ + player_participation_ │
│ in_best[player_id] += 1 │
│ + │
│ + # Identify the 3-player core: │
│ players who appear in the most best runs │
│ + total_dungeons = len(dungeons │
│ _completed) │
│ + participation_sorted = sorted │
│ (player_participation_in_best.items(), key=l │
│ ambda x: x[1], reverse=True) │
│ + │
│ + # Take top 3 players who appe │
│ ar in the most best runs as the core │
│ + if len(participation_sorted) │
│ >= 3: │
│ + core_ids = sorted([pid fo │
│ r pid, count in participation_sorted[:3]]) │
│ + else: │
│ + continue # Not enough co │
│ nsistent players │
│ + │
│ + # Get extended roster (only p │
│ layers who appear in the best runs per dunge │
│ on) │
│ + extended_roster_ids = set() │
│ + for run_data in best_runs_per │
│ _dungeon.values(): │
│ + # Find the actual run to │
│ get member_ids │
│ + for dungeon_slug, runs in │
│ dungeon_data.items(): │
│ + for run in runs: │
│ + if run["duration" │
│ ] == run_data["duration"] and run["completed │
│ _timestamp"] == run_data["completed_timestam │
│ p"]: │
│ + extended_rost │
│ er_ids.update(run["member_ids"]) │
│ + break │
│ + │
│ + # REQUIREMENT: Roster must ha │
│ ve minimum number of total runs │
│ + total_runs = sum(len(runs) fo │
│ r runs in dungeon_data.values()) │
│ + if total_runs < MIN_TEAM_RUNS │
│ : │
│ + continue │
│ + │
│ + # Create core team info │
│ + core_info = get_team_info(cor │
│ e_ids, all_members_data) │
│ + │
│ + # Create extended roster info │
│ (only players who contributed to best times │
│ ) │
│ + extended_roster = [] │
│ + for player_id in sorted(exten │
│ ded_roster_ids): │
│ + if player_id in all_membe │
│ rs_data: │
│ + member_data = all_mem │
│ bers_data[player_id] │
│ + roster_member = { │
│ + "name": member_da │
│ ta["name"], │
│ + "realm_slug": mem │
│ ber_data["realm_slug"] │
│ + } │
│ + # Add spec info if av │
│ ailable (use first spec if multiple) │
│ + if member_data.get("s │
│ pecs"): │
│ + roster_member["sp │
│ ec_id"] = member_data["specs"][0] │
│ + extended_roster.appen │
│ d(roster_member) │
│ + │
│ + # Calculate combined best tim │
│ e across ALL dungeons │
│ + combined_best_time = sum(run[ │
│ "duration"] for run in best_runs_per_dungeon │
│ .values()) │
│ + │
│ + # Calculate team statistics │
│ + regions_played = set() │
│ + all_team_runs = [] │
│ + seen_runs = set() # Track un │
│ ique runs by (duration, timestamp) │
│ + for runs in dungeon_data.valu │
│ es(): │
│ + for run in runs: │
│ + regions_played.add(ru │
│ n["region"]) │
│ + # Create unique ident │
│ ifier for run deduplication (include member │
│ names for cross-realm duplicates) │
│ + member_names_sorted = │
│ tuple(sorted(run["member_names"])) │
│ + run_id = (run["durati │
│ on"], run["completed_timestamp"], member_nam │
│ es_sorted) │
│ + if run_id not in seen │
│ _runs: │
│ + seen_runs.add(run │
│ _id) │
│ + all_team_runs.app │
│ end(run) │
│ + │
│ + # Use core signature for uniq │
│ ueness │
│ + core_sig = create_team_signat │
│ ure(core_ids) │
│ + │
│ + qualified_teams.append({ │
│ + "team_signature": core_si │
│ g, │
│ + "core_members": core_info │
│ , │
│ + "extended_roster": extend │
│ ed_roster, │
│ + "dungeons_completed": len │
│ (dungeons_completed), │
│ + "total_runs": total_runs, │
│ + "combined_best_time": com │
│ bined_best_time, │
│ + "average_best_time": comb │
│ ined_best_time / len(dungeons_completed), │
│ + "regions_played": list(re │
│ gions_played), │
│ + "best_runs_per_dungeon": │
│ best_runs_per_dungeon, │
│ + "all_runs": sorted(all_te │
│ am_runs, key=lambda x: x["duration"]) │
│ + }) │
│ + │
│ + print(f"Analysis results:") │
│ + print(f" Extended rosters analyz │
│ ed: {rosters_analyzed}") │
│ + print(f" Rosters with complete c │
│ overage: {rosters_with_complete_coverage}") │
│ + print(f" Final qualifying teams: │
│ {len(qualified_teams)}") │
│ + │
│ + return qualified_teams, all_membe │
│ rs_data │
│ + │
│ + def generate_team_leaderboard(teams): │
│ + """Generate team leaderboard file │
│ sorted by combined best time""" │
│ + if not teams: │
│ + print("No qualifying teams fo │
│ und.") │
│ + return │
│ + │
│ + print(f"Generating team leaderboa │
│ rd...") │
│ + os.makedirs(OUTPUT_ROOT, exist_ok │
│ =True) │
│ + │
│ + # Sort by combined best times acr │
│ oss ALL dungeons (primary ranking) │
│ + teams_by_combined = sorted(teams, │
│ key=lambda x: x["combined_best_time"])[:TOP │
│ _N_TEAMS] │
│ + │
│ + # Add rankings │
│ + for i, team in enumerate(teams_by │
│ _combined): │
│ + team["ranking"] = i + 1 │
│ + │
│ + output_file = os.path.join(OUTPUT │
│ _ROOT, "best-overall.json") │
│ + with open(output_file, 'w', encod │
│ ing='utf-8') as f: │
│ + json.dump({ │
│ + "title": "Best Teams Over │
│ all", │
│ + "description": "Teams ran │
│ ked by their combined best times across all │
│ 9 dungeons (complete coverage required)", │
│ + "generated_timestamp": in │
│ t(__import__('time').time() * 1000), │
│ + "total_teams": len(teams) │
│ , │
│ + "min_runs_required": MIN_ │
│ TEAM_RUNS, │
│ + "leaderboard": teams_by_c │
│ ombined │
│ + }, f, separators=(',', ':')) │
│ + │
│ + print(f" Generated best-overall. │
│ json with {len(teams_by_combined)} teams") │
│ + │
│ + # Generate summary statistics │
│ + summary_file = os.path.join(OUTPU │
│ T_ROOT, "summary.json") │
│ + with open(summary_file, 'w', enco │
│ ding='utf-8') as f: │
│ + json.dump({ │
│ + "total_teams_analyzed": l │
│ en(teams), │
│ + "teams_with_complete_cove │
│ rage": len(teams), # All teams in the list │
│ have complete coverage │
│ + "total_runs_processed": s │
│ um(t["total_runs"] for t in teams) // 10, # │
│ Divide by 10 since each run creates 10 team │
│ combinations │
│ + "average_runs_per_team": │
│ sum(t["total_runs"] for t in teams) / len(te │
│ ams) if teams else 0, │
│ + "most_active_team_runs": │
│ max(t["total_runs"] for t in teams) if teams │
│ else 0, │
│ + "generated_timestamp": in │
│ t(__import__('time').time() * 1000) │
│ + }, f, separators=(',', ':')) │
│ + │
│ + def main(): │
│ + print("=== WoW Challenge Mode Tea │
│ m Leaderboard Generator ===") │
│ + print(f"Minimum runs required per │
│ team: {MIN_TEAM_RUNS}") │
│ + print(f"Top teams per leaderboard │
│ : {TOP_N_TEAMS}") │
│ + print() │
│ + │
│ + # Analyze teams from challenge mo │
│ de data │
│ + teams, all_members_data = analyze │
│ _teams() │
│ + │
│ + # Deduplicate overlapping teams ( │
│ same underlying team with different core per │
│ spectives) │
│ + teams = deduplicate_overlapping_t │
│ eams(teams, all_members_data) │
│ + print(f"After deduplication: {len │
│ (teams)} unique teams") │
│ + │
│ + # Generate leaderboard file │
│ + generate_team_leaderboard(teams) │
│ + │
│ + print(f"\nTeam leaderboard genera │
│ ted in: {os.path.abspath(OUTPUT_ROOT)}") │
│ + print("Available files:") │
│ + print(" - best-overall.json: Tea │
│ ms by combined best time across all dungeons │
│ ") │
│ + print(" - summary.json: Analysis │
│ statistics") │
│ + │
│ + if __name__ == "__main__": │
│ + main() │
│ + ''; │
│ +in │
│ + teamLeaderboardScript │
└──────────────────────────────────────────────┘
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET