Add shot accuracy data calculation and visualization to player stats page

- Implement calculate_shot_accuracy() function in tv_app.py to extract individual shot values from tournament participant data
- Aggregate shot accuracy data (0-10) by tournament type (4_targets, 20_targets, 40_targets) in analyze_player_performance()
- Update modern_player_stats.html to load shot accuracy data directly from template context instead of API
- Add tournament type mapping between display names (40 Targets) and backend keys (40_targets)
- Implement CSS-based bar chart visualization that displays shot distribution with proper color gradients
- Remove unused async loadShotAccuracyData() API fetch and replace with direct template data access
- Data is now properly aggregated across all tournaments for each player and format type

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-12 13:00:52 +01:00
parent 8b503be144
commit a876c121ef
3 changed files with 322 additions and 185 deletions
+80 -14
View File
@@ -163,6 +163,56 @@ def get_archived_leagues():
def load_archive_file(filepath):
return ArchiveStorage.load_archive_file(filepath)
def calculate_shot_accuracy(participant):
"""
Extract shot accuracy breakdown from participant targets.
Returns dict with counts for each shot value (0-10).
"""
accuracy_data = {
'tens': 0,
'nines': 0,
'eights': 0,
'sevens': 0,
'sixes': 0,
'fives': 0,
'fours': 0,
'threes': 0,
'twos': 0,
'ones': 0,
'zeros': 0
}
targets = participant.get('targets', {})
for target_num, shots in targets.items():
# Extract all shots from this target (shot1, shot2, etc.)
for shot_key, shot_value in shots.items():
shot_val = int(shot_value)
if shot_val == 10:
accuracy_data['tens'] += 1
elif shot_val == 9:
accuracy_data['nines'] += 1
elif shot_val == 8:
accuracy_data['eights'] += 1
elif shot_val == 7:
accuracy_data['sevens'] += 1
elif shot_val == 6:
accuracy_data['sixes'] += 1
elif shot_val == 5:
accuracy_data['fives'] += 1
elif shot_val == 4:
accuracy_data['fours'] += 1
elif shot_val == 3:
accuracy_data['threes'] += 1
elif shot_val == 2:
accuracy_data['twos'] += 1
elif shot_val == 1:
accuracy_data['ones'] += 1
elif shot_val == 0:
accuracy_data['zeros'] += 1
return accuracy_data
def analyze_player_performance(player_id, archives_data):
"""Analyze performance of a specific player across all archives"""
player_stats = {
@@ -176,46 +226,58 @@ def analyze_player_performance(player_id, archives_data):
'total_shots_fired': 0,
'performance_trend': [],
'tournament_history': [],
'league_history': []
'league_history': [],
'shot_accuracy': {
'4_targets': {'tens': 0, 'nines': 0, 'eights': 0, 'sevens': 0, 'sixes': 0, 'fives': 0, 'fours': 0, 'threes': 0, 'twos': 0, 'ones': 0, 'zeros': 0},
'20_targets': {'tens': 0, 'nines': 0, 'eights': 0, 'sevens': 0, 'sixes': 0, 'fives': 0, 'fours': 0, 'threes': 0, 'twos': 0, 'ones': 0, 'zeros': 0},
'40_targets': {'tens': 0, 'nines': 0, 'eights': 0, 'sevens': 0, 'sixes': 0, 'fives': 0, 'fours': 0, 'threes': 0, 'twos': 0, 'ones': 0, 'zeros': 0}
}
}
# Process tournament archives
for archive in archives_data.get('tournaments', []):
try:
data = load_archive_file(archive['filepath'])
if not data:
continue
results = data.get('results', {})
participants = results.get('participants', {})
if str(player_id) in participants:
participant = participants[str(player_id)]
score = participant.get('total_score', 0)
completed = participant.get('completed', False)
if completed:
player_stats['total_tournaments'] += 1
player_stats['tournament_scores'].append(score)
if score > player_stats['best_tournament_score']:
player_stats['best_tournament_score'] = score
if score < player_stats['worst_tournament_score']:
player_stats['worst_tournament_score'] = score
# 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
# Calculate and aggregate shot accuracy
if tournament_type in player_stats['shot_accuracy']:
shot_accuracy = calculate_shot_accuracy(participant)
for key in shot_accuracy:
player_stats['shot_accuracy'][tournament_type][key] += shot_accuracy[key]
# Add to history
player_stats['tournament_history'].append({
'date': archive['archived_at'],
'score': score,
'tournament_type': archive['tournament_type'],
'completed': completed,
'shots_fired': shots_in_tournament # NOW CORRECTLY CALCULATED
'shots_fired': shots_in_tournament, # NOW CORRECTLY CALCULATED
'filename': archive.get('filename', '')
})
except Exception as e:
print(f"Error analyzing tournament archive: {e}")
@@ -235,18 +297,22 @@ def analyze_player_performance(player_id, archives_data):
participant = participants[str(player_id)]
final_score = participant.get('final_score', 0)
total_score = participant.get('total_score', 0)
tournaments_participated = participant.get('tournaments_participated', 0)
tournament_results = participant.get('tournament_results', [])
# Count tournaments participated based on tournament_results array
tournaments_participated = len([t for t in tournament_results if t.get('participated', False)])
player_stats['total_leagues'] += 1
player_stats['league_scores'].append(final_score)
player_stats['league_history'].append({
'date': archive['archived_at'],
'final_score': final_score,
'total_score': total_score,
'tournaments_participated': tournaments_participated,
'joker_used': participant.get('joker_used', False),
'tournament_results': participant.get('tournament_results', [])
'tournament_results': tournament_results,
'filename': archive.get('filename', '')
})
except Exception as e:
print(f"Error analyzing league archive: {e}")