from typing import Dict, List, Optional
import pandas as pd
def dataframe_to_html(
df: pd.DataFrame,
column_label_map: Optional[Dict[str, str]] = None,
dimension_metrics: Optional[List[str]] = None,
primary_metric: Optional[str] = None,
) -> str:
if df.empty:
return "
No data available
"
column_label_map = column_label_map or {}
dimension_metrics = dimension_metrics or []
headers = df.columns.tolist()
html = """
"""
for header in headers:
display_header = column_label_map.get(header, header)
if header == "Model":
html += f''
elif header == "entry_type":
html += f''
elif header == "Rank":
html += f''
elif primary_metric and header == primary_metric:
html += f'| {display_header} | '
elif header in dimension_metrics:
html += f''
else:
html += f"{display_header} | "
html += """
"""
for row_idx, (_, row) in enumerate(df.iterrows()):
row_class = "even-row" if row_idx % 2 == 0 else "odd-row"
html += f''
for col in headers:
cell_value = row[col]
if col == "Model":
cell_class = "model-cell"
elif col == "entry_type":
cell_class = "entry-type-cell"
elif col == "Rank":
cell_class = "rank-cell"
elif primary_metric and col == primary_metric:
cell_class = "primary-metric-cell"
elif col in dimension_metrics:
cell_class = "dimension-metric-cell"
else:
cell_class = "regular-cell"
cell_content = str(cell_value)
if col == "Rank":
rank_class = ""
if cell_content == "1":
rank_class = " rank-1"
elif cell_content == "2":
rank_class = " rank-2"
elif cell_content == "3":
rank_class = " rank-3"
html += f'| {cell_content} | '
elif col == "entry_type":
entry_class = ""
entry_lower = cell_content.lower()
if "baseline" in entry_lower:
entry_class = " baseline-entry"
elif "submission" in entry_lower:
entry_class = " competition-entry"
html += f'{cell_content} | '
elif "**" in cell_content:
html += f'{cell_content.replace("**", "")} | '
elif "" in cell_content:
html += f'{cell_content.replace("", "").replace("", "")} | '
else:
html += f'{cell_content} | '
html += "
"
html += """
"""
return html
def get_academic_css() -> str:
return """
/* 艺术化标题样式 */
#title, .wa-title {
text-align: center;
font-size: 3.25em !important;
margin-bottom: 40px !important;
font-family: 'Segoe UI', 'Arial', sans-serif;
font-weight: 900;
letter-spacing: -0.5px;
line-height: 1.06;
padding: 36px 30px;
border-radius: 15px;
background: linear-gradient(145deg, #ffffff, #f5f7fa);
box-shadow:
0 10px 40px rgba(41, 128, 185, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
#title:hover, .wa-title:hover {
box-shadow:
0 15px 50px rgba(41, 128, 185, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
#title h1, .wa-title h1 {
margin: 0;
padding: 0;
background: linear-gradient(
90deg,
#3498db 0%,
#2ecc71 25%,
#f1c40f 50%,
#e74c3c 75%,
#9b59b6 100%
);
background-size: 400% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-fill-color: transparent;
animation: gradientShift 8s ease infinite;
position: relative;
z-index: 2;
text-shadow:
2px 2px 0px rgba(0, 0, 0, 0.05),
4px 4px 0px rgba(0, 0, 0, 0.03);
filter: drop-shadow(0 5px 15px rgba(52, 152, 219, 0.3));
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
#title .subtitle, .wa-title .subtitle {
font-size: 0.9em !important;
color: #34495e;
margin-top: 18px;
font-weight: 600;
display: block;
line-height: 1.45;
font-style: normal;
max-width: 1080px;
margin-left: auto;
margin-right: auto;
background: linear-gradient(
90deg,
#e74c3c 0%,
#f39c12 20%,
#2ecc71 40%,
#3498db 60%,
#9b59b6 80%,
#e74c3c 100%
);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-fill-color: transparent;
animation: subtitleShift 10s ease infinite;
padding: 12px 24px;
border-radius: 8px;
background-color: rgba(255, 255, 255, 0.9);
box-shadow:
inset 0 0 0 1px rgba(52, 152, 219, 0.1),
0 4px 12px rgba(0, 0, 0, 0.05);
font-family: 'Georgia', 'Times New Roman', serif;
text-transform: none;
letter-spacing: 0.5px;
}
@keyframes subtitleShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
#title::before, .wa-title::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 8px;
background: linear-gradient(90deg,
#3498db, #2ecc71, #f1c40f, #e74c3c, #9b59b6, #3498db);
background-size: 400% 100%;
animation: gradientShift 12s linear infinite;
}
#title::after, .wa-title::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(circle at 10% 20%, rgba(52, 152, 219, 0.08) 0%, transparent 50%),
radial-gradient(circle at 90% 80%, rgba(46, 204, 113, 0.08) 0%, transparent 50%),
radial-gradient(circle at 50% 50%, rgba(155, 89, 182, 0.05) 0%, transparent 50%);
pointer-events: none;
z-index: 1;
}
#title .emoji, .wa-title .emoji {
font-size: 1.2em;
vertical-align: middle;
margin-right: 15px;
display: inline-block;
background: linear-gradient(
90deg,
#3498db 0%,
#2ecc71 25%,
#f1c40f 50%,
#e74c3c 75%,
#9b59b6 100%
);
background-size: 400% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-fill-color: transparent;
animation: gradientShift 8s ease infinite, float 3s ease-in-out infinite;
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
position: relative;
z-index: 2;
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-5px); }
}
.academic-table-container {
margin: 20px 0;
overflow-x: auto;
border-radius: 12px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08);
background: white;
}
.academic-table {
width: 100%;
border-collapse: collapse;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 14px;
background: white;
}
.academic-table thead {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
}
.academic-table th {
padding: 15px 12px;
text-align: center;
font-weight: 700;
color: #2c3e50;
border-bottom: 2px solid #dee2e6;
border-right: 1px solid #e9ecef;
white-space: nowrap;
}
.academic-table th:last-child {
border-right: none;
}
.academic-table td {
padding: 12px 10px;
text-align: center;
border-bottom: 1px solid #f1f3f4;
border-right: 1px solid #f8f9fa;
transition: background-color 0.2s ease;
}
.academic-table td:last-child {
border-right: none;
}
.academic-table tbody tr:hover {
background-color: #f8f9ff !important;
}
.academic-table tbody tr.even-row {
background-color: #ffffff;
}
.academic-table tbody tr.odd-row {
background-color: #fafbfc;
}
.model-header, .model-cell {
text-align: left !important;
min-width: 200px;
font-weight: 600;
}
.entry-type-header, .entry-type-cell {
min-width: 160px;
text-align: center !important;
white-space: nowrap;
}
.entry-type-cell {
font-weight: 700;
}
.entry-type-cell.baseline-entry {
color: #8e5a00;
background-color: #fff7e6;
}
.entry-type-cell.competition-entry {
color: #0b5d3b;
background-color: #eafaf1;
}
.rank-header, .rank-cell {
width: 80px;
font-weight: 700;
color: #495057;
}
.rank-cell.rank-1 {
color: #e67e22;
font-weight: 800;
font-size: 15px;
}
.rank-cell.rank-2 {
color: #95a5a6;
font-weight: 700;
font-size: 14px;
}
.rank-cell.rank-3 {
color: #d35400;
font-weight: 700;
font-size: 14px;
}
.primary-metric {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
font-weight: 800;
}
.primary-metric-cell {
background-color: rgba(102, 126, 234, 0.06);
font-weight: 700;
color: #5a67d8;
}
.dimension-metric-header {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%) !important;
color: white !important;
font-weight: 700;
}
.dimension-metric-cell {
background-color: rgba(79, 172, 254, 0.05);
color: #3182ce;
font-weight: 600;
}
.best-score {
font-weight: 800 !important;
color: #27ae60 !important;
background-color: #eafaf1 !important;
position: relative;
}
.best-score::after {
content: "🏆";
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
font-size: 10px;
}
.second-best {
color: #f39c12 !important;
background-color: #fef9e7 !important;
font-weight: 700 !important;
text-decoration: underline;
text-decoration-color: #f39c12;
text-decoration-thickness: 2px;
text-underline-offset: 2px;
}
.no-data, .placeholder {
padding: 40px;
text-align: center;
color: #6c757d;
font-size: 16px;
font-style: italic;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 12px;
border: 2px dashed #dee2e6;
}
@media (max-width: 1200px) {
#title, .wa-title {
font-size: 2.7em !important;
padding: 30px 24px;
}
#title .subtitle, .wa-title .subtitle {
font-size: 0.8em !important;
}
}
@media (max-width: 768px) {
#title, .wa-title {
font-size: 2.1em !important;
padding: 24px 18px;
}
#title .subtitle, .wa-title .subtitle {
font-size: 0.72em !important;
padding: 10px 16px;
}
}
"""