From f110ae52f0d95e92fb399a45e3997e9c14a43362 Mon Sep 17 00:00:00 2001 From: bloodystream <77095318@qq.com> Date: Wed, 28 Jan 2026 15:11:31 +0800 Subject: [PATCH] feat: Add recent performance stability stats (matches/days) to player profile --- web/routes/players.py | 6 ++- web/services/stats_service.py | 66 ++++++++++++++++++++++++++++++ web/templates/players/profile.html | 60 +++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/web/routes/players.py b/web/routes/players.py index 28c5000..1f113c4 100644 --- a/web/routes/players.py +++ b/web/routes/players.py @@ -202,6 +202,9 @@ def detail(steam_id): }) map_stats_list.sort(key=lambda x: x['matches'], reverse=True) + # --- New: Recent Performance Stats --- + recent_stats = StatsService.get_recent_performance_stats(steam_id) + return render_template('players/profile.html', player=player, features=features, @@ -211,7 +214,8 @@ def detail(steam_id): distribution=distribution, map_stats=map_stats_list, l2_stats=l2_stats, - side_stats=side_stats) + side_stats=side_stats, + recent_stats=recent_stats) @bp.route('/comment//like', methods=['POST']) def like_comment(comment_id): diff --git a/web/services/stats_service.py b/web/services/stats_service.py index ee1bb72..118ab7b 100644 --- a/web/services/stats_service.py +++ b/web/services/stats_service.py @@ -632,6 +632,72 @@ class StatsService: """ return query_db('l2', sql, [steam_id, limit]) + @staticmethod + def get_recent_performance_stats(steam_id): + """ + Calculates Avg Rating and Rating Variance for: + - Last 5, 10, 15 matches + - Last 5, 10, 15 days + """ + import numpy as np + from datetime import datetime, timedelta + + # Fetch all match ratings with timestamps + sql = """ + SELECT m.start_time, mp.rating + FROM fact_match_players mp + JOIN fact_matches m ON mp.match_id = m.match_id + WHERE mp.steam_id_64 = ? + ORDER BY m.start_time DESC + """ + rows = query_db('l2', sql, [steam_id]) + + if not rows: + return {} + + # Convert to list of dicts + matches = [{'time': r['start_time'], 'rating': r['rating'] or 0} for r in rows] + + stats = {} + + # 1. Recent N Matches + for n in [5, 10, 15]: + subset = matches[:n] + if not subset: + stats[f'last_{n}_matches'] = {'avg': 0, 'var': 0, 'count': 0} + continue + + ratings = [m['rating'] for m in subset] + stats[f'last_{n}_matches'] = { + 'avg': np.mean(ratings), + 'var': np.var(ratings), + 'count': len(ratings) + } + + # 2. Recent N Days + # Use server time or max match time? usually server time 'now' is fine if data is fresh. + # But if data is old, 'last 5 days' might be empty. + # User asked for "recent 5/10/15 days", implying calendar days from now. + import time + now = time.time() + + for d in [5, 10, 15]: + cutoff = now - (d * 24 * 3600) + subset = [m for m in matches if m['time'] >= cutoff] + + if not subset: + stats[f'last_{d}_days'] = {'avg': 0, 'var': 0, 'count': 0} + continue + + ratings = [m['rating'] for m in subset] + stats[f'last_{d}_days'] = { + 'avg': np.mean(ratings), + 'var': np.var(ratings), + 'count': len(ratings) + } + + return stats + @staticmethod def get_roster_stats_distribution(target_steam_id): """ diff --git a/web/templates/players/profile.html b/web/templates/players/profile.html index 6a9fd70..d3bc230 100644 --- a/web/templates/players/profile.html +++ b/web/templates/players/profile.html @@ -141,6 +141,66 @@ + +
+

+ 📅 近期表现稳定性 (Recent Stability) +

+ +
+ +
+

By Matches

+
+ {% for n in [5, 10, 15] %} + {% set key = 'last_' ~ n ~ '_matches' %} + {% set data = recent_stats.get(key) %} +
+
+ {{ n }} + Matches +
+
+ {% if data and data.count > 0 %} +
{{ "{:.2f}".format(data.avg) }} Rating
+
Var: {{ "{:.3f}".format(data.var) }}
+ {% else %} + N/A + {% endif %} +
+
+ {% endfor %} +
+
+ + +
+

By Days

+
+ {% for n in [5, 10, 15] %} + {% set key = 'last_' ~ n ~ '_days' %} + {% set data = recent_stats.get(key) %} +
+
+ {{ n }} + Days +
+
+ {% if data and data.count > 0 %} +
{{ "{:.2f}".format(data.avg) }} Rating
+
Var: {{ "{:.3f}".format(data.var) }}
+
{{ data.count }} matches
+ {% else %} + No matches + {% endif %} +
+
+ {% endfor %} +
+
+
+
+