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:
@@ -41,64 +41,7 @@
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Standardized Navigation Bar */
|
||||
.navbar {
|
||||
background: white;
|
||||
color: black;
|
||||
padding: 15px 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e9ecef;
|
||||
cursor: pointer;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-1px);
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Standardized Container */
|
||||
/* Standardized Container */
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -25,63 +25,6 @@
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Enhanced Navigation Bar */
|
||||
.navbar {
|
||||
background: white;
|
||||
color: black;
|
||||
padding: 15px 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navbar-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
background: #f8f9fa;
|
||||
border: 2px solid #e9ecef;
|
||||
cursor: pointer;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-1px);
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: #007bff;
|
||||
border-color: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-btn.active:hover {
|
||||
background: #0056b3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Standardized Container */
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
@@ -321,6 +264,67 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.accuracy-chart-container {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e9ecef;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.accuracy-chart-container h4 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #333;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.accuracy-bar-chart {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-around;
|
||||
height: 200px;
|
||||
gap: 6px;
|
||||
padding: 10px 0;
|
||||
border-bottom: 2px solid #e9ecef;
|
||||
}
|
||||
|
||||
.accuracy-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
max-width: 35px;
|
||||
}
|
||||
|
||||
.accuracy-bar-value {
|
||||
width: 100%;
|
||||
border-radius: 4px 4px 0 0;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
min-height: 5px;
|
||||
}
|
||||
|
||||
.accuracy-bar-value:hover {
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 -2px 8px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.accuracy-bar-label {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.accuracy-bar-count {
|
||||
font-size: 0.7rem;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
@@ -447,6 +451,44 @@
|
||||
border-color: rgba(33, 150, 243, 0.2);
|
||||
}
|
||||
|
||||
/* Clickable History Item Links */
|
||||
.history-item-link {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.history-item-link .history-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.history-item-link:hover .history-item {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 35px rgba(33, 150, 243, 0.35);
|
||||
background: linear-gradient(135deg, #c5dff8 0%, #a5d6fd 100%);
|
||||
border-color: rgba(33, 150, 243, 0.4);
|
||||
}
|
||||
|
||||
/* Clickable League History Item Links */
|
||||
.league-history-item-link {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.league-history-item-link .league-history-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.league-history-item-link:hover .league-history-item {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 35px rgba(156, 39, 176, 0.35);
|
||||
background: linear-gradient(135deg, #d1a4e0 0%, #c878d8 100%);
|
||||
border-color: rgba(156, 39, 176, 0.3);
|
||||
}
|
||||
|
||||
.history-info {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -462,6 +504,9 @@
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
color: #0d47a1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.history-score {
|
||||
@@ -742,6 +787,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shot Accuracy Bar Chart -->
|
||||
<div class="accuracy-chart-container">
|
||||
<h4 data-i18n="analysis.shot_accuracy">Shot Accuracy Distribution</h4>
|
||||
<div class="accuracy-bar-chart" id="accuracyBarChart"></div>
|
||||
</div>
|
||||
|
||||
<!-- Shot Accuracy Stats -->
|
||||
<div class="accuracy-stats" id="accuracyStats">
|
||||
<div class="accuracy-stat">
|
||||
@@ -805,7 +856,8 @@
|
||||
<div class="panel-title">🎯 <span data-i18n="analysis.tournament_history">Tournament History</span></div>
|
||||
{% if stats.tournament_history %}
|
||||
<div class="history-list">
|
||||
{% for tournament in stats.tournament_history[:10] %}
|
||||
{% for tournament in stats.tournament_history %}
|
||||
<a href="/archive/tournament/{{ tournament.filename }}" class="history-item-link" style="text-decoration: none;">
|
||||
<div class="history-item">
|
||||
<div class="history-info">
|
||||
<div class="history-date">{{ tournament.date[:10] if tournament.date != 'Unknown' else (translations.analysis.unknown_date if translations else 'Unknown Date') }}</div>
|
||||
@@ -813,12 +865,8 @@
|
||||
</div>
|
||||
<div class="history-score">{{ tournament.score }}</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% if stats.tournament_history|length > 10 %}
|
||||
<div style="text-align: center; padding: 8px; color: #666; font-size: 0.8rem;">
|
||||
... and {{ stats.tournament_history|length - 10 }} more
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
@@ -834,6 +882,7 @@
|
||||
{% if stats.league_history %}
|
||||
<div class="history-list">
|
||||
{% for league in stats.league_history %}
|
||||
<a href="/archive/league/{{ league.filename }}" class="league-history-item-link" style="text-decoration: none;">
|
||||
<div class="league-history-item">
|
||||
<div class="league-header">
|
||||
<div class="league-info">
|
||||
@@ -860,6 +909,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -884,6 +934,7 @@
|
||||
};
|
||||
|
||||
let currentChart = null;
|
||||
let accuracyChartInstance = null;
|
||||
let currentTournamentType = '40 Targets'; // Will be updated based on available data
|
||||
let tournamentsByType = {};
|
||||
let shotAccuracyData = {};
|
||||
@@ -896,23 +947,38 @@
|
||||
updateChart();
|
||||
}
|
||||
|
||||
// Load shot accuracy data from archive
|
||||
async function loadShotAccuracyData() {
|
||||
// Load shot accuracy data from template context
|
||||
function loadShotAccuracyData() {
|
||||
try {
|
||||
const playerId = '{{ player.id }}'; // Get player ID from template
|
||||
const response = await fetch(`/api/archive/player/${playerId}/shot-accuracy`);
|
||||
const result = await response.json();
|
||||
// Get shot accuracy data directly from template context
|
||||
const statsData = {{ stats|tojson }};
|
||||
console.log('Stats data from template:', statsData);
|
||||
|
||||
if (result.status === 'success') {
|
||||
shotAccuracyData = result.data;
|
||||
console.log('Shot accuracy data loaded:', shotAccuracyData);
|
||||
if (statsData && statsData.shot_accuracy) {
|
||||
shotAccuracyData = statsData.shot_accuracy;
|
||||
console.log('Shot accuracy data loaded from template:', shotAccuracyData);
|
||||
|
||||
// Map backend keys to display names
|
||||
const typeMapping = {
|
||||
'40_targets': '40 Targets',
|
||||
'20_targets': '20 Targets',
|
||||
'4_targets': '4 Targets'
|
||||
};
|
||||
|
||||
// Auto-select the first tournament type with data
|
||||
const availableTypes = Object.keys(shotAccuracyData);
|
||||
if (availableTypes.length > 0) {
|
||||
// Update current type to first available type
|
||||
const firstAvailableType = availableTypes[0];
|
||||
currentTournamentType = firstAvailableType;
|
||||
// Find the first type with actual data
|
||||
for (const backendType of availableTypes) {
|
||||
const typeData = shotAccuracyData[backendType];
|
||||
const hasData = Object.values(typeData).some(v => v > 0);
|
||||
if (hasData) {
|
||||
// Convert backend key to display name
|
||||
currentTournamentType = typeMapping[backendType] || backendType;
|
||||
console.log('Selected tournament type:', currentTournamentType, 'from backend key:', backendType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update button states
|
||||
updateActiveButton();
|
||||
@@ -920,13 +986,11 @@
|
||||
|
||||
updateChart(); // Refresh chart with accuracy data
|
||||
} else {
|
||||
console.log('No shot accuracy data available:', result.message);
|
||||
// Still try to show basic tournament data without shot accuracy
|
||||
console.log('No shot accuracy data available in template');
|
||||
updateChart();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading shot accuracy data:', error);
|
||||
console.log('API endpoints may not be set up yet. Showing basic tournament data only.');
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
@@ -1068,9 +1132,23 @@
|
||||
});
|
||||
|
||||
// Also check if we have aggregated shot accuracy data for this tournament type
|
||||
if (shotAccuracyData && shotAccuracyData[currentTournamentType]) {
|
||||
console.log('Current tournament type:', currentTournamentType);
|
||||
console.log('Available keys in shotAccuracyData:', Object.keys(shotAccuracyData));
|
||||
|
||||
// Map display names to backend keys
|
||||
const typeMapping = {
|
||||
'40 Targets': '40_targets',
|
||||
'20 Targets': '20_targets',
|
||||
'4 Targets': '4_targets'
|
||||
};
|
||||
|
||||
const backendKey = typeMapping[currentTournamentType] || currentTournamentType;
|
||||
console.log('Looking for data with key:', backendKey);
|
||||
|
||||
if (shotAccuracyData && shotAccuracyData[backendKey]) {
|
||||
hasData = true;
|
||||
const typeData = shotAccuracyData[currentTournamentType];
|
||||
const typeData = shotAccuracyData[backendKey];
|
||||
console.log('Found shot accuracy data for type:', typeData);
|
||||
tens = typeData.tens || 0;
|
||||
nines = typeData.nines || 0;
|
||||
eights = typeData.eights || 0;
|
||||
@@ -1082,6 +1160,9 @@
|
||||
twos = typeData.twos || 0;
|
||||
ones = typeData.ones || 0;
|
||||
zeros = typeData.zeros || 0;
|
||||
} else {
|
||||
console.log('No shot accuracy data found for type:', currentTournamentType, 'mapped to:', backendKey);
|
||||
console.log('Full shotAccuracyData object:', shotAccuracyData);
|
||||
}
|
||||
|
||||
const accuracyStatsDiv = document.getElementById('accuracyStats');
|
||||
@@ -1108,11 +1189,58 @@
|
||||
document.getElementById('ones').textContent = ones;
|
||||
document.getElementById('zeros').textContent = zeros;
|
||||
|
||||
// Render accuracy bar chart
|
||||
createAccuracyChart(tens, nines, eights, sevens, sixes, fives, fours, threes, twos, ones, zeros);
|
||||
|
||||
console.log(`Shot accuracy for ${currentTournamentType}:`, {
|
||||
tens, nines, eights, sevens, sixes, fives, fours, threes, twos, ones, zeros
|
||||
});
|
||||
}
|
||||
|
||||
// Create bar chart for shot accuracy distribution using CSS
|
||||
function createAccuracyChart(tens, nines, eights, sevens, sixes, fives, fours, threes, twos, ones, zeros) {
|
||||
const chartContainer = document.getElementById('accuracyBarChart');
|
||||
if (!chartContainer) {
|
||||
console.warn('Accuracy bar chart container not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = [
|
||||
{ label: '10', count: tens, color: '#4CAF50' },
|
||||
{ label: '9', count: nines, color: '#8BC34A' },
|
||||
{ label: '8', count: eights, color: '#CDDC39' },
|
||||
{ label: '7', count: sevens, color: '#FFC107' },
|
||||
{ label: '6', count: sixes, color: '#FF9800' },
|
||||
{ label: '5', count: fives, color: '#FF7043' },
|
||||
{ label: '4', count: fours, color: '#F44336' },
|
||||
{ label: '3', count: threes, color: '#E91E63' },
|
||||
{ label: '2', count: twos, color: '#9C27B0' },
|
||||
{ label: '1', count: ones, color: '#673AB7' },
|
||||
{ label: '0', count: zeros, color: '#9E9E9E' }
|
||||
];
|
||||
|
||||
// Find max count for scaling
|
||||
const maxCount = Math.max(...data.map(d => d.count), 1);
|
||||
|
||||
// Clear previous content
|
||||
chartContainer.innerHTML = '';
|
||||
|
||||
// Create bars
|
||||
data.forEach(item => {
|
||||
const barHeight = (item.count / maxCount) * 100;
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'accuracy-bar';
|
||||
bar.innerHTML = `
|
||||
<div class="accuracy-bar-value" style="height: ${barHeight}%; background-color: ${item.color};" title="${item.label}: ${item.count}"></div>
|
||||
<div class="accuracy-bar-label">${item.label}</div>
|
||||
<div class="accuracy-bar-count">${item.count}</div>
|
||||
`;
|
||||
chartContainer.appendChild(bar);
|
||||
});
|
||||
|
||||
console.log('CSS Bar chart created successfully with data:', {tens, nines, eights, sevens, sixes, fives, fours, threes, twos, ones, zeros});
|
||||
}
|
||||
|
||||
// Create chart for current tournament type
|
||||
function createChart(tournaments) {
|
||||
const canvas = document.getElementById('tournamentChart');
|
||||
|
||||
@@ -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,7 +226,12 @@ 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
|
||||
@@ -209,13 +264,20 @@ def analyze_player_performance(player_id, archives_data):
|
||||
|
||||
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,7 +297,10 @@ 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)
|
||||
@@ -246,7 +311,8 @@ def analyze_player_performance(player_id, archives_data):
|
||||
'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}")
|
||||
|
||||
Reference in New Issue
Block a user