1.7.0: New features.

This commit is contained in:
2026-01-27 21:26:07 +08:00
parent 5693eb84ee
commit 6b4cc048b3
11 changed files with 737 additions and 40 deletions

View File

@@ -5,21 +5,24 @@ import os
class StatsService:
@staticmethod
def resolve_avatar_url(steam_id, avatar_url):
"""
Resolves avatar URL with priority:
1. Local File (web/static/avatars/{steam_id}.jpg/png) - User override
2. DB Value (avatar_url)
"""
try:
# Check local file first (User Request: "directly associate if exists")
base = os.path.join(current_app.root_path, 'static', 'avatars')
for ext in ('.jpg', '.png', '.jpeg'):
fname = f"{steam_id}{ext}"
fpath = os.path.join(base, fname)
if os.path.exists(fpath):
return url_for('static', filename=f'avatars/{fname}')
# Fallback to DB value if valid
if avatar_url and str(avatar_url).strip():
return avatar_url
base = os.path.join(current_app.root_path, 'static', 'avatars')
# Check jpg/png in order
for ext in ('.jpg', '.png'):
fname = f"{steam_id}{ext}"
if os.path.exists(os.path.join(base, fname)):
url = url_for('static', filename=f'avatars/{fname}')
try:
# Persist fallback URL into L2 for future reads
execute_db('l2', "UPDATE dim_players SET avatar_url = ? WHERE steam_id_64 = ?", [url, str(steam_id)])
except Exception:
pass
return url
return None
except Exception:
return avatar_url
@@ -739,6 +742,9 @@ class StatsService:
'side_headshot_rate_ct', 'side_headshot_rate_t',
'side_defuses_ct', 'side_plants_t',
'util_avg_nade_dmg', 'util_avg_flash_time', 'util_avg_flash_enemy', 'util_usage_rate',
# New: ECO & PACE
'eco_avg_damage_per_1k', 'eco_rating_eco_rounds', 'eco_kd_ratio', 'eco_avg_rounds',
'pace_avg_time_to_first_contact', 'pace_trade_kill_rate', 'pace_opening_kill_time', 'pace_avg_life_time',
# New: Party Size Stats
'party_1_win_rate', 'party_1_rating', 'party_1_adr',
'party_2_win_rate', 'party_2_rating', 'party_2_adr',
@@ -759,6 +765,9 @@ class StatsService:
# Mapping for L2 legacy calls (if any) - mainly map 'rating' to 'basic_avg_rating' etc if needed
# But here we just use L3 columns directly.
# Define metrics where LOWER is BETTER
lower_is_better = ['pace_avg_time_to_first_contact', 'pace_opening_kill_time']
result = {}
for m in metrics:
@@ -768,8 +777,10 @@ class StatsService:
if not values:
result[m] = None
continue
values.sort(reverse=True)
# Sort: Reverse (High to Low) by default, unless in lower_is_better
is_reverse = m not in lower_is_better
values.sort(reverse=is_reverse)
# Rank
try:
@@ -783,7 +794,8 @@ class StatsService:
'total': len(values),
'min': min(values),
'max': max(values),
'avg': sum(values) / len(values)
'avg': sum(values) / len(values),
'inverted': not is_reverse # Flag for frontend to invert bar
}
# Legacy mapping for top cards (rating, kd, adr, kast)