Spaces:
Sleeping
Sleeping
sunmarinup commited on
Commit ·
585a9bb
1
Parent(s): 3182612
Simplify flow
Browse files- app.py +6 -15
- tests/test_leaderboard.py +55 -72
app.py
CHANGED
|
@@ -6,11 +6,6 @@ import pandas as pd
|
|
| 6 |
|
| 7 |
from src.about import TITLE, INTRODUCTION_TEXT
|
| 8 |
from src.display.css_html_js import custom_css
|
| 9 |
-
from src.leaderboard.read_evals import (
|
| 10 |
-
TabularLeaderboardEntry,
|
| 11 |
-
parse_tabular_leaderboard,
|
| 12 |
-
tabular_leaderboard_to_dataframe,
|
| 13 |
-
)
|
| 14 |
from src.utils import download_github_file_content
|
| 15 |
|
| 16 |
# GitHub API endpoint for the file (handles Git LFS files)
|
|
@@ -29,16 +24,16 @@ DISPLAY_COLUMNS = [
|
|
| 29 |
]
|
| 30 |
|
| 31 |
|
| 32 |
-
def download_leaderboard() ->
|
| 33 |
"""Download the remote leaderboard CSV from GitHub (handles Git LFS).
|
| 34 |
|
| 35 |
-
Returns a
|
| 36 |
"""
|
| 37 |
csv_content = download_github_file_content(LEADERBOARD_API_URL, timeout=30)
|
| 38 |
|
| 39 |
df = pd.read_csv(io.StringIO(csv_content))
|
| 40 |
if df.empty:
|
| 41 |
-
return
|
| 42 |
|
| 43 |
missing_cols = [col for col in DISPLAY_COLUMNS if col not in df.columns]
|
| 44 |
if missing_cols:
|
|
@@ -51,19 +46,15 @@ def download_leaderboard() -> list[TabularLeaderboardEntry]:
|
|
| 51 |
df["sem_medal_pct"] = (df["sem_medal_pct"] * 100).round(1)
|
| 52 |
df["Date"] = pd.to_datetime(df["Date"], errors="coerce").dt.strftime("%Y-%m-%d")
|
| 53 |
|
| 54 |
-
# Sort by mean_normalized_score
|
| 55 |
df = df.sort_values(by="mean_normalized_score", ascending=False, ignore_index=True)
|
| 56 |
|
| 57 |
-
|
| 58 |
-
entries = parse_tabular_leaderboard(df)
|
| 59 |
-
|
| 60 |
-
return entries
|
| 61 |
|
| 62 |
|
| 63 |
def refresh_leaderboard():
|
| 64 |
"""Fetch the leaderboard and build the status message for the UI."""
|
| 65 |
-
|
| 66 |
-
df = tabular_leaderboard_to_dataframe(entries)
|
| 67 |
status = (
|
| 68 |
f"Showing data from [GitHub]({LEADERBOARD_GITHUB_URL}). "
|
| 69 |
f"Last refreshed: {datetime.now(timezone.utc):%Y-%m-%d %H:%M UTC}."
|
|
|
|
| 6 |
|
| 7 |
from src.about import TITLE, INTRODUCTION_TEXT
|
| 8 |
from src.display.css_html_js import custom_css
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
from src.utils import download_github_file_content
|
| 10 |
|
| 11 |
# GitHub API endpoint for the file (handles Git LFS files)
|
|
|
|
| 24 |
]
|
| 25 |
|
| 26 |
|
| 27 |
+
def download_leaderboard() -> pd.DataFrame:
|
| 28 |
"""Download the remote leaderboard CSV from GitHub (handles Git LFS).
|
| 29 |
|
| 30 |
+
Returns a processed DataFrame ready for display.
|
| 31 |
"""
|
| 32 |
csv_content = download_github_file_content(LEADERBOARD_API_URL, timeout=30)
|
| 33 |
|
| 34 |
df = pd.read_csv(io.StringIO(csv_content))
|
| 35 |
if df.empty:
|
| 36 |
+
return pd.DataFrame(columns=DISPLAY_COLUMNS)
|
| 37 |
|
| 38 |
missing_cols = [col for col in DISPLAY_COLUMNS if col not in df.columns]
|
| 39 |
if missing_cols:
|
|
|
|
| 46 |
df["sem_medal_pct"] = (df["sem_medal_pct"] * 100).round(1)
|
| 47 |
df["Date"] = pd.to_datetime(df["Date"], errors="coerce").dt.strftime("%Y-%m-%d")
|
| 48 |
|
| 49 |
+
# Sort by mean_normalized_score
|
| 50 |
df = df.sort_values(by="mean_normalized_score", ascending=False, ignore_index=True)
|
| 51 |
|
| 52 |
+
return df
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
|
| 55 |
def refresh_leaderboard():
|
| 56 |
"""Fetch the leaderboard and build the status message for the UI."""
|
| 57 |
+
df = download_leaderboard()
|
|
|
|
| 58 |
status = (
|
| 59 |
f"Showing data from [GitHub]({LEADERBOARD_GITHUB_URL}). "
|
| 60 |
f"Last refreshed: {datetime.now(timezone.utc):%Y-%m-%d %H:%M UTC}."
|
tests/test_leaderboard.py
CHANGED
|
@@ -5,7 +5,6 @@ import pytest
|
|
| 5 |
import requests
|
| 6 |
|
| 7 |
from app import DISPLAY_COLUMNS, download_leaderboard, refresh_leaderboard
|
| 8 |
-
from src.leaderboard.read_evals import TabularLeaderboardEntry
|
| 9 |
|
| 10 |
|
| 11 |
@pytest.fixture
|
|
@@ -49,12 +48,12 @@ class TestDownloadLeaderboard:
|
|
| 49 |
mock_download.return_value = sample_csv_data
|
| 50 |
|
| 51 |
# Execute
|
| 52 |
-
|
| 53 |
|
| 54 |
# Assertions
|
| 55 |
-
assert isinstance(
|
| 56 |
-
assert len(
|
| 57 |
-
assert
|
| 58 |
mock_download.assert_called_once()
|
| 59 |
|
| 60 |
@patch("app.download_github_file_content")
|
|
@@ -62,68 +61,67 @@ class TestDownloadLeaderboard:
|
|
| 62 |
"""Test that numeric columns are properly rounded."""
|
| 63 |
mock_download.return_value = sample_csv_data
|
| 64 |
|
| 65 |
-
|
| 66 |
|
| 67 |
-
# Check rounding -
|
| 68 |
-
assert
|
| 69 |
-
assert
|
| 70 |
-
assert
|
| 71 |
# Check that scores are floats
|
| 72 |
-
assert isinstance(
|
| 73 |
-
assert isinstance(
|
| 74 |
|
| 75 |
@patch("app.download_github_file_content")
|
| 76 |
def test_percentage_conversion(self, mock_download, sample_csv_data):
|
| 77 |
"""Test that medal percentages are converted from decimal to percentage."""
|
| 78 |
mock_download.return_value = sample_csv_data
|
| 79 |
|
| 80 |
-
|
| 81 |
|
| 82 |
# Check percentage conversion (0.876543 * 100 = 87.6543, rounded to 87.7)
|
| 83 |
-
#
|
| 84 |
-
assert
|
| 85 |
-
assert
|
| 86 |
-
assert
|
| 87 |
|
| 88 |
@patch("app.download_github_file_content")
|
| 89 |
def test_date_formatting(self, mock_download, sample_csv_data):
|
| 90 |
"""Test that dates are properly formatted."""
|
| 91 |
mock_download.return_value = sample_csv_data
|
| 92 |
|
| 93 |
-
|
| 94 |
|
| 95 |
-
# Check date formatting -
|
| 96 |
# exp_003 (2024-02-01), exp_001 (2024-01-15), exp_002 (2024-01-20)
|
| 97 |
-
assert
|
| 98 |
-
assert
|
| 99 |
-
assert
|
| 100 |
|
| 101 |
@patch("app.download_github_file_content")
|
| 102 |
def test_sorting(self, mock_download, sample_csv_data):
|
| 103 |
-
"""Test that
|
| 104 |
mock_download.return_value = sample_csv_data
|
| 105 |
|
| 106 |
-
|
| 107 |
|
| 108 |
# Check sorting (highest score first)
|
| 109 |
-
scores = [
|
| 110 |
assert scores == sorted(scores, reverse=True)
|
| 111 |
-
assert
|
| 112 |
-
assert
|
| 113 |
|
| 114 |
@patch("app.download_github_file_content")
|
| 115 |
def test_extra_columns_filtered(self, mock_download, sample_csv_with_extra_columns):
|
| 116 |
"""Test that extra columns are filtered out."""
|
| 117 |
mock_download.return_value = sample_csv_with_extra_columns
|
| 118 |
|
| 119 |
-
|
| 120 |
|
| 121 |
-
# Check that
|
| 122 |
-
assert len(
|
| 123 |
-
assert
|
| 124 |
-
# Verify the
|
| 125 |
-
|
| 126 |
-
assert "extra_col" not in entry_dict
|
| 127 |
|
| 128 |
@patch("app.download_github_file_content")
|
| 129 |
def test_missing_columns_error(self, mock_download, sample_csv_missing_columns):
|
|
@@ -172,10 +170,11 @@ class TestDownloadLeaderboard:
|
|
| 172 |
csv_data = ",".join(DISPLAY_COLUMNS) # Header only
|
| 173 |
mock_download.return_value = csv_data
|
| 174 |
|
| 175 |
-
|
| 176 |
|
| 177 |
-
assert isinstance(
|
| 178 |
-
assert len(
|
|
|
|
| 179 |
|
| 180 |
@patch("app.download_github_file_content")
|
| 181 |
def test_invalid_date_handling(self, mock_download):
|
|
@@ -188,13 +187,14 @@ class TestDownloadLeaderboard:
|
|
| 188 |
)
|
| 189 |
mock_download.return_value = csv_with_invalid_date
|
| 190 |
|
| 191 |
-
|
| 192 |
|
| 193 |
-
# Invalid dates should become NaT and then
|
| 194 |
-
# Find
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
assert
|
|
|
|
| 198 |
|
| 199 |
@patch("app.download_github_file_content")
|
| 200 |
def test_git_lfs_pointer_file(self, mock_download, sample_csv_data):
|
|
@@ -202,12 +202,12 @@ class TestDownloadLeaderboard:
|
|
| 202 |
# The utility function handles LFS internally, so we just return the content
|
| 203 |
mock_download.return_value = sample_csv_data
|
| 204 |
|
| 205 |
-
|
| 206 |
|
| 207 |
# Should successfully download via download_url
|
| 208 |
-
assert isinstance(
|
| 209 |
-
assert len(
|
| 210 |
-
assert
|
| 211 |
mock_download.assert_called_once()
|
| 212 |
|
| 213 |
@patch("app.download_github_file_content")
|
|
@@ -216,33 +216,21 @@ class TestDownloadLeaderboard:
|
|
| 216 |
# The utility function handles download_url internally, so we just return the content
|
| 217 |
mock_download.return_value = sample_csv_data
|
| 218 |
|
| 219 |
-
|
| 220 |
|
| 221 |
-
assert isinstance(
|
| 222 |
-
assert len(
|
| 223 |
-
assert
|
| 224 |
mock_download.assert_called_once()
|
| 225 |
|
| 226 |
|
| 227 |
class TestRefreshLeaderboard:
|
| 228 |
"""Tests for refresh_leaderboard function."""
|
| 229 |
|
| 230 |
-
@patch("app.tabular_leaderboard_to_dataframe")
|
| 231 |
@patch("app.download_leaderboard")
|
| 232 |
-
def test_refresh_leaderboard_success(self, mock_download
|
| 233 |
"""Test that refresh_leaderboard returns dataframe and status message."""
|
| 234 |
# Setup mocks
|
| 235 |
-
mock_entry = TabularLeaderboardEntry(
|
| 236 |
-
experiment_id="exp_001",
|
| 237 |
-
agent="Agent A",
|
| 238 |
-
llms_used="GPT-4",
|
| 239 |
-
mean_normalized_score=0.85,
|
| 240 |
-
std_normalized_score=0.01,
|
| 241 |
-
mean_medal_pct=87.0,
|
| 242 |
-
sem_medal_pct=1.0,
|
| 243 |
-
date="2024-01-15",
|
| 244 |
-
)
|
| 245 |
-
mock_entries = [mock_entry]
|
| 246 |
mock_df = pd.DataFrame(
|
| 247 |
{
|
| 248 |
"experiment_id": ["exp_001"],
|
|
@@ -250,8 +238,7 @@ class TestRefreshLeaderboard:
|
|
| 250 |
"Agent": ["Agent A"],
|
| 251 |
}
|
| 252 |
)
|
| 253 |
-
mock_download.return_value =
|
| 254 |
-
mock_to_df.return_value = mock_df
|
| 255 |
|
| 256 |
# Execute
|
| 257 |
df, status = refresh_leaderboard()
|
|
@@ -269,16 +256,12 @@ class TestRefreshLeaderboard:
|
|
| 269 |
timestamp_pattern = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2} UTC"
|
| 270 |
assert re.search(timestamp_pattern, status) is not None
|
| 271 |
mock_download.assert_called_once()
|
| 272 |
-
mock_to_df.assert_called_once_with(mock_entries)
|
| 273 |
|
| 274 |
-
@patch("app.tabular_leaderboard_to_dataframe")
|
| 275 |
@patch("app.download_leaderboard")
|
| 276 |
-
def test_refresh_leaderboard_includes_url(self, mock_download
|
| 277 |
"""Test that status message includes the GitHub URL."""
|
| 278 |
-
mock_entries = []
|
| 279 |
mock_df = pd.DataFrame()
|
| 280 |
-
mock_download.return_value =
|
| 281 |
-
mock_to_df.return_value = mock_df
|
| 282 |
|
| 283 |
df, status = refresh_leaderboard()
|
| 284 |
|
|
|
|
| 5 |
import requests
|
| 6 |
|
| 7 |
from app import DISPLAY_COLUMNS, download_leaderboard, refresh_leaderboard
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
@pytest.fixture
|
|
|
|
| 48 |
mock_download.return_value = sample_csv_data
|
| 49 |
|
| 50 |
# Execute
|
| 51 |
+
df = download_leaderboard()
|
| 52 |
|
| 53 |
# Assertions
|
| 54 |
+
assert isinstance(df, pd.DataFrame)
|
| 55 |
+
assert len(df) == 3
|
| 56 |
+
assert list(df.columns) == DISPLAY_COLUMNS
|
| 57 |
mock_download.assert_called_once()
|
| 58 |
|
| 59 |
@patch("app.download_github_file_content")
|
|
|
|
| 61 |
"""Test that numeric columns are properly rounded."""
|
| 62 |
mock_download.return_value = sample_csv_data
|
| 63 |
|
| 64 |
+
df = download_leaderboard()
|
| 65 |
|
| 66 |
+
# Check rounding - df is sorted by score descending
|
| 67 |
+
assert df.iloc[0]["mean_normalized_score"] == 0.912
|
| 68 |
+
assert df.iloc[1]["mean_normalized_score"] == 0.854
|
| 69 |
+
assert df.iloc[2]["mean_normalized_score"] == 0.789
|
| 70 |
# Check that scores are floats
|
| 71 |
+
assert isinstance(df.iloc[0]["mean_normalized_score"], float)
|
| 72 |
+
assert isinstance(df.iloc[0]["std_normalized_score"], float)
|
| 73 |
|
| 74 |
@patch("app.download_github_file_content")
|
| 75 |
def test_percentage_conversion(self, mock_download, sample_csv_data):
|
| 76 |
"""Test that medal percentages are converted from decimal to percentage."""
|
| 77 |
mock_download.return_value = sample_csv_data
|
| 78 |
|
| 79 |
+
df = download_leaderboard()
|
| 80 |
|
| 81 |
# Check percentage conversion (0.876543 * 100 = 87.6543, rounded to 87.7)
|
| 82 |
+
# df is sorted by score descending: exp_003 (92.3), exp_001 (87.7), exp_002 (76.5)
|
| 83 |
+
assert df.iloc[0]["mean_medal_pct"] == 92.3 # exp_003
|
| 84 |
+
assert df.iloc[1]["mean_medal_pct"] == 87.7 # exp_001
|
| 85 |
+
assert df.iloc[2]["mean_medal_pct"] == 76.5 # exp_002
|
| 86 |
|
| 87 |
@patch("app.download_github_file_content")
|
| 88 |
def test_date_formatting(self, mock_download, sample_csv_data):
|
| 89 |
"""Test that dates are properly formatted."""
|
| 90 |
mock_download.return_value = sample_csv_data
|
| 91 |
|
| 92 |
+
df = download_leaderboard()
|
| 93 |
|
| 94 |
+
# Check date formatting - df sorted by score descending
|
| 95 |
# exp_003 (2024-02-01), exp_001 (2024-01-15), exp_002 (2024-01-20)
|
| 96 |
+
assert df.iloc[0]["Date"] == "2024-02-01"
|
| 97 |
+
assert df.iloc[1]["Date"] == "2024-01-15"
|
| 98 |
+
assert df.iloc[2]["Date"] == "2024-01-20"
|
| 99 |
|
| 100 |
@patch("app.download_github_file_content")
|
| 101 |
def test_sorting(self, mock_download, sample_csv_data):
|
| 102 |
+
"""Test that df is sorted by mean_normalized_score descending."""
|
| 103 |
mock_download.return_value = sample_csv_data
|
| 104 |
|
| 105 |
+
df = download_leaderboard()
|
| 106 |
|
| 107 |
# Check sorting (highest score first)
|
| 108 |
+
scores = df["mean_normalized_score"].tolist()
|
| 109 |
assert scores == sorted(scores, reverse=True)
|
| 110 |
+
assert df.iloc[0]["experiment_id"] == "exp_003" # Highest score
|
| 111 |
+
assert df.iloc[2]["experiment_id"] == "exp_002" # Lowest score
|
| 112 |
|
| 113 |
@patch("app.download_github_file_content")
|
| 114 |
def test_extra_columns_filtered(self, mock_download, sample_csv_with_extra_columns):
|
| 115 |
"""Test that extra columns are filtered out."""
|
| 116 |
mock_download.return_value = sample_csv_with_extra_columns
|
| 117 |
|
| 118 |
+
df = download_leaderboard()
|
| 119 |
|
| 120 |
+
# Check that df is created correctly (extra columns should be filtered)
|
| 121 |
+
assert len(df) == 2
|
| 122 |
+
assert list(df.columns) == DISPLAY_COLUMNS
|
| 123 |
+
# Verify the df doesn't have extra columns
|
| 124 |
+
assert "extra_col" not in df.columns
|
|
|
|
| 125 |
|
| 126 |
@patch("app.download_github_file_content")
|
| 127 |
def test_missing_columns_error(self, mock_download, sample_csv_missing_columns):
|
|
|
|
| 170 |
csv_data = ",".join(DISPLAY_COLUMNS) # Header only
|
| 171 |
mock_download.return_value = csv_data
|
| 172 |
|
| 173 |
+
df = download_leaderboard()
|
| 174 |
|
| 175 |
+
assert isinstance(df, pd.DataFrame)
|
| 176 |
+
assert len(df) == 0
|
| 177 |
+
assert list(df.columns) == DISPLAY_COLUMNS
|
| 178 |
|
| 179 |
@patch("app.download_github_file_content")
|
| 180 |
def test_invalid_date_handling(self, mock_download):
|
|
|
|
| 187 |
)
|
| 188 |
mock_download.return_value = csv_with_invalid_date
|
| 189 |
|
| 190 |
+
df = download_leaderboard()
|
| 191 |
|
| 192 |
+
# Invalid dates should become NaT and then "nan" string
|
| 193 |
+
# Find rows by experiment_id since order may vary
|
| 194 |
+
row_001 = df[df["experiment_id"] == "exp_001"].iloc[0]
|
| 195 |
+
row_002 = df[df["experiment_id"] == "exp_002"].iloc[0]
|
| 196 |
+
assert pd.isna(row_001["Date"])
|
| 197 |
+
assert row_002["Date"] == "2024-01-20"
|
| 198 |
|
| 199 |
@patch("app.download_github_file_content")
|
| 200 |
def test_git_lfs_pointer_file(self, mock_download, sample_csv_data):
|
|
|
|
| 202 |
# The utility function handles LFS internally, so we just return the content
|
| 203 |
mock_download.return_value = sample_csv_data
|
| 204 |
|
| 205 |
+
df = download_leaderboard()
|
| 206 |
|
| 207 |
# Should successfully download via download_url
|
| 208 |
+
assert isinstance(df, pd.DataFrame)
|
| 209 |
+
assert len(df) == 3
|
| 210 |
+
assert list(df.columns) == DISPLAY_COLUMNS
|
| 211 |
mock_download.assert_called_once()
|
| 212 |
|
| 213 |
@patch("app.download_github_file_content")
|
|
|
|
| 216 |
# The utility function handles download_url internally, so we just return the content
|
| 217 |
mock_download.return_value = sample_csv_data
|
| 218 |
|
| 219 |
+
df = download_leaderboard()
|
| 220 |
|
| 221 |
+
assert isinstance(df, pd.DataFrame)
|
| 222 |
+
assert len(df) == 3
|
| 223 |
+
assert list(df.columns) == DISPLAY_COLUMNS
|
| 224 |
mock_download.assert_called_once()
|
| 225 |
|
| 226 |
|
| 227 |
class TestRefreshLeaderboard:
|
| 228 |
"""Tests for refresh_leaderboard function."""
|
| 229 |
|
|
|
|
| 230 |
@patch("app.download_leaderboard")
|
| 231 |
+
def test_refresh_leaderboard_success(self, mock_download):
|
| 232 |
"""Test that refresh_leaderboard returns dataframe and status message."""
|
| 233 |
# Setup mocks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
mock_df = pd.DataFrame(
|
| 235 |
{
|
| 236 |
"experiment_id": ["exp_001"],
|
|
|
|
| 238 |
"Agent": ["Agent A"],
|
| 239 |
}
|
| 240 |
)
|
| 241 |
+
mock_download.return_value = mock_df
|
|
|
|
| 242 |
|
| 243 |
# Execute
|
| 244 |
df, status = refresh_leaderboard()
|
|
|
|
| 256 |
timestamp_pattern = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2} UTC"
|
| 257 |
assert re.search(timestamp_pattern, status) is not None
|
| 258 |
mock_download.assert_called_once()
|
|
|
|
| 259 |
|
|
|
|
| 260 |
@patch("app.download_leaderboard")
|
| 261 |
+
def test_refresh_leaderboard_includes_url(self, mock_download):
|
| 262 |
"""Test that status message includes the GitHub URL."""
|
|
|
|
| 263 |
mock_df = pd.DataFrame()
|
| 264 |
+
mock_download.return_value = mock_df
|
|
|
|
| 265 |
|
| 266 |
df, status = refresh_leaderboard()
|
| 267 |
|