add radar graph
This commit is contained in:
@@ -704,7 +704,7 @@ class WebInterface:
|
||||
.scale-toggle {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 140px;
|
||||
right: 180px;
|
||||
background: var(--border-color);
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
.scale-toggle {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 140px;
|
||||
right: 180px;
|
||||
background: var(--border-color);
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
@@ -408,6 +408,92 @@
|
||||
.tooltiptext strong {
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.chart-type-toggle {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
background: var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.chart-type-btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
transition: all 0.3s;
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.chart-type-btn:hover {
|
||||
background: rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
.chart-type-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.model-toggles {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background: var(--stat-card-bg);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.model-toggle-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 12px;
|
||||
background: var(--card-bg);
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.model-toggle-item:hover {
|
||||
border-color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.model-toggle-item.active {
|
||||
border-color: currentColor;
|
||||
}
|
||||
|
||||
.model-toggle-checkbox {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: #667eea;
|
||||
}
|
||||
|
||||
.model-toggle-label {
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.model-color-indicator {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.radar-controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.radar-controls.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -473,6 +559,16 @@
|
||||
<select id="categorySelect" onchange="updateCategoryChart()">
|
||||
<option value="">Loading categories...</option>
|
||||
</select>
|
||||
<div class="chart-type-toggle">
|
||||
<button class="chart-type-btn active" onclick="setCategoryChartType('bar')" id="barChartBtn">📊 Bar Chart</button>
|
||||
<button class="chart-type-btn" onclick="setCategoryChartType('radar')" id="radarChartBtn">🕸️ Spider Web</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="radar-controls" id="radarModelToggles">
|
||||
<p style="margin-bottom: 10px; color: var(--text-secondary); font-size: 0.9em;">Toggle models to show/hide on the spider web chart:</p>
|
||||
<div class="model-toggles" id="modelToggleContainer">
|
||||
<!-- Model toggles will be populated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<canvas id="categoryChart"></canvas>
|
||||
@@ -924,13 +1020,91 @@
|
||||
});
|
||||
|
||||
if (comparisonData.categories.length > 0) {
|
||||
initializeModelToggles();
|
||||
updateCategoryChart();
|
||||
}
|
||||
}
|
||||
|
||||
// Category chart type state
|
||||
let categoryChartType = 'bar';
|
||||
let selectedModelsForRadar = {};
|
||||
|
||||
// Model colors for radar chart
|
||||
const modelColors = [
|
||||
{ bg: 'rgba(102, 126, 234, 0.3)', border: 'rgba(102, 126, 234, 1)' },
|
||||
{ bg: 'rgba(118, 75, 162, 0.3)', border: 'rgba(118, 75, 162, 1)' },
|
||||
{ bg: 'rgba(16, 185, 129, 0.3)', border: 'rgba(16, 185, 129, 1)' },
|
||||
{ bg: 'rgba(245, 158, 11, 0.3)', border: 'rgba(245, 158, 11, 1)' },
|
||||
{ bg: 'rgba(239, 68, 68, 0.3)', border: 'rgba(239, 68, 68, 1)' },
|
||||
{ bg: 'rgba(59, 130, 246, 0.3)', border: 'rgba(59, 130, 246, 1)' },
|
||||
{ bg: 'rgba(236, 72, 153, 0.3)', border: 'rgba(236, 72, 153, 1)' },
|
||||
{ bg: 'rgba(34, 197, 94, 0.3)', border: 'rgba(34, 197, 94, 1)' },
|
||||
{ bg: 'rgba(168, 85, 247, 0.3)', border: 'rgba(168, 85, 247, 1)' },
|
||||
{ bg: 'rgba(251, 146, 60, 0.3)', border: 'rgba(251, 146, 60, 1)' }
|
||||
];
|
||||
|
||||
function initializeModelToggles() {
|
||||
if (!comparisonData) return;
|
||||
|
||||
const models = Object.keys(comparisonData.models);
|
||||
const container = document.getElementById('modelToggleContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
models.forEach((model, index) => {
|
||||
const colorIndex = index % modelColors.length;
|
||||
selectedModelsForRadar[model] = true; // All selected by default
|
||||
|
||||
const item = document.createElement('label');
|
||||
item.className = 'model-toggle-item active';
|
||||
item.style.color = modelColors[colorIndex].border;
|
||||
item.innerHTML = `
|
||||
<span class="model-color-indicator" style="background: ${modelColors[colorIndex].border}"></span>
|
||||
<input type="checkbox" class="model-toggle-checkbox" checked
|
||||
onchange="toggleModelVisibility('${model}', this.checked, this.parentElement)">
|
||||
<span class="model-toggle-label">${model}</span>
|
||||
`;
|
||||
container.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleModelVisibility(model, isVisible, element) {
|
||||
selectedModelsForRadar[model] = isVisible;
|
||||
element.classList.toggle('active', isVisible);
|
||||
updateCategoryChart();
|
||||
}
|
||||
|
||||
function setCategoryChartType(type) {
|
||||
categoryChartType = type;
|
||||
|
||||
// Update button states
|
||||
document.getElementById('barChartBtn').classList.toggle('active', type === 'bar');
|
||||
document.getElementById('radarChartBtn').classList.toggle('active', type === 'radar');
|
||||
|
||||
// Show/hide radar controls
|
||||
document.getElementById('radarModelToggles').classList.toggle('visible', type === 'radar');
|
||||
|
||||
// Show/hide category selector (hide for radar since it shows all categories)
|
||||
document.getElementById('categorySelect').style.display = type === 'radar' ? 'none' : 'block';
|
||||
|
||||
updateCategoryChart();
|
||||
}
|
||||
|
||||
function updateCategoryChart() {
|
||||
if (!comparisonData) return;
|
||||
|
||||
const ctx = document.getElementById('categoryChart');
|
||||
if (window.categoryChartInstance) {
|
||||
window.categoryChartInstance.destroy();
|
||||
}
|
||||
|
||||
if (categoryChartType === 'radar') {
|
||||
updateRadarChart(ctx);
|
||||
} else {
|
||||
updateBarChart(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
function updateBarChart(ctx) {
|
||||
const category = document.getElementById('categorySelect').value;
|
||||
const models = Object.keys(comparisonData.models);
|
||||
|
||||
@@ -939,11 +1113,6 @@
|
||||
return stats ? stats.average : 0;
|
||||
});
|
||||
|
||||
const ctx = document.getElementById('categoryChart');
|
||||
if (window.categoryChartInstance) {
|
||||
window.categoryChartInstance.destroy();
|
||||
}
|
||||
|
||||
window.categoryChartInstance = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
@@ -964,6 +1133,92 @@
|
||||
});
|
||||
}
|
||||
|
||||
function updateRadarChart(ctx) {
|
||||
const categories = comparisonData.categories;
|
||||
const models = Object.keys(comparisonData.models);
|
||||
|
||||
// Create datasets for each selected model
|
||||
const datasets = [];
|
||||
let allData = [];
|
||||
|
||||
models.forEach((model, index) => {
|
||||
if (!selectedModelsForRadar[model]) return;
|
||||
|
||||
const colorIndex = index % modelColors.length;
|
||||
const data = categories.map(category => {
|
||||
const stats = comparisonData.models[model].category_stats[category];
|
||||
return stats ? stats.average : 0;
|
||||
});
|
||||
|
||||
allData = allData.concat(data);
|
||||
|
||||
datasets.push({
|
||||
label: model,
|
||||
data: data,
|
||||
backgroundColor: modelColors[colorIndex].bg,
|
||||
borderColor: modelColors[colorIndex].border,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: modelColors[colorIndex].border,
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: modelColors[colorIndex].border,
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6
|
||||
});
|
||||
});
|
||||
|
||||
window.categoryChartInstance = new Chart(ctx, {
|
||||
type: 'radar',
|
||||
data: {
|
||||
labels: categories,
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
labels: {
|
||||
padding: 20,
|
||||
usePointStyle: true,
|
||||
pointStyle: 'circle'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return `${context.dataset.label}: ${context.raw.toFixed(2)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
r: {
|
||||
beginAtZero: !zoomedScale,
|
||||
min: zoomedScale ? Math.max(0, Math.min(...allData) - 0.5) : 0,
|
||||
max: zoomedScale ? Math.min(5, Math.max(...allData) + 0.5) : 5,
|
||||
ticks: {
|
||||
stepSize: zoomedScale ? 0.5 : 1,
|
||||
backdropColor: 'transparent'
|
||||
},
|
||||
grid: {
|
||||
color: 'rgba(102, 126, 234, 0.2)'
|
||||
},
|
||||
angleLines: {
|
||||
color: 'rgba(102, 126, 234, 0.2)'
|
||||
},
|
||||
pointLabels: {
|
||||
font: {
|
||||
size: 11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadModelDetails() {
|
||||
const modelName = document.getElementById('modelSelect').value;
|
||||
if (!modelName || !comparisonData) return;
|
||||
|
||||
Reference in New Issue
Block a user