2.0.0 Alpha: Data Refinery

This commit is contained in:
2026-08-08 21:31:56 +08:00
parent fa75081d4d
commit 562775e5db
48 changed files with 4172 additions and 661 deletions
+69 -34
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from typing import Any, Iterable
from typing import Any
from web.database import query_db
@@ -138,20 +138,47 @@ class FeatureService:
}
order_col = sort_map.get(sort_by, "core_avg_rating")
where = []
args: list[Any] = []
if search:
where.append("steam_id_64 IN (SELECT steam_id_64 FROM dim_players WHERE username LIKE ?)")
args.append(f"%{search}%")
where_sql = f"WHERE {' AND '.join(where)}" if where else ""
rows = query_db(
"l3",
f"SELECT * FROM dm_player_features {where_sql} ORDER BY {order_col} DESC LIMIT ? OFFSET ?",
args + [per_page, offset],
)
total_row = query_db("l3", f"SELECT COUNT(*) as cnt FROM dm_player_features {where_sql}", args, one=True)
total = int(total_row["cnt"]) if total_row else 0
dim_rows = query_db(
"l2",
"""
SELECT steam_id_64
FROM dim_players
WHERE LOWER(username) LIKE LOWER(?) OR steam_id_64 LIKE ?
""",
[f"%{search}%", f"%{search}%"],
)
matching_ids = [str(row["steam_id_64"]) for row in dim_rows]
rows = []
for start in range(0, len(matching_ids), 500):
chunk = matching_ids[start:start + 500]
placeholders = ",".join("?" for _ in chunk)
rows.extend(query_db(
"l3",
f"SELECT * FROM dm_player_features "
f"WHERE steam_id_64 IN ({placeholders})",
chunk,
))
rows = sorted(
rows,
key=lambda row: row[order_col] if row[order_col] is not None else float("-inf"),
reverse=True,
)
total = len(rows)
rows = rows[offset:offset + per_page]
else:
rows = query_db(
"l3",
f"SELECT * FROM dm_player_features "
f"ORDER BY {order_col} DESC LIMIT ? OFFSET ?",
[per_page, offset],
)
total_row = query_db(
"l3",
"SELECT COUNT(*) as cnt FROM dm_player_features",
one=True,
)
total = int(total_row["cnt"]) if total_row else 0
players = [FeatureService._normalize_features(dict(r)) for r in rows] if rows else []
players = [p for p in players if p]
@@ -160,19 +187,11 @@ class FeatureService:
@staticmethod
def get_roster_features_distribution(target_steam_id: str):
from web.services.web_service import WebService
import json
from web.services.team_context_service import TeamContextService
lineups = WebService.get_lineups()
roster_ids: list[str] = []
if lineups:
try:
p_ids = [str(i) for i in json.loads(lineups[0].get("player_ids_json") or "[]")]
if str(target_steam_id) in p_ids:
roster_ids = p_ids
except Exception:
roster_ids = []
roster_ids = TeamContextService.get_active_roster_ids()
if str(target_steam_id) not in roster_ids:
roster_ids = []
if not roster_ids:
return None
@@ -202,7 +221,17 @@ class FeatureService:
sample_keys = list(p.keys())
break
lower_is_better = {"int_timing_first_contact_time", "tac_avg_fd", "core_avg_match_duration"}
lower_is_better = {
"int_timing_first_contact_time",
"int_trade_response_time",
"tac_avg_fd",
"tac_fd_rate",
"core_avg_match_duration",
"core_dpr",
"meta_rating_volatility",
"meta_map_stability",
"meta_elo_tier_stability",
}
result: dict[str, Any] = {}
for m in sample_keys:
@@ -224,16 +253,22 @@ class FeatureService:
values = []
for p in stats_map.values():
v = (p or {}).get(m)
if v is None:
continue
try:
values.append(float(v) if v is not None else 0.0)
values.append(float(v))
except (ValueError, TypeError):
values.append(0.0)
continue
target_val_raw = (stats_map.get(target_steam_id) or {}).get(m)
if target_val_raw is None or not values:
result[m] = None
continue
try:
target_val = float(target_val_raw) if target_val_raw is not None else 0.0
target_val = float(target_val_raw)
except (ValueError, TypeError):
target_val = 0.0
result[m] = None
continue
is_reverse = m not in lower_is_better
# Sort values. For standard metrics, higher is better (reverse=True).
@@ -251,9 +286,9 @@ class FeatureService:
"val": target_val,
"rank": rank,
"total": len(values_sorted),
"min": min(values_sorted) if values_sorted else 0,
"max": max(values_sorted) if values_sorted else 0,
"avg": (sum(values_sorted) / len(values_sorted)) if values_sorted else 0,
"min": min(values_sorted),
"max": max(values_sorted),
"avg": sum(values_sorted) / len(values_sorted),
"inverted": not is_reverse,
}
return result