2026-04-08 08:13:02 +00:00
|
|
|
import os
|
|
|
|
|
import json
|
|
|
|
|
from collections import defaultdict
|
2026-04-09 10:11:39 +00:00
|
|
|
import data.db_access as db_access
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
from tags.slayer import compute_slayer_for_career_player
|
|
|
|
|
from tags.objective_payload import compute_payload_objective_for_career_player
|
|
|
|
|
from tags.sharpshooter import compute_sharpshooter_for_career_player
|
|
|
|
|
from tags.consistency import compute_consistency
|
2026-04-09 10:11:39 +00:00
|
|
|
from tags.clutch import compute_clutch
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_career_database(output_path):
|
|
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# 1. Load all players from the local SQLite DB
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
player_rows = db_access.query("SELECT PlayerUUID, PlayerGameName FROM players;")
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
if not player_rows:
|
|
|
|
|
print("No players found in local DB.")
|
|
|
|
|
return False
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
final_db = {"players": {}, "league_averages": {}}
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# 2. Build per-player career stats from local DB
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
for row in player_rows:
|
|
|
|
|
pid = row["PlayerUUID"]
|
|
|
|
|
name = row.get("PlayerGameName", "Unknown")
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# Pull all matches from SQLite
|
|
|
|
|
matches = db_access.get_player_match_history(pid)
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
if not matches:
|
|
|
|
|
# Skip players with no match history
|
2026-04-08 08:13:02 +00:00
|
|
|
continue
|
|
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# Aggregate raw career totals
|
|
|
|
|
career_raw = defaultdict(float)
|
|
|
|
|
|
|
|
|
|
for m in matches:
|
|
|
|
|
career_raw["kills"] += m["Kills"]
|
|
|
|
|
career_raw["deaths"] += m["Deaths"]
|
|
|
|
|
career_raw["damage"] += m["Damage"]
|
|
|
|
|
career_raw["shots"] += m["Shots"]
|
|
|
|
|
career_raw["shots_hit"] += m["ShotsHit"]
|
|
|
|
|
career_raw["headshots"] += m["Headshots"]
|
|
|
|
|
career_raw["PAY_PushTime"] += m["PAY_PushTime"]
|
|
|
|
|
career_raw["DOM_captures"] += m["DOM_Captures"]
|
|
|
|
|
career_raw["DOM_counters"] += m["DOM_Counters"]
|
|
|
|
|
career_raw["maps"] += 1 # each match = 1 map for now
|
|
|
|
|
|
|
|
|
|
# Derived stats
|
|
|
|
|
maps = max(1, career_raw["maps"])
|
|
|
|
|
KD = career_raw["kills"] / career_raw["deaths"] if career_raw["deaths"] > 0 else career_raw["kills"]
|
|
|
|
|
accuracy = (career_raw["shots_hit"] / career_raw["shots"]) if career_raw["shots"] > 0 else 0.0
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
derived = {
|
2026-04-09 10:11:39 +00:00
|
|
|
"kills_per_map": career_raw["kills"] / maps,
|
|
|
|
|
"deaths_per_map": career_raw["deaths"] / maps,
|
|
|
|
|
"push_time_per_season": career_raw["PAY_PushTime"], # no seasons now
|
2026-04-08 08:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final_db["players"][pid] = {
|
2026-04-09 10:11:39 +00:00
|
|
|
"name": name,
|
2026-04-08 08:13:02 +00:00
|
|
|
"career": {
|
2026-04-09 10:11:39 +00:00
|
|
|
"kills": career_raw["kills"],
|
|
|
|
|
"deaths": career_raw["deaths"],
|
2026-04-08 08:13:02 +00:00
|
|
|
"KD": KD,
|
|
|
|
|
"accuracy": accuracy,
|
2026-04-09 10:11:39 +00:00
|
|
|
"push_time": career_raw["PAY_PushTime"],
|
|
|
|
|
"captures": career_raw["DOM_captures"],
|
|
|
|
|
"counters": career_raw["DOM_counters"],
|
|
|
|
|
"maps": career_raw["maps"],
|
|
|
|
|
"damage": career_raw["damage"],
|
|
|
|
|
"shots": career_raw["shots"],
|
|
|
|
|
"shots_hit": career_raw["shots_hit"],
|
|
|
|
|
"headshots": career_raw["headshots"],
|
2026-04-08 08:13:02 +00:00
|
|
|
},
|
|
|
|
|
"derived": derived,
|
2026-04-09 10:11:39 +00:00
|
|
|
"matches": matches,
|
2026-04-08 08:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# 3. Compute league averages (local-only)
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
league_acc = defaultdict(float)
|
|
|
|
|
league_count = defaultdict(int)
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
for pid, pdata in final_db["players"].items():
|
|
|
|
|
c = pdata["career"]
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
league_acc["KD"] += c["KD"]
|
|
|
|
|
league_count["KD"] += 1
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
league_acc["accuracy"] += c["accuracy"]
|
|
|
|
|
league_count["accuracy"] += 1
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
league_acc["kills_per_map"] += pdata["derived"]["kills_per_map"]
|
|
|
|
|
league_count["kills_per_map"] += 1
|
|
|
|
|
|
|
|
|
|
league_acc["damage"] += c["damage"]
|
|
|
|
|
league_count["damage"] += 1
|
|
|
|
|
|
|
|
|
|
league_averages = {
|
|
|
|
|
key: league_acc[key] / max(1, league_count[key])
|
|
|
|
|
for key in league_acc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final_db["league_averages"] = league_averages
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# 4. Compute all tags
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
for pid, pdata in final_db["players"].items():
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
# Slayer
|
|
|
|
|
slayer_info = compute_slayer_for_career_player(pdata, league_averages)
|
|
|
|
|
pdata["slayer_score_raw"] = slayer_info["score_raw"]
|
|
|
|
|
pdata["slayer_score_display"] = slayer_info["score_display"]
|
|
|
|
|
pdata["slayer_strength"] = slayer_info["strength"]
|
|
|
|
|
|
|
|
|
|
# Sharpshooter
|
2026-04-09 10:11:39 +00:00
|
|
|
pdata["sharpshooter"] = compute_sharpshooter_for_career_player(pdata["career"])
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
# Payload Objective Specialist
|
2026-04-09 10:11:39 +00:00
|
|
|
pdata["objective_payload_components"] = compute_payload_objective_for_career_player(
|
|
|
|
|
pdata, league_averages
|
|
|
|
|
)
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# Consistency
|
|
|
|
|
matches = pdata["matches"]
|
2026-04-08 08:13:02 +00:00
|
|
|
consistency_raw, components = compute_consistency(matches)
|
|
|
|
|
pdata["consistency_components"] = {
|
|
|
|
|
**components,
|
|
|
|
|
"consistency_raw": consistency_raw,
|
2026-04-09 10:11:39 +00:00
|
|
|
"consistency_norm": components.get("consistency_norm", consistency_raw),
|
2026-04-08 08:13:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# Clutch
|
2026-04-08 08:13:02 +00:00
|
|
|
pdata["clutch_components"] = {
|
|
|
|
|
"clutch_norm": compute_clutch(pdata, league_averages)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# 5. Save DB
|
|
|
|
|
# ---------------------------------------------------------
|
2026-04-08 08:13:02 +00:00
|
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
|
|
|
json.dump(final_db, f, indent=2)
|
|
|
|
|
|
|
|
|
|
return True
|