player stats updated badges
This commit is contained in:
@@ -182,6 +182,8 @@
|
|||||||
"average_score": "Average Score",
|
"average_score": "Average Score",
|
||||||
"highest_score": "Highest Score",
|
"highest_score": "Highest Score",
|
||||||
"worst_score": "Worst Score",
|
"worst_score": "Worst Score",
|
||||||
|
"hit_rate": "Hit Rate",
|
||||||
|
"most_common": "Most Common",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"position": "Position",
|
"position": "Position",
|
||||||
"points": "Points",
|
"points": "Points",
|
||||||
|
|||||||
@@ -188,6 +188,8 @@
|
|||||||
"average_score": "Povprečni Rezultat",
|
"average_score": "Povprečni Rezultat",
|
||||||
"highest_score": "Najvišji Rezultat",
|
"highest_score": "Najvišji Rezultat",
|
||||||
"worst_score": "Najslabši Rezultat",
|
"worst_score": "Najslabši Rezultat",
|
||||||
|
"hit_rate": "Stopnja Uspešnosti",
|
||||||
|
"most_common": "Najpogostejši",
|
||||||
"completed": "Zaključeno",
|
"completed": "Zaključeno",
|
||||||
"position": "Uvrstitev",
|
"position": "Uvrstitev",
|
||||||
"points": "Točke",
|
"points": "Točke",
|
||||||
|
|||||||
@@ -73,6 +73,45 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tournament Badge Styling */
|
||||||
|
.stat-badge.tournament-badge {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tournament-scores {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tournament-score-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tournament-type {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
color: #999;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tournament-emoji {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tournament-best {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
/* Charts Section */
|
/* Charts Section */
|
||||||
.charts-section {
|
.charts-section {
|
||||||
min-height: 450px;
|
min-height: 450px;
|
||||||
@@ -625,6 +664,49 @@
|
|||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shot Count Cards */
|
||||||
|
.shot-count-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 25px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shot-count-card {
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shot-count-card:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.15);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
border-color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shot-score {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #28a745;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shot-count {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
.history-list {
|
.history-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -968,6 +1050,90 @@
|
|||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Responsive Shot Count Cards */
|
||||||
|
.shot-count-cards {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shot-count-card {
|
||||||
|
min-height: 80px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shot-score {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shot-count {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Overall Accuracy Dashboard */
|
||||||
|
.overall-top-section {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge-section {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card-group {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
grid-template-columns: 2fr 2fr;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overall-bottom-section {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overall-bar-chart,
|
||||||
|
.overall-radar-chart {
|
||||||
|
min-height: 280px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card-icon {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card-value {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card-label {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge-stats {
|
||||||
|
gap: 15px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gauge-stat-value {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accuracy-gauge-wrapper {
|
||||||
|
height: 250px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="/static/js/i18n.js"></script>
|
<script src="/static/js/i18n.js"></script>
|
||||||
@@ -986,25 +1152,30 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- Stats Overview -->
|
<!-- Stats Overview -->
|
||||||
<div class="stats-badges">
|
<div class="stats-badges">
|
||||||
<div class="stat-badge">
|
<div class="stat-badge tournament-badge">
|
||||||
<span class="stat-icon">🏆</span>
|
<div class="tournament-scores">
|
||||||
<div class="stat-number">{{ stats.total_tournaments }}</div>
|
<div class="tournament-score-item">
|
||||||
<div class="stat-label" data-i18n="tournament.tournaments">Tournaments</div>
|
<div class="tournament-emoji">🎯</div>
|
||||||
|
<div class="tournament-type">4T</div>
|
||||||
|
<div class="tournament-best" id="best-4">{{ stats.best_4_targets_score if stats.best_4_targets_score > 0 else 0 }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="tournament-score-item">
|
||||||
|
<div class="tournament-emoji">⚡</div>
|
||||||
|
<div class="tournament-type">20T</div>
|
||||||
|
<div class="tournament-best" id="best-20">{{ stats.best_20_targets_score if stats.best_20_targets_score > 0 else 0 }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="tournament-score-item">
|
||||||
|
<div class="tournament-emoji">💪</div>
|
||||||
|
<div class="tournament-type">40T</div>
|
||||||
|
<div class="tournament-best" id="best-40">{{ stats.best_40_targets_score if stats.best_40_targets_score > 0 else 0 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-label">Best Scores</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-badge">
|
<div class="stat-badge">
|
||||||
<span class="stat-icon">🎖️</span>
|
<span class="stat-icon">🎯</span>
|
||||||
<div class="stat-number">{{ stats.total_leagues }}</div>
|
<div class="stat-number" id="mostCommon-badge">0</div>
|
||||||
<div class="stat-label" data-i18n="league.league">Leagues</div>
|
<div class="stat-label" data-i18n="results.most_common">Most Common</div>
|
||||||
</div>
|
|
||||||
<div class="stat-badge">
|
|
||||||
<span class="stat-icon">⭐</span>
|
|
||||||
<div class="stat-number">{{ stats.best_tournament_score }}</div>
|
|
||||||
<div class="stat-label" data-i18n="results.best_score">Best Score</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-badge">
|
|
||||||
<span class="stat-icon">📊</span>
|
|
||||||
<div class="stat-number">{{ stats.average_tournament_score|round|int if stats.average_tournament_score > 0 else 0 }}</div>
|
|
||||||
<div class="stat-label" data-i18n="results.average_score">Average</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-badge">
|
<div class="stat-badge">
|
||||||
<span class="stat-icon">🔫</span>
|
<span class="stat-icon">🔫</span>
|
||||||
@@ -1012,25 +1183,81 @@
|
|||||||
<div class="stat-label" data-i18n="results.total_shots">Total Shots</div>
|
<div class="stat-label" data-i18n="results.total_shots">Total Shots</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-badge">
|
<div class="stat-badge">
|
||||||
<span class="stat-icon">📉</span>
|
<span class="stat-icon">🏆</span>
|
||||||
<div class="stat-number">{{ stats.worst_tournament_score if stats.worst_tournament_score > 0 else 0 }}</div>
|
<div class="stat-number">{{ stats.total_tournaments }}</div>
|
||||||
<div class="stat-label" data-i18n="results.worst_score">Worst Score</div>
|
<div class="stat-label" data-i18n="tournament.tournaments">Tournaments</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-badge">
|
||||||
|
<span class="stat-icon">👑</span>
|
||||||
|
<div class="stat-number">{{ stats.total_leagues|default(0) }}</div>
|
||||||
|
<div class="stat-label" data-i18n="league.league">Leagues</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card 1: Overall Accuracy (Colorful - No Filters) -->
|
<!-- Card 1: Overall Accuracy (Bar + Pie Charts) -->
|
||||||
<div class="overall-accuracy-card">
|
<div class="overall-accuracy-card">
|
||||||
<div class="panel-title">📊 <span data-i18n="analysis.shot_accuracy">Overall Shot Accuracy</span></div>
|
<div class="panel-title">📊 <span data-i18n="analysis.shot_accuracy">Overall Shot Accuracy</span></div>
|
||||||
|
|
||||||
<!-- Overall Shot Accuracy Section -->
|
<!-- Charts Section -->
|
||||||
<div class="overall-content-wrapper">
|
<div class="overall-content-wrapper">
|
||||||
|
<!-- Bar Chart: All Shots -->
|
||||||
<div class="overall-chart-left">
|
<div class="overall-chart-left">
|
||||||
<canvas id="overallAccuracyChart"></canvas>
|
<canvas id="overallAccuracyChart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pie/Radar Chart -->
|
||||||
<div class="overall-radar-right">
|
<div class="overall-radar-right">
|
||||||
<canvas id="overallRadarChart"></canvas>
|
<canvas id="overallRadarChart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Shot Count Cards -->
|
||||||
|
<div class="shot-count-cards">
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">10</div>
|
||||||
|
<div class="shot-count" id="count-10">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">9</div>
|
||||||
|
<div class="shot-count" id="count-9">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">8</div>
|
||||||
|
<div class="shot-count" id="count-8">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">7</div>
|
||||||
|
<div class="shot-count" id="count-7">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">6</div>
|
||||||
|
<div class="shot-count" id="count-6">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">5</div>
|
||||||
|
<div class="shot-count" id="count-5">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">4</div>
|
||||||
|
<div class="shot-count" id="count-4">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">3</div>
|
||||||
|
<div class="shot-count" id="count-3">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">2</div>
|
||||||
|
<div class="shot-count" id="count-2">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">1</div>
|
||||||
|
<div class="shot-count" id="count-1">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="shot-count-card">
|
||||||
|
<div class="shot-score">0</div>
|
||||||
|
<div class="shot-count" id="count-0">0</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card 2: Filtered Accuracy (Main Outer Card - White) -->
|
<!-- Card 2: Filtered Accuracy (Main Outer Card - White) -->
|
||||||
@@ -1058,13 +1285,17 @@
|
|||||||
<div class="chart-stat-value" id="gameCount">0</div>
|
<div class="chart-stat-value" id="gameCount">0</div>
|
||||||
<div class="chart-stat-label" data-i18n="tournament.tournaments">Games</div>
|
<div class="chart-stat-label" data-i18n="tournament.tournaments">Games</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="chart-stat">
|
||||||
|
<div class="chart-stat-value" id="bestScore">0</div>
|
||||||
|
<div class="chart-stat-label" data-i18n="results.best_score">Best</div>
|
||||||
|
</div>
|
||||||
<div class="chart-stat">
|
<div class="chart-stat">
|
||||||
<div class="chart-stat-value" id="avgScore">0</div>
|
<div class="chart-stat-value" id="avgScore">0</div>
|
||||||
<div class="chart-stat-label" data-i18n="results.average_score">Average</div>
|
<div class="chart-stat-label" data-i18n="results.average_score">Average</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart-stat">
|
<div class="chart-stat">
|
||||||
<div class="chart-stat-value" id="bestScore">0</div>
|
<div class="chart-stat-value" id="mostCommonShot">0</div>
|
||||||
<div class="chart-stat-label" data-i18n="results.best_score">Best</div>
|
<div class="chart-stat-label">Most Common</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1342,11 +1573,75 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create overall accuracy bar chart
|
// Create overall bar chart
|
||||||
createOverallAccuracyChart(totalCounts);
|
createOverallAccuracyChart(totalCounts);
|
||||||
|
|
||||||
// Create overall radar chart
|
// Create overall pie chart
|
||||||
createOverallRadarChart(totalCounts);
|
createOverallRadarChart(totalCounts);
|
||||||
|
|
||||||
|
// Populate shot count cards
|
||||||
|
populateShotCountCards(totalCounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate shot count cards below charts
|
||||||
|
function populateShotCountCards(totalCounts) {
|
||||||
|
const scoreMap = {
|
||||||
|
10: totalCounts.tens,
|
||||||
|
9: totalCounts.nines,
|
||||||
|
8: totalCounts.eights,
|
||||||
|
7: totalCounts.sevens,
|
||||||
|
6: totalCounts.sixes,
|
||||||
|
5: totalCounts.fives,
|
||||||
|
4: totalCounts.fours,
|
||||||
|
3: totalCounts.threes,
|
||||||
|
2: totalCounts.twos,
|
||||||
|
1: totalCounts.ones,
|
||||||
|
0: totalCounts.zeros
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update shot count cards
|
||||||
|
for (const [score, count] of Object.entries(scoreMap)) {
|
||||||
|
const countElement = document.getElementById(`count-${score}`);
|
||||||
|
if (countElement) {
|
||||||
|
countElement.textContent = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update top stat badges
|
||||||
|
updateTopStatsBadges(totalCounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update top stat badges with overall metrics
|
||||||
|
function updateTopStatsBadges(totalCounts) {
|
||||||
|
// Perfect 10s count
|
||||||
|
const totalTensBadge = document.getElementById('totalTens-badge');
|
||||||
|
if (totalTensBadge) {
|
||||||
|
totalTensBadge.textContent = totalCounts.tens;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate most common shot value
|
||||||
|
let mostCommonShot = 0;
|
||||||
|
let maxCount = 0;
|
||||||
|
|
||||||
|
const shotScores = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
|
||||||
|
const shotCountsArray = [
|
||||||
|
totalCounts.tens, totalCounts.nines, totalCounts.eights,
|
||||||
|
totalCounts.sevens, totalCounts.sixes, totalCounts.fives,
|
||||||
|
totalCounts.fours, totalCounts.threes, totalCounts.twos,
|
||||||
|
totalCounts.ones, totalCounts.zeros
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < shotScores.length; i++) {
|
||||||
|
if (shotCountsArray[i] > maxCount) {
|
||||||
|
maxCount = shotCountsArray[i];
|
||||||
|
mostCommonShot = shotScores[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mostCommonBadge = document.getElementById('mostCommon-badge');
|
||||||
|
if (mostCommonBadge) {
|
||||||
|
mostCommonBadge.textContent = mostCommonShot;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create overall accuracy bar chart
|
// Create overall accuracy bar chart
|
||||||
@@ -1505,6 +1800,124 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create accuracy gauge chart (doughnut showing overall accuracy percentage)
|
||||||
|
let accuracyGaugeChartInstance = null;
|
||||||
|
|
||||||
|
function createAccuracyGaugeChart(totalCounts) {
|
||||||
|
const canvas = document.getElementById('accuracyGaugeChart');
|
||||||
|
if (!canvas) {
|
||||||
|
console.warn('Accuracy gauge chart canvas not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Destroy existing chart if it exists
|
||||||
|
if (accuracyGaugeChartInstance) {
|
||||||
|
accuracyGaugeChartInstance.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate accuracy percentage (perfect shots 8-10 / total shots)
|
||||||
|
const perfectShots = totalCounts.tens + totalCounts.nines + totalCounts.eights;
|
||||||
|
const totalShots = Object.values(totalCounts).reduce((a, b) => a + b, 0);
|
||||||
|
const accuracyPercentage = totalShots > 0 ? Math.round((perfectShots / totalShots) * 100) : 0;
|
||||||
|
|
||||||
|
// Calculate consistency score (lower variance = higher consistency)
|
||||||
|
const shotValues = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
|
||||||
|
const shotCounts = [totalCounts.tens, totalCounts.nines, totalCounts.eights, totalCounts.sevens, totalCounts.sixes, totalCounts.fives, totalCounts.fours, totalCounts.threes, totalCounts.twos, totalCounts.ones, totalCounts.zeros];
|
||||||
|
|
||||||
|
let mean = 0;
|
||||||
|
if (totalShots > 0) {
|
||||||
|
mean = shotValues.reduce((sum, val, i) => sum + (val * shotCounts[i]), 0) / totalShots;
|
||||||
|
}
|
||||||
|
|
||||||
|
let variance = 0;
|
||||||
|
if (totalShots > 0) {
|
||||||
|
variance = shotValues.reduce((sum, val, i) => sum + (Math.pow(val - mean, 2) * shotCounts[i]), 0) / totalShots;
|
||||||
|
}
|
||||||
|
const consistency = Math.max(0, Math.round(100 - (variance * 5))); // Scale variance to 0-100
|
||||||
|
|
||||||
|
// Update DOM with percentages
|
||||||
|
document.getElementById('accuracyPercentage').textContent = accuracyPercentage + '%';
|
||||||
|
document.getElementById('consistencyScore').textContent = consistency;
|
||||||
|
|
||||||
|
const gaugeData = [accuracyPercentage, 100 - accuracyPercentage];
|
||||||
|
|
||||||
|
accuracyGaugeChartInstance = new Chart(ctx, {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: ['Accuracy', 'Missed'],
|
||||||
|
datasets: [{
|
||||||
|
data: gaugeData,
|
||||||
|
backgroundColor: ['#28a745', '#f0f0f0'],
|
||||||
|
borderColor: ['#28a745', '#e0e0e0'],
|
||||||
|
borderWidth: 3,
|
||||||
|
circumference: 180,
|
||||||
|
rotation: 270
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
return context.label + ': ' + context.parsed + '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stat cards with performance metrics
|
||||||
|
function updateStatCards(statsData, totalCounts) {
|
||||||
|
try {
|
||||||
|
// Count tournaments
|
||||||
|
const tournaments = statsData.tournament_history ? statsData.tournament_history.length : 0;
|
||||||
|
document.getElementById('totalTournaments').textContent = tournaments;
|
||||||
|
|
||||||
|
// Count total 10s
|
||||||
|
const totalTens = totalCounts.tens || 0;
|
||||||
|
document.getElementById('totalTens').textContent = totalTens;
|
||||||
|
|
||||||
|
// Calculate variance
|
||||||
|
const shotValues = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
|
||||||
|
const shotCounts = [totalCounts.tens, totalCounts.nines, totalCounts.eights, totalCounts.sevens, totalCounts.sixes, totalCounts.fives, totalCounts.fours, totalCounts.threes, totalCounts.twos, totalCounts.ones, totalCounts.zeros];
|
||||||
|
const totalShots = shotCounts.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
let mean = 0;
|
||||||
|
if (totalShots > 0) {
|
||||||
|
mean = shotValues.reduce((sum, val, i) => sum + (val * shotCounts[i]), 0) / totalShots;
|
||||||
|
}
|
||||||
|
|
||||||
|
let variance = 0;
|
||||||
|
if (totalShots > 0) {
|
||||||
|
variance = shotValues.reduce((sum, val, i) => sum + (Math.pow(val - mean, 2) * shotCounts[i]), 0) / totalShots;
|
||||||
|
}
|
||||||
|
document.getElementById('scoreVariance').textContent = variance.toFixed(2);
|
||||||
|
|
||||||
|
// Calculate range (highest - lowest non-zero score)
|
||||||
|
let minScore = null;
|
||||||
|
let maxScore = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < shotValues.length; i++) {
|
||||||
|
if (shotCounts[i] > 0) {
|
||||||
|
if (maxScore === null) maxScore = shotValues[i];
|
||||||
|
minScore = shotValues[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const range = maxScore !== null ? (maxScore - minScore) : 0;
|
||||||
|
document.getElementById('scoreRange').textContent = range;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating stat cards:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load shot accuracy data from template context
|
// Load shot accuracy data from template context
|
||||||
function loadShotAccuracyData() {
|
function loadShotAccuracyData() {
|
||||||
try {
|
try {
|
||||||
@@ -1672,10 +2085,41 @@
|
|||||||
const avgScore = gameCount > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / gameCount) : 0;
|
const avgScore = gameCount > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / gameCount) : 0;
|
||||||
const bestScore = gameCount > 0 ? Math.max(...scores) : 0;
|
const bestScore = gameCount > 0 ? Math.max(...scores) : 0;
|
||||||
|
|
||||||
|
// Calculate most common shot value
|
||||||
|
let mostCommonShot = 0;
|
||||||
|
const shotCounts = { 10: 0, 9: 0, 8: 0, 7: 0, 6: 0, 5: 0, 4: 0, 3: 0, 2: 0, 1: 0, 0: 0 };
|
||||||
|
|
||||||
|
tournaments.forEach(tournament => {
|
||||||
|
if (tournament.shot_breakdown) {
|
||||||
|
const breakdown = tournament.shot_breakdown;
|
||||||
|
shotCounts[10] += breakdown.tens || 0;
|
||||||
|
shotCounts[9] += breakdown.nines || 0;
|
||||||
|
shotCounts[8] += breakdown.eights || 0;
|
||||||
|
shotCounts[7] += breakdown.sevens || 0;
|
||||||
|
shotCounts[6] += breakdown.sixes || 0;
|
||||||
|
shotCounts[5] += breakdown.fives || 0;
|
||||||
|
shotCounts[4] += breakdown.fours || 0;
|
||||||
|
shotCounts[3] += breakdown.threes || 0;
|
||||||
|
shotCounts[2] += breakdown.twos || 0;
|
||||||
|
shotCounts[1] += breakdown.ones || 0;
|
||||||
|
shotCounts[0] += breakdown.zeros || 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find shot value with highest count
|
||||||
|
let maxCount = 0;
|
||||||
|
for (const [score, count] of Object.entries(shotCounts)) {
|
||||||
|
if (count > maxCount) {
|
||||||
|
maxCount = count;
|
||||||
|
mostCommonShot = parseInt(score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update basic stats
|
// Update basic stats
|
||||||
document.getElementById('gameCount').textContent = gameCount;
|
document.getElementById('gameCount').textContent = gameCount;
|
||||||
document.getElementById('avgScore').textContent = avgScore;
|
|
||||||
document.getElementById('bestScore').textContent = bestScore;
|
document.getElementById('bestScore').textContent = bestScore;
|
||||||
|
document.getElementById('avgScore').textContent = avgScore;
|
||||||
|
document.getElementById('mostCommonShot').textContent = mostCommonShot;
|
||||||
|
|
||||||
// Update shot accuracy stats (if available in tournament data)
|
// Update shot accuracy stats (if available in tournament data)
|
||||||
updateAccuracyStats(tournaments);
|
updateAccuracyStats(tournaments);
|
||||||
@@ -1767,17 +2211,17 @@
|
|||||||
function generateColorfulArray() {
|
function generateColorfulArray() {
|
||||||
// Color mapping from green (perfect 10) to red (misses 0): labels are [10,9,8,7,6,5,4,3,2,1,0]
|
// Color mapping from green (perfect 10) to red (misses 0): labels are [10,9,8,7,6,5,4,3,2,1,0]
|
||||||
return [
|
return [
|
||||||
'#2E7D32', // 10 - green (perfect)
|
'#4A9D6F', // 10 - medium green (perfect)
|
||||||
'#388E3C', // 9 - green
|
'#5BA97A', // 9 - medium green
|
||||||
'#43A047', // 8 - green
|
'#6CB585', // 8 - medium green
|
||||||
'#558B2F', // 7 - light green
|
'#7DC285', // 7 - medium light green
|
||||||
'#9CCC65', // 6 - lime green
|
'#8FCC7F', // 6 - medium lime green
|
||||||
'#FDD835', // 5 - yellow
|
'#D4B84D', // 5 - medium yellow
|
||||||
'#FBC02D', // 4 - golden yellow
|
'#CDA642', // 4 - medium golden yellow
|
||||||
'#FFA726', // 3 - orange
|
'#D99A5D', // 3 - medium orange
|
||||||
'#FF7043', // 2 - light orange-red
|
'#D87A6C', // 2 - medium light orange-red
|
||||||
'#E53935', // 1 - red
|
'#C85C5C', // 1 - medium red
|
||||||
'#C62828' // 0 - dark red (miss)
|
'#B84A4A' // 0 - medium dark red (miss)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,9 @@ def analyze_player_performance(player_id, archives_data):
|
|||||||
'best_tournament_score': 0,
|
'best_tournament_score': 0,
|
||||||
'worst_tournament_score': float('inf'),
|
'worst_tournament_score': float('inf'),
|
||||||
'average_tournament_score': 0,
|
'average_tournament_score': 0,
|
||||||
|
'best_4_targets_score': 0,
|
||||||
|
'best_20_targets_score': 0,
|
||||||
|
'best_40_targets_score': 0,
|
||||||
'total_shots_fired': 0,
|
'total_shots_fired': 0,
|
||||||
'performance_trend': [],
|
'performance_trend': [],
|
||||||
'tournament_history': [],
|
'tournament_history': [],
|
||||||
@@ -264,6 +267,17 @@ def analyze_player_performance(player_id, archives_data):
|
|||||||
|
|
||||||
player_stats['total_shots_fired'] += shots_in_tournament
|
player_stats['total_shots_fired'] += shots_in_tournament
|
||||||
|
|
||||||
|
# Track format-specific best scores
|
||||||
|
if tournament_type == '4_targets':
|
||||||
|
if score > player_stats['best_4_targets_score']:
|
||||||
|
player_stats['best_4_targets_score'] = score
|
||||||
|
elif tournament_type == '20_targets':
|
||||||
|
if score > player_stats['best_20_targets_score']:
|
||||||
|
player_stats['best_20_targets_score'] = score
|
||||||
|
elif tournament_type == '40_targets':
|
||||||
|
if score > player_stats['best_40_targets_score']:
|
||||||
|
player_stats['best_40_targets_score'] = score
|
||||||
|
|
||||||
# Calculate and aggregate shot accuracy
|
# Calculate and aggregate shot accuracy
|
||||||
if tournament_type in player_stats['shot_accuracy']:
|
if tournament_type in player_stats['shot_accuracy']:
|
||||||
shot_accuracy = calculate_shot_accuracy(participant)
|
shot_accuracy = calculate_shot_accuracy(participant)
|
||||||
|
|||||||
Reference in New Issue
Block a user