Print function correction + player analysis fix and player stats fix
This commit is contained in:
@@ -638,14 +638,9 @@ def analyze_player_performance(player_id, archives_data):
|
||||
if score < player_stats['worst_tournament_score']:
|
||||
player_stats['worst_tournament_score'] = score
|
||||
|
||||
# Count shots fired
|
||||
targets = participant.get('targets', {})
|
||||
shots_in_tournament = 0
|
||||
for target in targets.values():
|
||||
if target.get('shot1') is not None:
|
||||
shots_in_tournament += 1
|
||||
if target.get('shot2') is not None:
|
||||
shots_in_tournament += 1
|
||||
# Count shots fired - NOW WITH PROPER COUNTING FOR ALL FORMATS
|
||||
tournament_type = archive.get('tournament_type', '20_targets')
|
||||
shots_in_tournament = count_shots_in_tournament(participant, tournament_type)
|
||||
|
||||
player_stats['total_shots_fired'] += shots_in_tournament
|
||||
|
||||
@@ -655,7 +650,7 @@ def analyze_player_performance(player_id, archives_data):
|
||||
'score': score,
|
||||
'tournament_type': archive['tournament_type'],
|
||||
'completed': completed,
|
||||
'shots_fired': shots_in_tournament
|
||||
'shots_fired': shots_in_tournament # NOW CORRECTLY CALCULATED
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"Error analyzing tournament archive: {e}")
|
||||
@@ -923,48 +918,7 @@ def api_get_archive_stats():
|
||||
return jsonify({'status': 'success', 'stats': stats})
|
||||
except Exception as e:
|
||||
return jsonify({'status': 'error', 'message': str(e)}), 500
|
||||
@app.route('/api/archive/player/<int:player_id>/performance', methods=['GET'])
|
||||
def api_get_player_performance(player_id):
|
||||
"""API endpoint to get player performance data for charts"""
|
||||
try:
|
||||
archives_data = {
|
||||
'tournaments': get_archived_tournaments(),
|
||||
'leagues': get_archived_leagues()
|
||||
}
|
||||
|
||||
player_stats = analyze_player_performance(player_id, archives_data)
|
||||
|
||||
# Prepare chart data
|
||||
performance_data = {
|
||||
'trend': {
|
||||
'labels': [f'T{i+1}' for i in range(len(player_stats['tournament_scores']))],
|
||||
'data': player_stats['tournament_scores']
|
||||
},
|
||||
'distribution': {
|
||||
'labels': ['0-50', '51-60', '61-70', '71-80', '81-90', '91-100'],
|
||||
'data': [0, 0, 0, 0, 0, 0]
|
||||
}
|
||||
}
|
||||
|
||||
# Calculate score distribution
|
||||
for score in player_stats['tournament_scores']:
|
||||
if score <= 50:
|
||||
performance_data['distribution']['data'][0] += 1
|
||||
elif score <= 60:
|
||||
performance_data['distribution']['data'][1] += 1
|
||||
elif score <= 70:
|
||||
performance_data['distribution']['data'][2] += 1
|
||||
elif score <= 80:
|
||||
performance_data['distribution']['data'][3] += 1
|
||||
elif score <= 90:
|
||||
performance_data['distribution']['data'][4] += 1
|
||||
else:
|
||||
performance_data['distribution']['data'][5] += 1
|
||||
|
||||
return jsonify({'status': 'success', 'performance': performance_data})
|
||||
except Exception as e:
|
||||
return jsonify({'status': 'error', 'message': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/archive/players/with-stats', methods=['GET'])
|
||||
def api_get_players_with_stats():
|
||||
"""API endpoint to get all players with their basic stats"""
|
||||
@@ -2162,7 +2116,504 @@ def get_camera_titles():
|
||||
except Exception as e:
|
||||
return jsonify({'status': 'error', 'message': str(e)}), 400
|
||||
|
||||
@app.route('/api/archive/player/<int:player_id>/shot-accuracy')
|
||||
def get_player_shot_accuracy(player_id):
|
||||
"""
|
||||
Get aggregated shot accuracy data for a player from tournament archive files
|
||||
"""
|
||||
try:
|
||||
# Get player name from JSON file instead of database
|
||||
players_data = load_players()
|
||||
player = None
|
||||
for p in players_data['players']:
|
||||
if p['id'] == player_id:
|
||||
player = p
|
||||
break
|
||||
|
||||
if not player:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Player with ID {player_id} not found'
|
||||
}), 404
|
||||
|
||||
player_name = player['name']
|
||||
|
||||
# Initialize shot counts by tournament type
|
||||
shot_accuracy = {
|
||||
'40 Targets': defaultdict(int),
|
||||
'20 Targets': defaultdict(int),
|
||||
'4 Targets': defaultdict(int)
|
||||
}
|
||||
|
||||
# Path to tournament archives
|
||||
tournament_archives_path = 'tournament_archives'
|
||||
|
||||
# Check if directory exists
|
||||
if not os.path.exists(tournament_archives_path):
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Tournament archives directory not found: {tournament_archives_path}'
|
||||
}), 404
|
||||
|
||||
# Find all tournament archive files
|
||||
archive_files = glob.glob(f"{tournament_archives_path}/tournament_*.json")
|
||||
|
||||
if not archive_files:
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'data': {},
|
||||
'player_name': player_name,
|
||||
'message': 'No tournament archive files found'
|
||||
})
|
||||
|
||||
# Process each tournament archive file
|
||||
for file_path in archive_files:
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
tournament_data = json.load(f)
|
||||
|
||||
# Check if this tournament has our player
|
||||
participants = tournament_data.get('results', {}).get('participants', {})
|
||||
|
||||
# Find player by ID or name
|
||||
player_data = None
|
||||
for participant_id, participant_info in participants.items():
|
||||
if (str(participant_id) == str(player_id) or
|
||||
participant_info.get('name') == player_name):
|
||||
player_data = participant_info
|
||||
break
|
||||
|
||||
if not player_data or not player_data.get('completed'):
|
||||
continue
|
||||
|
||||
# Determine tournament type
|
||||
tournament_type = determine_tournament_type_from_archive(tournament_data)
|
||||
|
||||
# Extract individual shots - NOW WITH PROPER SHOT COUNTING FOR ALL FORMATS
|
||||
shots = extract_shots_from_player_data(player_data, tournament_type)
|
||||
|
||||
# Debug print to verify shot counts
|
||||
print(f"Player {player_name}, Tournament type: {tournament_type}, Total shots extracted: {len(shots)}")
|
||||
|
||||
# Count shots by value
|
||||
for shot_value in shots:
|
||||
if shot_value == 10:
|
||||
shot_accuracy[tournament_type]['tens'] += 1
|
||||
elif shot_value == 9:
|
||||
shot_accuracy[tournament_type]['nines'] += 1
|
||||
elif shot_value == 8:
|
||||
shot_accuracy[tournament_type]['eights'] += 1
|
||||
elif shot_value == 7:
|
||||
shot_accuracy[tournament_type]['sevens'] += 1
|
||||
elif shot_value == 6:
|
||||
shot_accuracy[tournament_type]['sixes'] += 1
|
||||
elif shot_value == 5:
|
||||
shot_accuracy[tournament_type]['fives'] += 1
|
||||
elif shot_value == 4:
|
||||
shot_accuracy[tournament_type]['fours'] += 1
|
||||
elif shot_value == 3:
|
||||
shot_accuracy[tournament_type]['threes'] += 1
|
||||
elif shot_value == 2:
|
||||
shot_accuracy[tournament_type]['twos'] += 1
|
||||
elif shot_value == 1:
|
||||
shot_accuracy[tournament_type]['ones'] += 1
|
||||
elif shot_value == 0:
|
||||
shot_accuracy[tournament_type]['zeros'] += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing tournament file {file_path}: {e}")
|
||||
continue
|
||||
|
||||
# Convert defaultdict to regular dict for JSON serialization
|
||||
result = {}
|
||||
for tournament_type, counts in shot_accuracy.items():
|
||||
if any(counts.values()): # Only include types with data
|
||||
result[tournament_type] = dict(counts)
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'data': result,
|
||||
'player_name': player_name,
|
||||
'files_processed': len(archive_files)
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e),
|
||||
'traceback': traceback.format_exc()
|
||||
}), 500
|
||||
|
||||
@app.route('/api/archive/tournament/<tournament_id>/shots')
|
||||
def get_tournament_shots(tournament_id):
|
||||
"""
|
||||
Get individual shot data for a specific tournament from archive
|
||||
"""
|
||||
try:
|
||||
# Tournament ID might be a database ID or the tournament filename/timestamp
|
||||
tournament_archives_path = 'tournament_archives'
|
||||
|
||||
# Try to find tournament file by various methods
|
||||
tournament_file = None
|
||||
|
||||
# Method 1: Direct filename match
|
||||
potential_files = [
|
||||
f"{tournament_archives_path}/tournament_{tournament_id}.json",
|
||||
f"{tournament_archives_path}/{tournament_id}.json"
|
||||
]
|
||||
|
||||
for file_path in potential_files:
|
||||
if os.path.exists(file_path):
|
||||
tournament_file = file_path
|
||||
break
|
||||
|
||||
# Method 2: Search through all tournament files for matching ID
|
||||
if not tournament_file:
|
||||
archive_files = glob.glob(f"{tournament_archives_path}/tournament_*.json")
|
||||
for file_path in archive_files:
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Check if tournament ID matches
|
||||
tournament_data = data.get('tournament', {})
|
||||
results_data = data.get('results', {})
|
||||
|
||||
if (tournament_data.get('created_at') == tournament_id or
|
||||
results_data.get('tournament_id') == tournament_id or
|
||||
str(tournament_id) in file_path):
|
||||
tournament_file = file_path
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
if not tournament_file:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Tournament archive file not found'
|
||||
}), 404
|
||||
|
||||
# Load tournament data
|
||||
with open(tournament_file, 'r') as f:
|
||||
tournament_data = json.load(f)
|
||||
|
||||
# Extract all players' shots
|
||||
participants = tournament_data.get('results', {}).get('participants', {})
|
||||
all_shots_data = {}
|
||||
|
||||
for participant_id, participant_info in participants.items():
|
||||
if participant_info.get('completed'):
|
||||
shots = extract_shots_from_player_data(participant_info)
|
||||
all_shots_data[participant_info.get('name')] = shots
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'tournament_data': {
|
||||
'tournament_type': determine_tournament_type_from_archive(tournament_data),
|
||||
'participants': len(participants),
|
||||
'file_path': tournament_file
|
||||
},
|
||||
'shots_by_player': all_shots_data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}), 500
|
||||
|
||||
|
||||
def extract_shots_from_player_data(player_data, tournament_type=None):
|
||||
"""
|
||||
Extract individual shot values from player data in your archive format
|
||||
Now properly handles all tournament formats:
|
||||
- 4 targets: 5 shots each (shot1-shot5) = 20 total shots
|
||||
- 20 targets: 2 shots each (shot1-shot2) = 40 total shots
|
||||
- 40 targets: 2 shots each (shot1-shot2) = 80 total shots
|
||||
"""
|
||||
shots = []
|
||||
targets = player_data.get('targets', {})
|
||||
|
||||
if not targets:
|
||||
return shots
|
||||
|
||||
# Sort targets by number to maintain order
|
||||
target_numbers = sorted([int(k) for k in targets.keys() if k.isdigit()])
|
||||
|
||||
# Determine shots per target based on tournament format
|
||||
if tournament_type and '4' in str(tournament_type):
|
||||
shots_per_target = 5 # 4 targets format: 5 shots each
|
||||
else:
|
||||
# Auto-detect if tournament_type not provided
|
||||
num_targets = len(target_numbers)
|
||||
if num_targets <= 6: # Likely 4 targets format (4 targets + maybe some extras)
|
||||
shots_per_target = 5
|
||||
else: # 20 or 40 targets format
|
||||
shots_per_target = 2
|
||||
|
||||
for target_num in target_numbers:
|
||||
target_data = targets[str(target_num)]
|
||||
|
||||
# Extract shots for this target
|
||||
for shot_num in range(1, shots_per_target + 1):
|
||||
shot_key = f'shot{shot_num}'
|
||||
shot_value = target_data.get(shot_key)
|
||||
|
||||
if shot_value is not None:
|
||||
shots.append(int(shot_value))
|
||||
|
||||
return shots
|
||||
|
||||
def count_shots_in_tournament(participant_data, tournament_type=None):
|
||||
"""
|
||||
Count total shots fired by a participant in a tournament
|
||||
Now properly handles all tournament formats:
|
||||
- 4 targets: 5 shots each = 20 total shots
|
||||
- 20 targets: 2 shots each = 40 total shots
|
||||
- 40 targets: 2 shots each = 80 total shots
|
||||
"""
|
||||
targets = participant_data.get('targets', {})
|
||||
shots_count = 0
|
||||
|
||||
if not targets:
|
||||
return shots_count
|
||||
|
||||
# Determine shots per target based on tournament format
|
||||
if tournament_type and '4' in str(tournament_type):
|
||||
max_shots_per_target = 5 # 4 targets format: 5 shots each
|
||||
else:
|
||||
# Auto-detect if tournament_type not provided
|
||||
target_count = len([k for k in targets.keys() if k.isdigit()])
|
||||
if target_count <= 6: # Likely 4 targets format
|
||||
max_shots_per_target = 5
|
||||
else: # 20 or 40 targets format
|
||||
max_shots_per_target = 2
|
||||
|
||||
for target in targets.values():
|
||||
for shot_num in range(1, max_shots_per_target + 1):
|
||||
shot_key = f'shot{shot_num}'
|
||||
if target.get(shot_key) is not None:
|
||||
shots_count += 1
|
||||
|
||||
return shots_count
|
||||
|
||||
def determine_tournament_type_from_archive(tournament_data):
|
||||
"""
|
||||
Determine tournament type from your archive data structure
|
||||
"""
|
||||
# First check the tournament_type field
|
||||
tournament_info = tournament_data.get('tournament', {})
|
||||
results_info = tournament_data.get('results', {})
|
||||
|
||||
tournament_type = (tournament_info.get('tournament_type') or
|
||||
results_info.get('tournament_type'))
|
||||
|
||||
if tournament_type:
|
||||
if '40' in tournament_type:
|
||||
return '40 Targets'
|
||||
elif '20' in tournament_type:
|
||||
return '20 Targets'
|
||||
elif '4' in tournament_type:
|
||||
return '4 Targets'
|
||||
|
||||
# Fallback: count targets from first completed player
|
||||
participants = results_info.get('participants', {})
|
||||
for participant_info in participants.values():
|
||||
if participant_info.get('completed'):
|
||||
targets = participant_info.get('targets', {})
|
||||
target_count = len([k for k in targets.keys() if k.isdigit()])
|
||||
|
||||
if target_count >= 30: # 40 targets
|
||||
return '40 Targets'
|
||||
elif target_count >= 10: # 20 targets
|
||||
return '20 Targets'
|
||||
elif target_count <= 6: # 4 targets (changed from <= 8 to <= 6)
|
||||
return '4 Targets'
|
||||
break
|
||||
|
||||
# Default fallback
|
||||
return '20 Targets'
|
||||
|
||||
@app.route('/api/debug/player/<int:player_id>')
|
||||
def debug_player_info(player_id):
|
||||
"""
|
||||
Debug endpoint to check player info and archive structure
|
||||
"""
|
||||
try:
|
||||
# Check player exists
|
||||
players_data = load_players()
|
||||
player = None
|
||||
for p in players_data['players']:
|
||||
if p['id'] == player_id:
|
||||
player = p
|
||||
break
|
||||
|
||||
# Check archive directory
|
||||
tournament_archives_path = 'tournament_archives'
|
||||
archive_exists = os.path.exists(tournament_archives_path)
|
||||
archive_files = []
|
||||
|
||||
if archive_exists:
|
||||
archive_files = glob.glob(f"{tournament_archives_path}/tournament_*.json")
|
||||
|
||||
# Check current working directory
|
||||
cwd = os.getcwd()
|
||||
|
||||
# List contents of current directory
|
||||
current_dir_contents = os.listdir('.')
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'player_found': player is not None,
|
||||
'player_info': player,
|
||||
'current_working_directory': cwd,
|
||||
'current_dir_contents': current_dir_contents,
|
||||
'archive_directory_exists': archive_exists,
|
||||
'archive_directory_path': tournament_archives_path,
|
||||
'archive_files_found': len(archive_files),
|
||||
'archive_files': [os.path.basename(f) for f in archive_files[:5]] # First 5 files
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e),
|
||||
'traceback': traceback.format_exc()
|
||||
}), 500
|
||||
|
||||
# Debug endpoint to see raw tournament data
|
||||
@app.route('/api/archive/tournament-file/<path:filename>')
|
||||
def get_tournament_file_debug(filename):
|
||||
"""
|
||||
Debug endpoint to see raw tournament file data
|
||||
"""
|
||||
try:
|
||||
tournament_archives_path = 'tournament_archives'
|
||||
file_path = os.path.join(tournament_archives_path, filename)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'File not found'
|
||||
}), 404
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'file_path': file_path,
|
||||
'data': data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}), 500
|
||||
|
||||
@app.route('/api/archive/tournament-leaders', methods=['GET'])
|
||||
def api_get_tournament_leaders():
|
||||
"""API endpoint to get overall tournament leaders by tournament type"""
|
||||
try:
|
||||
tournaments = get_archived_tournaments()
|
||||
|
||||
# Group data by tournament type
|
||||
tournament_types = {
|
||||
'20_targets': {
|
||||
'name': '20 Targets',
|
||||
'description': '20 Targets (2 shots each)',
|
||||
'best_score': {'player_name': None, 'score': 0},
|
||||
'most_tens': {'player_name': None, 'tens': 0},
|
||||
'total_tournaments': 0
|
||||
},
|
||||
'40_targets': {
|
||||
'name': '40 Targets',
|
||||
'description': '40 Targets (2 shots each)',
|
||||
'best_score': {'player_name': None, 'score': 0},
|
||||
'most_tens': {'player_name': None, 'tens': 0},
|
||||
'total_tournaments': 0
|
||||
},
|
||||
'4_targets': {
|
||||
'name': '4 Targets',
|
||||
'description': '4 Targets (5 shots each)',
|
||||
'best_score': {'player_name': None, 'score': 0},
|
||||
'most_tens': {'player_name': None, 'tens': 0},
|
||||
'total_tournaments': 0
|
||||
}
|
||||
}
|
||||
|
||||
for tournament in tournaments:
|
||||
try:
|
||||
data = load_archive_file(tournament['filepath'])
|
||||
if not data:
|
||||
continue
|
||||
|
||||
results = data.get('results', {})
|
||||
participants = results.get('participants', {})
|
||||
tournament_type = tournament.get('tournament_type', '20_targets')
|
||||
|
||||
if tournament_type not in tournament_types or not participants:
|
||||
continue
|
||||
|
||||
tournament_types[tournament_type]['total_tournaments'] += 1
|
||||
|
||||
for player_id, participant in participants.items():
|
||||
if not participant.get('completed'):
|
||||
continue
|
||||
|
||||
player_name = participant.get('name', f'Player {player_id}')
|
||||
|
||||
# Check best score for this tournament type
|
||||
score = participant.get('total_score', 0)
|
||||
if score > tournament_types[tournament_type]['best_score']['score']:
|
||||
tournament_types[tournament_type]['best_score'] = {
|
||||
'player_name': player_name,
|
||||
'score': score
|
||||
}
|
||||
|
||||
# Count 10s for this player in this tournament
|
||||
targets = participant.get('targets', {})
|
||||
tens_count = 0
|
||||
for target in targets.values():
|
||||
if target.get('shot1') == 10:
|
||||
tens_count += 1
|
||||
if target.get('shot2') == 10:
|
||||
tens_count += 1
|
||||
# For 4_targets format, check additional shots
|
||||
for shot_num in range(3, 6): # shot3, shot4, shot5
|
||||
if target.get(f'shot{shot_num}') == 10:
|
||||
tens_count += 1
|
||||
|
||||
if tens_count > tournament_types[tournament_type]['most_tens']['tens']:
|
||||
tournament_types[tournament_type]['most_tens'] = {
|
||||
'player_name': player_name,
|
||||
'tens': tens_count
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing tournament {tournament.get('filepath', 'unknown')}: {e}")
|
||||
continue
|
||||
|
||||
# Convert to list format, only include types with data
|
||||
tournament_leaders = []
|
||||
for type_key, type_data in tournament_types.items():
|
||||
if type_data['total_tournaments'] > 0 and type_data['best_score']['player_name']:
|
||||
tournament_leaders.append({
|
||||
'id': type_key,
|
||||
'name': type_data['name'],
|
||||
'description': type_data['description'],
|
||||
'total_tournaments': type_data['total_tournaments'],
|
||||
'best_score': type_data['best_score'],
|
||||
'most_tens': type_data['most_tens']
|
||||
})
|
||||
|
||||
return jsonify({'status': 'success', 'tournament_types': tournament_leaders})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'status': 'error', 'message': str(e)}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
Reference in New Issue
Block a user