2026-04-08 08:13:02 +00:00
|
|
|
import os
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
2026-04-10 08:08:47 +00:00
|
|
|
CAREER_DB_PATH = os.path.join(BASE_DIR, "final_db.json")
|
|
|
|
|
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# Load DB
|
|
|
|
|
# ---------------------------------------------------------
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
def load_career_db():
|
|
|
|
|
if not os.path.exists(CAREER_DB_PATH):
|
|
|
|
|
return None
|
|
|
|
|
with open(CAREER_DB_PATH, "r", encoding="utf-8") as f:
|
|
|
|
|
return json.load(f)
|
|
|
|
|
|
|
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# Career Rankings (Modern Tag System)
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
def top_players_by_tag(tag_name, limit=20):
|
|
|
|
|
"""
|
|
|
|
|
Returns top players sorted by percentile for a given tag.
|
|
|
|
|
tag_name must match the new tag keys:
|
|
|
|
|
- slayer
|
|
|
|
|
- sharpshooter
|
|
|
|
|
- objective_payload
|
|
|
|
|
- objective_domination
|
|
|
|
|
- consistency
|
|
|
|
|
- clutch
|
|
|
|
|
"""
|
|
|
|
|
|
2026-04-08 08:13:02 +00:00
|
|
|
db = load_career_db()
|
|
|
|
|
if db is None:
|
|
|
|
|
return []
|
|
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
players = db.get("players", {})
|
2026-04-08 08:13:02 +00:00
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
ranked = []
|
|
|
|
|
for pid, p in players.items():
|
|
|
|
|
tag = p.get(tag_name, {})
|
|
|
|
|
pct = tag.get("pct", 0)
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
ranked.append({
|
2026-04-10 08:08:47 +00:00
|
|
|
"id": pid,
|
2026-04-08 08:13:02 +00:00
|
|
|
"name": p.get("name", "Unknown"),
|
2026-04-10 08:08:47 +00:00
|
|
|
"team": p.get("team_history", ["Unknown"])[-1] if p.get("team_history") else "Unknown",
|
|
|
|
|
"pct": pct,
|
|
|
|
|
"tier": tag.get("tier", "D"),
|
|
|
|
|
"summary": tag.get("summary", ""),
|
|
|
|
|
"raw": tag.get("raw", 0.0),
|
2026-04-08 08:13:02 +00:00
|
|
|
})
|
|
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
ranked.sort(key=lambda x: x["pct"], reverse=True)
|
|
|
|
|
return ranked[:limit]
|
2026-04-08 08:13:02 +00:00
|
|
|
|
|
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
# ---------------------------------------------------------
|
2026-04-23 09:24:48 +00:00
|
|
|
# Match-Based Consistency Ranking (Modernized)
|
2026-04-10 08:08:47 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
def rank_match_players_consistency(players):
|
2026-04-08 08:13:02 +00:00
|
|
|
ranked = []
|
2026-04-10 08:08:47 +00:00
|
|
|
|
2026-04-08 08:13:02 +00:00
|
|
|
for p in players:
|
2026-04-10 08:08:47 +00:00
|
|
|
tag = p.get("consistency", {})
|
2026-04-23 09:24:48 +00:00
|
|
|
raw = tag.get("raw", 0.0)
|
|
|
|
|
pct = tag.get("pct", None)
|
|
|
|
|
tier = tag.get("tier", None)
|
2026-04-10 08:08:47 +00:00
|
|
|
|
2026-04-08 08:13:02 +00:00
|
|
|
ranked.append({
|
|
|
|
|
"name": p.get("name", "Unknown"),
|
|
|
|
|
"team": p.get("team", ""),
|
2026-04-23 09:24:48 +00:00
|
|
|
"score_raw": raw,
|
|
|
|
|
"score_display": f"{raw:.2f}",
|
|
|
|
|
"pct": pct,
|
|
|
|
|
"tier": tier,
|
2026-04-08 08:13:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ranked.sort(key=lambda x: x["score_raw"], reverse=True)
|
|
|
|
|
|
|
|
|
|
for i, r in enumerate(ranked, start=1):
|
|
|
|
|
r["rank"] = i
|
|
|
|
|
|
2026-04-09 10:11:39 +00:00
|
|
|
return ranked
|
|
|
|
|
|
|
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
# ---------------------------------------------------------
|
2026-04-23 09:24:48 +00:00
|
|
|
# Match-Based Clutch Ranking (Modernized)
|
2026-04-10 08:08:47 +00:00
|
|
|
# ---------------------------------------------------------
|
2026-04-09 10:11:39 +00:00
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
def rank_match_players_clutch(players):
|
2026-04-09 10:11:39 +00:00
|
|
|
ranked = []
|
2026-04-10 08:08:47 +00:00
|
|
|
|
|
|
|
|
for p in players:
|
|
|
|
|
tag = p.get("clutch", {})
|
2026-04-23 09:24:48 +00:00
|
|
|
raw = tag.get("raw", 0.0)
|
|
|
|
|
pct = tag.get("pct", None)
|
|
|
|
|
tier = tag.get("tier", None)
|
2026-04-09 10:11:39 +00:00
|
|
|
|
|
|
|
|
ranked.append({
|
2026-04-10 08:08:47 +00:00
|
|
|
"name": p.get("name", "Unknown"),
|
|
|
|
|
"team": p.get("team", ""),
|
2026-04-23 09:24:48 +00:00
|
|
|
"score_raw": raw,
|
|
|
|
|
"score_display": f"{raw:.2f}",
|
|
|
|
|
"pct": pct,
|
|
|
|
|
"tier": tier,
|
2026-04-09 10:11:39 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ranked.sort(key=lambda x: x["score_raw"], reverse=True)
|
|
|
|
|
|
2026-04-10 08:08:47 +00:00
|
|
|
for i, r in enumerate(ranked, start=1):
|
2026-04-09 10:11:39 +00:00
|
|
|
r["rank"] = i
|
|
|
|
|
|
2026-04-15 06:15:07 +00:00
|
|
|
return ranked
|
2026-04-23 09:24:48 +00:00
|
|
|
|
|
|
|
|
|
2026-04-21 08:55:26 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# Generic Match Tag Ranking (Modern Tag System)
|
2026-04-23 09:24:48 +00:00
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
|
2026-04-22 07:25:42 +00:00
|
|
|
def rank_match_players_by_tag(players, tag):
|
|
|
|
|
rows = []
|
2026-04-15 06:15:07 +00:00
|
|
|
|
|
|
|
|
for p in players:
|
2026-04-23 09:24:48 +00:00
|
|
|
tag_data = p.get(tag) or {}
|
|
|
|
|
raw = tag_data.get("raw", 0.0)
|
|
|
|
|
pct = tag_data.get("pct", None)
|
|
|
|
|
tier = tag_data.get("tier", None)
|
2026-04-15 06:15:07 +00:00
|
|
|
|
2026-04-22 07:25:42 +00:00
|
|
|
rows.append({
|
2026-04-15 06:15:07 +00:00
|
|
|
"name": p.get("name", "Unknown"),
|
|
|
|
|
"team": p.get("team", ""),
|
2026-04-22 07:25:42 +00:00
|
|
|
"score_raw": raw,
|
|
|
|
|
"score_display": f"{raw:.2f}",
|
2026-04-23 09:24:48 +00:00
|
|
|
"pct": pct,
|
|
|
|
|
"tier": tier,
|
2026-09-29 00:59:21 +00:00
|
|
|
# Prefer the tag's OWN explicit has_data flag (set by
|
|
|
|
|
# resolve_tag_career_first / the Consistency step in
|
|
|
|
|
# on_generate_slots) — those dicts are always non-empty even
|
|
|
|
|
# for a "no career data" fallback estimate, so bool(tag_data)
|
|
|
|
|
# alone can't tell the two cases apart.
|
|
|
|
|
"has_data": tag_data.get("has_data", bool(tag_data)),
|
2026-04-15 06:15:07 +00:00
|
|
|
})
|
|
|
|
|
|
2026-04-22 07:25:42 +00:00
|
|
|
rows.sort(key=lambda x: x["score_raw"], reverse=True)
|
2026-04-15 06:15:07 +00:00
|
|
|
|
2026-04-22 07:25:42 +00:00
|
|
|
for i, r in enumerate(rows, start=1):
|
2026-04-21 08:55:26 +00:00
|
|
|
r["rank"] = i
|
2026-04-15 06:15:07 +00:00
|
|
|
|
2026-04-22 07:25:42 +00:00
|
|
|
return rows
|
2026-04-23 09:24:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
# Split into two columns for UI
|
|
|
|
|
# ---------------------------------------------------------
|
|
|
|
|
|
2026-04-22 07:25:42 +00:00
|
|
|
def split_two_columns(ranked, team_a, team_b):
|
|
|
|
|
left = [r for r in ranked if r.get("team") == team_a]
|
|
|
|
|
right = [r for r in ranked if r.get("team") == team_b]
|
2026-04-23 09:24:48 +00:00
|
|
|
return left, right
|