199 lines
8.2 KiB
Python
199 lines
8.2 KiB
Python
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, flash, current_app, session
|
|
from web.services.stats_service import StatsService
|
|
from web.services.feature_service import FeatureService
|
|
from web.services.web_service import WebService
|
|
from web.database import execute_db
|
|
from web.config import Config
|
|
from datetime import datetime
|
|
import os
|
|
from werkzeug.utils import secure_filename
|
|
|
|
bp = Blueprint('players', __name__, url_prefix='/players')
|
|
|
|
@bp.route('/')
|
|
def index():
|
|
page = request.args.get('page', 1, type=int)
|
|
search = request.args.get('search')
|
|
# Default sort by 'matches' as requested
|
|
sort_by = request.args.get('sort', 'matches')
|
|
|
|
players, total = FeatureService.get_players_list(page, Config.ITEMS_PER_PAGE, sort_by, search)
|
|
total_pages = (total + Config.ITEMS_PER_PAGE - 1) // Config.ITEMS_PER_PAGE
|
|
|
|
return render_template('players/list.html', players=players, total=total, page=page, total_pages=total_pages, sort_by=sort_by)
|
|
|
|
@bp.route('/<steam_id>', methods=['GET', 'POST'])
|
|
def detail(steam_id):
|
|
if request.method == 'POST':
|
|
# Check if admin action
|
|
if 'admin_action' in request.form and session.get('is_admin'):
|
|
action = request.form.get('admin_action')
|
|
|
|
if action == 'update_profile':
|
|
notes = request.form.get('notes')
|
|
|
|
# Handle Avatar Upload
|
|
if 'avatar' in request.files:
|
|
file = request.files['avatar']
|
|
if file and file.filename:
|
|
try:
|
|
# Use steam_id as filename to ensure uniqueness per player
|
|
# Preserve extension
|
|
ext = os.path.splitext(file.filename)[1].lower()
|
|
if not ext: ext = '.jpg'
|
|
|
|
filename = f"{steam_id}{ext}"
|
|
upload_folder = os.path.join(current_app.root_path, 'static', 'avatars')
|
|
os.makedirs(upload_folder, exist_ok=True)
|
|
|
|
file_path = os.path.join(upload_folder, filename)
|
|
file.save(file_path)
|
|
|
|
# Generate URL (relative to web root)
|
|
avatar_url = url_for('static', filename=f'avatars/{filename}')
|
|
|
|
# Update L2 DB directly (Immediate effect)
|
|
execute_db('l2', "UPDATE dim_players SET avatar_url = ? WHERE steam_id_64 = ?", [avatar_url, steam_id])
|
|
|
|
flash('Avatar updated successfully.', 'success')
|
|
except Exception as e:
|
|
print(f"Avatar upload error: {e}")
|
|
flash('Error uploading avatar.', 'error')
|
|
|
|
WebService.update_player_metadata(steam_id, notes=notes)
|
|
flash('Profile updated.', 'success')
|
|
|
|
elif action == 'add_tag':
|
|
tag = request.form.get('tag')
|
|
if tag:
|
|
meta = WebService.get_player_metadata(steam_id)
|
|
tags = meta.get('tags', [])
|
|
if tag not in tags:
|
|
tags.append(tag)
|
|
WebService.update_player_metadata(steam_id, tags=tags)
|
|
flash('Tag added.', 'success')
|
|
|
|
elif action == 'remove_tag':
|
|
tag = request.form.get('tag')
|
|
if tag:
|
|
meta = WebService.get_player_metadata(steam_id)
|
|
tags = meta.get('tags', [])
|
|
if tag in tags:
|
|
tags.remove(tag)
|
|
WebService.update_player_metadata(steam_id, tags=tags)
|
|
flash('Tag removed.', 'success')
|
|
|
|
return redirect(url_for('players.detail', steam_id=steam_id))
|
|
|
|
# Add Comment
|
|
username = request.form.get('username', 'Anonymous')
|
|
content = request.form.get('content')
|
|
if content:
|
|
WebService.add_comment(None, username, 'player', steam_id, content)
|
|
flash('Comment added!', 'success')
|
|
return redirect(url_for('players.detail', steam_id=steam_id))
|
|
|
|
player = StatsService.get_player_info(steam_id)
|
|
if not player:
|
|
return "Player not found", 404
|
|
|
|
features = FeatureService.get_player_features(steam_id)
|
|
# Ensure basic stats fallback if features missing or incomplete
|
|
basic = StatsService.get_player_basic_stats(steam_id)
|
|
|
|
if not features:
|
|
if basic:
|
|
features = {
|
|
'basic_avg_rating': basic.get('rating', 0),
|
|
'basic_avg_kd': basic.get('kd', 0),
|
|
'basic_avg_kast': basic.get('kast', 0),
|
|
'basic_avg_adr': basic.get('adr', 0), # Pass ADR
|
|
}
|
|
else:
|
|
# If features exist but ADR is missing (not in L3), try to patch it from basic
|
|
if 'basic_avg_adr' not in features:
|
|
features = dict(features) # Convert to dict if row
|
|
features['basic_avg_adr'] = basic.get('adr', 0) if basic else 0
|
|
|
|
comments = WebService.get_comments('player', steam_id)
|
|
metadata = WebService.get_player_metadata(steam_id)
|
|
|
|
# History for table (L2 Source) - Fetch ALL for history table/chart
|
|
history_asc = StatsService.get_player_trend(steam_id, limit=1000)
|
|
history = history_asc[::-1] if history_asc else []
|
|
|
|
return render_template('players/profile.html', player=player, features=features, comments=comments, metadata=metadata, history=history)
|
|
|
|
@bp.route('/comment/<int:comment_id>/like', methods=['POST'])
|
|
def like_comment(comment_id):
|
|
WebService.like_comment(comment_id)
|
|
return jsonify({'success': True})
|
|
|
|
@bp.route('/<steam_id>/charts_data')
|
|
def charts_data(steam_id):
|
|
# ... (existing code) ...
|
|
# Trend Data
|
|
trends = StatsService.get_player_trend(steam_id, limit=1000)
|
|
|
|
# Radar Data (Construct from features)
|
|
features = FeatureService.get_player_features(steam_id)
|
|
radar_data = {}
|
|
if features:
|
|
# Dimensions: STA, BAT, HPS, PTL, T/CT, UTIL
|
|
radar_data = {
|
|
'STA': features['basic_avg_rating'] or 0,
|
|
'BAT': features['bat_avg_duel_win_rate'] or 0,
|
|
'HPS': features['hps_clutch_win_rate_1v1'] or 0,
|
|
'PTL': features['ptl_pistol_win_rate'] or 0,
|
|
'SIDE': features['side_rating_ct'] or 0,
|
|
'UTIL': features['util_usage_rate'] or 0
|
|
}
|
|
|
|
trend_labels = []
|
|
trend_values = []
|
|
for t in trends:
|
|
dt = datetime.fromtimestamp(t['start_time']) if t['start_time'] else datetime.now()
|
|
trend_labels.append(dt.strftime('%Y-%m-%d'))
|
|
trend_values.append(t['rating'])
|
|
|
|
return jsonify({
|
|
'trend': {'labels': trend_labels, 'values': trend_values},
|
|
'radar': radar_data
|
|
})
|
|
|
|
# --- API for Comparison ---
|
|
@bp.route('/api/search')
|
|
def api_search():
|
|
query = request.args.get('q', '')
|
|
if len(query) < 2:
|
|
return jsonify([])
|
|
|
|
players, _ = FeatureService.get_players_list(page=1, per_page=10, search=query)
|
|
# Return minimal data
|
|
results = [{'steam_id': p['steam_id_64'], 'username': p['username'], 'avatar_url': p['avatar_url']} for p in players]
|
|
return jsonify(results)
|
|
|
|
@bp.route('/api/batch_stats')
|
|
def api_batch_stats():
|
|
steam_ids = request.args.get('ids', '').split(',')
|
|
stats = []
|
|
for sid in steam_ids:
|
|
if not sid: continue
|
|
f = FeatureService.get_player_features(sid)
|
|
p = StatsService.get_player_info(sid)
|
|
|
|
if f and p:
|
|
stats.append({
|
|
'username': p['username'],
|
|
'steam_id': sid,
|
|
'radar': {
|
|
'STA': f['basic_avg_rating'] or 0,
|
|
'BAT': f['bat_avg_duel_win_rate'] or 0,
|
|
'HPS': f['hps_clutch_win_rate_1v1'] or 0,
|
|
'PTL': f['ptl_pistol_win_rate'] or 0,
|
|
'SIDE': f['side_rating_ct'] or 0,
|
|
'UTIL': f['util_usage_rate'] or 0
|
|
}
|
|
})
|
|
return jsonify(stats)
|