RafaelJaime's picture
Update src/App.js
12b937b verified
import React, { useState, useEffect, useMemo } from 'react';
const UrologyLeaderboard = () => {
const [searchTerm, setSearchTerm] = useState('');
const [filterLicense, setFilterLicense] = useState('all');
const [sortBy, setSortBy] = useState('accuracy');
const [sortOrder, setSortOrder] = useState('desc');
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [lastUpdated, setLastUpdated] = useState(null);
const [error, setError] = useState(null);
const loadData = async () => {
try {
const response = await fetch('https://datasets-server.huggingface.co/rows?dataset=SASLeaderboard/results&config=default&split=train');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log('Raw HuggingFace data:', data);
if (!data.rows || data.rows.length === 0) {
throw new Error('No data found in the dataset');
}
// Agrupar por modelo y calcular estadísticas
const modelStats = {};
data.rows.forEach(row => {
const rowData = row.row;
const model = rowData.model;
const isCorrect = rowData.is_correct;
const timestamp = rowData.timestamp;
if (!modelStats[model]) {
modelStats[model] = {
model: model,
baseModel: model.split('/')[1] || model,
totalQuestions: 0,
correctAnswers: 0,
submittedTime: timestamp,
license: "Unknown",
submitType: "unknown",
params: 0,
precision: "unknown",
status: "FINISHED"
};
}
modelStats[model].totalQuestions++;
if (isCorrect) {
modelStats[model].correctAnswers++;
}
// Mantener el timestamp más reciente
if (new Date(timestamp) > new Date(modelStats[model].submittedTime)) {
modelStats[model].submittedTime = timestamp;
}
});
// Convertir a array y calcular accuracy
const processedData = Object.values(modelStats).map(stats => ({
...stats,
accuracy: stats.totalQuestions > 0 ? stats.correctAnswers / stats.totalQuestions : 0
}));
console.log('Processed data:', processedData);
return processedData;
} catch (error) {
console.error('Error loading data from HuggingFace:', error);
throw error;
}
};
const refreshData = async () => {
setRefreshing(true);
setError(null);
try {
const newData = await loadData();
setData(newData);
setLastUpdated(new Date());
} catch (error) {
setError(`Failed to load data: ${error.message}`);
setData([]);
} finally {
setRefreshing(false);
}
};
useEffect(() => {
const initializeData = async () => {
try {
const initialData = await loadData();
setData(initialData);
setLastUpdated(new Date());
} catch (error) {
setError(`Failed to load data: ${error.message}`);
setData([]);
} finally {
setLoading(false);
}
};
initializeData();
}, []);
const filteredAndSortedData = useMemo(() => {
// Si hay error, no procesar datos
if (error) {
return [];
}
let filtered = data.filter(item => {
const matchesSearch = item.model.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.baseModel.toLowerCase().includes(searchTerm.toLowerCase());
const matchesFilter = filterLicense === 'all' || item.license === filterLicense;
return matchesSearch && matchesFilter;
});
return filtered.sort((a, b) => {
let aValue = a[sortBy];
let bValue = b[sortBy];
if (sortBy === 'submittedTime') {
aValue = new Date(aValue);
bValue = new Date(bValue);
}
if (sortOrder === 'desc') {
return bValue > aValue ? 1 : -1;
} else {
return aValue > bValue ? 1 : -1;
}
});
}, [data, searchTerm, filterLicense, sortBy, sortOrder, error]);
const getRankIcon = (index) => {
switch(index) {
case 0: return React.createElement('span', { style: { fontSize: '24px' } }, '🏆');
case 1: return React.createElement('span', { style: { fontSize: '24px' } }, '🥈');
case 2: return React.createElement('span', { style: { fontSize: '24px' } }, '🥉');
default: return React.createElement('span', {
style: { fontSize: '20px', fontWeight: 'bold', color: '#9ca3af' }
}, `#${index + 1}`);
}
};
const formatAccuracy = (accuracy) => {
return `${(accuracy * 100).toFixed(2)}%`;
};
const formatParams = (params) => {
if (params === 0) return 'API';
if (params >= 1e9) return `${(params / 1e9).toFixed(1)}B`;
if (params >= 1e6) return `${(params / 1e6).toFixed(1)}M`;
return params.toLocaleString();
};
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString('en-US', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
const getSortIcon = (column) => {
if (sortBy !== column) {
return React.createElement('span', { style: { color: '#9ca3af' } }, '↕️');
}
return sortOrder === 'desc' ?
React.createElement('span', { style: { color: '#3b82f6' } }, '⬇️') :
React.createElement('span', { style: { color: '#3b82f6' } }, '⬆️');
};
const styles = {
container: {
minHeight: '100vh',
background: 'linear-gradient(135deg, #0f172a 0%, #1e3a8a 50%, #3730a3 100%)',
padding: '32px 16px'
},
innerContainer: {
maxWidth: '1200px',
margin: '0 auto'
},
header: {
textAlign: 'center',
marginBottom: '32px'
},
title: {
fontSize: '48px',
fontWeight: 'bold',
color: 'white',
marginBottom: '16px',
background: 'linear-gradient(to right, #60a5fa, #a78bfa)',
WebkitBackgroundClip: 'text',
backgroundClip: 'text',
color: 'transparent'
},
subtitle: {
fontSize: '20px',
color: '#d1d5db',
marginBottom: '24px'
},
statsContainer: {
display: 'flex',
justifyContent: 'center',
gap: '32px',
textAlign: 'center',
flexWrap: 'wrap'
},
statCard: {
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: '12px',
padding: '16px'
},
statNumber: {
fontSize: '24px',
fontWeight: 'bold',
color: 'white'
},
statLabel: {
fontSize: '14px',
color: '#d1d5db'
},
controlsContainer: {
marginBottom: '24px',
display: 'flex',
flexWrap: 'wrap',
gap: '16px',
alignItems: 'center',
justifyContent: 'space-between'
},
inputGroup: {
display: 'flex',
flexWrap: 'wrap',
gap: '16px',
alignItems: 'center'
},
inputContainer: {
position: 'relative'
},
inputIcon: {
position: 'absolute',
left: '12px',
top: '50%',
transform: 'translateY(-50%)',
color: '#9ca3af'
},
input: {
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: '8px',
color: 'white',
padding: '8px 12px 8px 40px',
outline: 'none',
fontSize: '14px'
},
select: {
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: '8px',
color: 'white',
padding: '8px 32px 8px 40px',
outline: 'none',
fontSize: '14px'
},
updateButton: {
background: '#059669',
color: 'white',
border: 'none',
borderRadius: '8px',
padding: '8px 16px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '8px',
fontSize: '14px'
},
tableCard: {
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: '12px',
overflow: 'hidden'
},
tableContainer: {
overflowX: 'auto'
},
scrollContainer: {
maxHeight: '384px',
overflowY: 'auto'
},
table: {
width: '100%',
borderCollapse: 'collapse'
},
tableHeader: {
position: 'sticky',
top: 0,
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
borderBottom: '1px solid rgba(255, 255, 255, 0.1)'
},
th: {
padding: '16px 24px',
textAlign: 'left',
color: 'white',
fontWeight: '600'
},
thClickable: {
padding: '16px 24px',
textAlign: 'left',
color: 'white',
fontWeight: '600',
cursor: 'pointer'
},
tr: {
borderBottom: '1px solid rgba(255, 255, 255, 0.05)'
},
td: {
padding: '16px 24px'
},
progressBar: {
width: '100%',
background: '#374151',
borderRadius: '4px',
height: '8px'
},
progressFill: {
background: 'linear-gradient(to right, #3b82f6, #8b5cf6)',
height: '8px',
borderRadius: '4px',
transition: 'width 0.5s ease'
},
badge: {
padding: '4px 8px',
borderRadius: '12px',
fontSize: '12px',
fontWeight: '500'
},
badgeBlue: {
background: 'rgba(59, 130, 246, 0.2)',
color: '#93c5fd'
},
badgeGreen: {
background: 'rgba(34, 197, 94, 0.2)',
color: '#86efac'
},
badgePurple: {
background: 'rgba(147, 51, 234, 0.2)',
color: '#c4b5fd'
},
infoSection: {
marginTop: '32px',
display: 'flex',
flexDirection: 'column',
gap: '24px'
},
infoCard: {
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: '12px',
padding: '24px'
},
academicCard: {
background: 'rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: '12px',
padding: '16px',
textAlign: 'center'
},
loadingContainer: {
minHeight: '100vh',
background: 'linear-gradient(135deg, #0f172a 0%, #1e3a8a 50%, #3730a3 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
spinner: {
width: '64px',
height: '64px',
border: '2px solid transparent',
borderBottom: '2px solid white',
borderRadius: '50%',
animation: 'spin 1s linear infinite',
margin: '0 auto 16px'
}
};
if (loading) {
return React.createElement('div', { style: styles.loadingContainer },
React.createElement('div', { style: { textAlign: 'center' } },
React.createElement('div', { style: styles.spinner }),
React.createElement('p', { style: { color: 'white', fontSize: '20px' } }, 'Loading leaderboard...')
)
);
}
return React.createElement('div', null,
React.createElement('style', null, `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.rotate {
animation: spin 1s linear infinite;
}
.hover-row:hover {
background: rgba(255, 255, 255, 0.05);
}
input::placeholder {
color: #9ca3af;
}
select option {
background: #1f2937;
color: white;
}
`),
React.createElement('div', { style: styles.container },
React.createElement('div', { style: styles.innerContainer },
React.createElement('div', { style: styles.header },
React.createElement('h1', { style: styles.title }, '🏆 Urology AI Leaderboard'),
React.createElement('p', { style: styles.subtitle }, 'Evaluating Natural Language Models on Urology Knowledge Assessment'),
React.createElement('div', { style: styles.statsContainer },
React.createElement('div', { style: styles.statCard },
React.createElement('div', { style: styles.statNumber }, data.length),
React.createElement('div', { style: styles.statLabel }, 'Models')
),
React.createElement('div', { style: styles.statCard },
React.createElement('div', { style: styles.statNumber }, '151'),
React.createElement('div', { style: styles.statLabel }, 'Questions')
),
React.createElement('div', { style: styles.statCard },
React.createElement('div', { style: styles.statNumber }, 'SAS Urology'),
React.createElement('div', { style: styles.statLabel }, 'Specialty')
)
)
),
React.createElement('div', { style: styles.controlsContainer },
React.createElement('div', { style: styles.inputGroup },
React.createElement('div', { style: styles.inputContainer },
React.createElement('span', { style: styles.inputIcon }, '🔍'),
React.createElement('input', {
type: 'text',
placeholder: 'Search model...',
value: searchTerm,
onChange: (e) => setSearchTerm(e.target.value),
style: styles.input
})
),
React.createElement('div', { style: styles.inputContainer },
React.createElement('span', { style: styles.inputIcon }, '📋'),
React.createElement('select', {
value: filterLicense,
onChange: (e) => setFilterLicense(e.target.value),
style: styles.select
},
React.createElement('option', { value: 'all' }, 'All licenses'),
React.createElement('option', { value: 'API Service' }, 'API Service'),
React.createElement('option', { value: 'Apache 2.0' }, 'Apache 2.0'),
React.createElement('option', { value: 'MIT' }, 'MIT')
)
)
),
React.createElement('button', {
onClick: refreshData,
disabled: refreshing,
style: { ...styles.updateButton, opacity: refreshing ? 0.5 : 1 }
},
React.createElement('span', { className: refreshing ? 'rotate' : '' }, '🔄'),
React.createElement('span', null, refreshing ? 'Updating...' : 'Update')
)
),
React.createElement('div', { style: styles.tableCard },
React.createElement('div', { style: styles.tableContainer },
React.createElement('div', { style: styles.scrollContainer },
React.createElement('table', { style: styles.table },
React.createElement('thead', { style: styles.tableHeader },
React.createElement('tr', null,
React.createElement('th', { style: styles.th }, 'Rank'),
React.createElement('th', { style: styles.th }, 'Model'),
React.createElement('th', {
style: styles.thClickable,
onClick: () => {
if (sortBy === 'accuracy') {
setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc');
} else {
setSortBy('accuracy');
setSortOrder('desc');
}
}
},
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '4px' } },
React.createElement('span', null, '🎯'),
React.createElement('span', null, 'Accuracy'),
getSortIcon('accuracy')
)
),
React.createElement('th', { style: styles.th }, 'Answers'),
React.createElement('th', { style: styles.th }, 'Parameters'),
React.createElement('th', { style: styles.th }, 'License'),
React.createElement('th', {
style: styles.thClickable,
onClick: () => {
if (sortBy === 'submittedTime') {
setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc');
} else {
setSortBy('submittedTime');
setSortOrder('desc');
}
}
},
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '4px' } },
React.createElement('span', null, '🕒'),
React.createElement('span', null, 'Date'),
getSortIcon('submittedTime')
)
)
)
),
React.createElement('tbody', null,
filteredAndSortedData.map((item, index) =>
React.createElement('tr', {
key: item.model,
style: styles.tr,
className: 'hover-row'
},
React.createElement('td', { style: styles.td }, getRankIcon(index)),
React.createElement('td', { style: styles.td },
React.createElement('div', { style: { display: 'flex', flexDirection: 'column' } },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
React.createElement('span', { style: { color: 'white', fontWeight: '500' } }, item.model.split('/').pop()),
React.createElement('span', { style: { color: '#9ca3af', cursor: 'pointer' } }, '🔗')
),
React.createElement('span', { style: { fontSize: '14px', color: '#9ca3af' } }, item.model.split('/')[0])
)
),
React.createElement('td', { style: styles.td },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '12px' } },
React.createElement('div', { style: { flex: 1 } },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '4px' } },
React.createElement('span', { style: { color: 'white', fontWeight: '600' } }, formatAccuracy(item.accuracy))
),
React.createElement('div', { style: styles.progressBar },
React.createElement('div', {
style: { ...styles.progressFill, width: `${item.accuracy * 100}%` }
})
)
)
)
),
React.createElement('td', { style: styles.td },
React.createElement('div', { style: { textAlign: 'center' } },
React.createElement('div', { style: { color: 'white', fontWeight: '600' } }, `${item.correctAnswers}/${item.totalQuestions}`),
React.createElement('div', { style: { fontSize: '14px', color: '#9ca3af' } }, 'correct')
)
),
React.createElement('td', { style: styles.td },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
React.createElement('span', null, '⚡'),
React.createElement('span', { style: { color: 'white' } }, formatParams(item.params))
)
),
React.createElement('td', { style: styles.td },
React.createElement('span', {
style: {
...styles.badge,
...(item.license === 'API Service' ? styles.badgeBlue :
item.license === 'Apache 2.0' ? styles.badgeGreen : styles.badgePurple)
}
}, item.license)
),
React.createElement('td', { style: styles.td },
React.createElement('div', { style: { fontSize: '14px', color: '#d1d5db' } }, formatDate(item.submittedTime))
)
)
)
)
)
)
)
),
React.createElement('div', { style: styles.infoSection },
React.createElement('div', { style: styles.infoCard },
React.createElement('h3', { style: { fontSize: '20px', fontWeight: '600', color: 'white', marginBottom: '12px' } }, '📊 About This Evaluation'),
React.createElement('p', { style: { color: '#d1d5db', marginBottom: '12px' } }, 'This leaderboard evaluates natural language models on their ability to answer urology questions. Models must respond to multiple-choice questions about urological knowledge, demonstrating their understanding and mastery of this medical specialty.'),
React.createElement('p', { style: { color: '#d1d5db', marginBottom: '16px' } },
'Questions are from the ',
React.createElement('a', {
href: 'https://www.sspa.juntadeandalucia.es/servicioandaluzdesalud/',
target: '_blank',
rel: 'noopener noreferrer',
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' }
}, 'SAS (Servicio Andaluz de Salud)'),
' for the ',
React.createElement('a', {
href: 'https://www.sspa.juntadeandalucia.es/servicioandaluzdesalud/profesionales/ofertas-de-empleo/oferta-de-empleo-publico-puestos-base/oep-extraordinaria-decreto-ley-122022-centros-sas/cuadro-de-evolucion-concurso-oposicion-centros-sas/fea-urologia',
target: '_blank',
rel: 'noopener noreferrer',
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' }
}, React.createElement('strong', null, 'Convocatoria Concurso Oposición')),
' - specialized medical examination for urology residents.'
),
React.createElement('div', { style: { display: 'flex', justifyContent: 'center', color: '#d1d5db' } },
React.createElement('span', null, 'Dataset: ', React.createElement('a', {
href: 'https://huggingface.co/datasets/SASLeaderboard/results',
style: { color: '#60a5fa', textDecoration: 'none' },
target: '_blank',
rel: 'noopener noreferrer'
}, 'SASLeaderboard/results'))
)
),
React.createElement('div', { style: styles.academicCard },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '20px', flexWrap: 'wrap' } },
React.createElement('a', {
href: 'https://iesrafaelalberti.es/',
target: '_blank',
rel: 'noopener noreferrer',
style: { textDecoration: 'none', flexShrink: 0 }
},
React.createElement('img', {
src: 'https://avatars.githubusercontent.com/u/79144080?s=200&v=4',
alt: 'IES Rafael Alberti Logo',
style: {
width: '80px',
height: '80px',
cursor: 'pointer',
background: 'white',
borderRadius: '8px',
padding: '4px'
}
})
),
React.createElement('div', { style: { textAlign: 'center', flex: 1, minWidth: '200px' } },
React.createElement('h4', { style: { fontSize: '18px', fontWeight: '600', color: 'white', marginBottom: '16px', margin: '0 0 16px 0' } }, '🎓 Academic Project'),
React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '8px' } },
React.createElement('p', { style: { color: '#d1d5db', fontSize: '14px', margin: 0 } },
'Final project for ',
React.createElement('a', {
href: 'https://iesrafaelalberti.es/',
target: '_blank',
rel: 'noopener noreferrer',
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' }
}, 'IES Rafael Alberti')
),
React.createElement('p', { style: { color: '#d1d5db', fontSize: '14px', margin: 0 } },
'Course: ',
React.createElement('a', {
href: 'https://iesrafaelalberti.es/curso-especializacion-inteligencia-artificial-y-big-datatarde/',
target: '_blank',
rel: 'noopener noreferrer',
style: { color: '#60a5fa', textDecoration: 'none', fontWeight: '600' }
}, 'Artificial Intelligence and Big Data')
),
React.createElement('p', { style: { color: '#d1d5db', fontSize: '14px', margin: 0 } },
'Subject: Artificial Intelligence Models'
)
)
),
React.createElement('a', {
href: 'https://iesrafaelalberti.es/curso-especializacion-inteligencia-artificial-y-big-datatarde/',
target: '_blank',
rel: 'noopener noreferrer',
style: { textDecoration: 'none', flexShrink: 0 }
},
React.createElement('img', {
src: '/IA-Big-Data-cuadrado.png',
alt: 'AI and Big Data Course Logo',
style: {
width: '80px',
height: '80px',
cursor: 'pointer',
background: 'white',
borderRadius: '8px',
padding: '4px'
},
onError: (e) => {
console.log('Error loading course image from public folder');
},
onLoad: () => {
console.log('Course image loaded successfully from public folder');
}
})
)
)
)
)
)
)
);
};
export default UrologyLeaderboard;