Spaces:
Sleeping
Sleeping
| document.addEventListener('DOMContentLoaded', () => { | |
| const configSelect = document.getElementById('config-select'); | |
| const tbody = document.getElementById('metrics-tbody'); | |
| const tfoot = document.getElementById('metrics-tfoot'); | |
| const instancesList = document.getElementById('instances-list'); | |
| const instancesTitle = document.getElementById('instances-title'); | |
| let currentConfig = null; | |
| let selectedCell = null; | |
| // Initialize | |
| fetchConfigs(); | |
| configSelect.addEventListener('change', (e) => { | |
| currentConfig = e.target.value; | |
| loadMetrics(currentConfig); | |
| clearInstances(); | |
| }); | |
| async function fetchConfigs() { | |
| try { | |
| const res = await fetch('/api/configs'); | |
| const data = await res.json(); | |
| configSelect.innerHTML = ''; | |
| if (data.configs.length === 0) { | |
| const opt = document.createElement('option'); | |
| opt.text = 'Aucune configuration trouvée'; | |
| opt.disabled = true; | |
| configSelect.appendChild(opt); | |
| return; | |
| } | |
| data.configs.forEach((conf, idx) => { | |
| const opt = document.createElement('option'); | |
| opt.value = conf; | |
| opt.text = conf; | |
| if (idx === 0) { | |
| opt.selected = true; | |
| currentConfig = conf; | |
| } | |
| configSelect.appendChild(opt); | |
| }); | |
| if (currentConfig) { | |
| loadMetrics(currentConfig); | |
| } | |
| } catch (err) { | |
| console.error('Failed to fetch configs', err); | |
| configSelect.innerHTML = '<option disabled selected>Erreur de chargement</option>'; | |
| } | |
| } | |
| async function loadMetrics(configName) { | |
| try { | |
| const res = await fetch(`/api/configs/${encodeURIComponent(configName)}/metrics`); | |
| const data = await res.json(); | |
| renderTable(data.metrics, data.macro_f1); | |
| } catch (err) { | |
| console.error('Failed to load metrics', err); | |
| tbody.innerHTML = '<tr><td colspan="8">Erreur lors du chargement des données.</td></tr>'; | |
| } | |
| } | |
| function getMetricColor(value) { | |
| if (value >= 0.8) return 'var(--metric-good)'; | |
| if (value >= 0.5) return 'var(--metric-ok)'; | |
| return 'var(--metric-bad)'; | |
| } | |
| function renderTable(metrics, macroF1) { | |
| tbody.innerHTML = ''; | |
| metrics.forEach(m => { | |
| const tr = document.createElement('tr'); | |
| const f1Color = getMetricColor(m.f1); | |
| const precColor = getMetricColor(m.precision); | |
| const recColor = getMetricColor(m.recall); | |
| tr.innerHTML = ` | |
| <td>${escapeHtml(m.display_name)}</td> | |
| <td style="color: ${f1Color}">${m.f1.toFixed(3)}</td> | |
| <td style="color: ${precColor}">${m.precision.toFixed(3)}</td> | |
| <td style="color: ${recColor}">${m.recall.toFixed(3)}</td> | |
| <td class="cell-clickable" data-label="${escapeHtml(m.label)}" data-outcome="fn">${m.fn}</td> | |
| <td class="cell-clickable" data-label="${escapeHtml(m.label)}" data-outcome="fp">${m.fp}</td> | |
| <td class="cell-clickable" data-label="${escapeHtml(m.label)}" data-outcome="tn">${m.tn}</td> | |
| <td class="cell-clickable" data-label="${escapeHtml(m.label)}" data-outcome="tp">${m.tp}</td> | |
| `; | |
| tbody.appendChild(tr); | |
| }); | |
| const macroColor = getMetricColor(macroF1); | |
| tfoot.innerHTML = ` | |
| <tr> | |
| <td style="font-style:italic;">Macro avg</td> | |
| <td style="color: ${macroColor}">${macroF1.toFixed(3)}</td> | |
| <td></td> | |
| <td></td> | |
| <td></td> | |
| <td></td> | |
| <td></td> | |
| <td></td> | |
| </tr> | |
| `; | |
| // Attach event listeners to clickable cells | |
| const clickableCells = tbody.querySelectorAll('.cell-clickable'); | |
| clickableCells.forEach(cell => { | |
| cell.addEventListener('click', onCellClick); | |
| }); | |
| } | |
| async function onCellClick(e) { | |
| if (!currentConfig) return; | |
| const cell = e.currentTarget; | |
| const label = cell.getAttribute('data-label'); | |
| const outcome = cell.getAttribute('data-outcome'); | |
| // Manage selection state | |
| if (selectedCell) { | |
| selectedCell.classList.remove('cell-selected'); | |
| } | |
| selectedCell = cell; | |
| selectedCell.classList.add('cell-selected'); | |
| try { | |
| const res = await fetch(`/api/configs/${encodeURIComponent(currentConfig)}/instances?label=${encodeURIComponent(label)}&outcome=${encodeURIComponent(outcome)}`); | |
| const data = await res.json(); | |
| instancesTitle.textContent = data.title; | |
| instancesList.innerHTML = ''; | |
| if (data.texts && data.texts.length > 0) { | |
| data.texts.forEach(text => { | |
| const div = document.createElement('div'); | |
| div.className = 'instance-item'; | |
| div.textContent = text; | |
| instancesList.appendChild(div); | |
| }); | |
| } else { | |
| instancesList.innerHTML = '<div class="instances-empty">Aucune phrase à afficher.</div>'; | |
| } | |
| } catch (err) { | |
| console.error('Failed to load instances', err); | |
| instancesTitle.textContent = "Erreur de chargement"; | |
| instancesList.innerHTML = '<div class="instances-empty">Impossible de charger les instances.</div>'; | |
| } | |
| } | |
| function clearInstances() { | |
| instancesTitle.textContent = "Cliquez sur une cellule pour voir les instances"; | |
| instancesList.innerHTML = '<div class="instances-empty">Aucune phrase à afficher.</div>'; | |
| if (selectedCell) { | |
| selectedCell.classList.remove('cell-selected'); | |
| selectedCell = null; | |
| } | |
| } | |
| function escapeHtml(unsafe) { | |
| return (unsafe || '').toString() | |
| .replace(/&/g, "&") | |
| .replace(/</g, "<") | |
| .replace(/>/g, ">") | |
| .replace(/"/g, """) | |
| .replace(/'/g, "'"); | |
| } | |
| }); | |