openhands openhands commited on
Commit
c14a283
·
1 Parent(s): 71ba49b

Add Winners by Category section to main page

Browse files

- Implemented new 'Winners by Category' section displaying top 5 systems for each benchmark category
- Added functions get_winners_by_category() and create_winners_by_category_html() to ui_components.py
- Created responsive grid layout with category cards showing:
- Category icon and name header
- Ranked table with medal emojis (🥇🥈🥉) for top 3
- Company logos for model providers
- SDK version and model name
- Category score
- Added comprehensive CSS styles in content.py:
- Light and dark mode support
- Responsive grid for mobile devices
- Hover effects for cards
- Consistent styling with existing design system
- Section placed directly after main leaderboard results

Co-authored-by: openhands <openhands@all-hands.dev>

Files changed (3) hide show
  1. content.py +190 -0
  2. main_page.py +13 -1
  3. ui_components.py +156 -0
content.py CHANGED
@@ -963,4 +963,194 @@ h3 .header-link-icon {
963
  #main-leaderboard td:nth-child(7) .prose {
964
  font-weight: 700 !important;
965
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
966
  """
 
963
  #main-leaderboard td:nth-child(7) .prose {
964
  font-weight: 700 !important;
965
  }
966
+
967
+ /* ====== Winners by Category Section ====== */
968
+ .winners-by-category-container {
969
+ display: grid;
970
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
971
+ gap: 20px;
972
+ margin: 20px 0;
973
+ }
974
+
975
+ .winner-category-card {
976
+ background: #fff;
977
+ border: 1px solid #032629;
978
+ border-radius: 12px;
979
+ padding: 16px;
980
+ transition: box-shadow 0.2s ease;
981
+ }
982
+
983
+ .dark .winner-category-card {
984
+ background: rgba(250, 242, 233, 0.05);
985
+ border-color: #9fead1;
986
+ }
987
+
988
+ .winner-category-card:hover {
989
+ box-shadow: 0 4px 12px rgba(3, 38, 41, 0.15);
990
+ }
991
+
992
+ .dark .winner-category-card:hover {
993
+ box-shadow: 0 4px 12px rgba(159, 234, 209, 0.2);
994
+ }
995
+
996
+ .winner-category-header {
997
+ display: flex;
998
+ align-items: center;
999
+ gap: 12px;
1000
+ margin-bottom: 16px;
1001
+ padding-bottom: 12px;
1002
+ border-bottom: 2px solid #F0529C;
1003
+ }
1004
+
1005
+ .dark .winner-category-header {
1006
+ border-bottom-color: #0FCB8C;
1007
+ }
1008
+
1009
+ .winner-category-icon {
1010
+ width: 32px;
1011
+ height: 32px;
1012
+ }
1013
+
1014
+ .winner-category-header h3 {
1015
+ margin: 0;
1016
+ font-size: 16px;
1017
+ font-weight: 700;
1018
+ color: #032629;
1019
+ }
1020
+
1021
+ .dark .winner-category-header h3 {
1022
+ color: #fff;
1023
+ }
1024
+
1025
+ .winner-table {
1026
+ width: 100%;
1027
+ border-collapse: collapse;
1028
+ font-size: 13px;
1029
+ }
1030
+
1031
+ .winner-table thead tr {
1032
+ border-bottom: 1px solid #ddd;
1033
+ }
1034
+
1035
+ .dark .winner-table thead tr {
1036
+ border-bottom-color: #4a5a5a;
1037
+ }
1038
+
1039
+ .winner-table th {
1040
+ padding: 8px 4px;
1041
+ text-align: left;
1042
+ font-weight: 600;
1043
+ color: #667876;
1044
+ font-size: 11px;
1045
+ text-transform: uppercase;
1046
+ }
1047
+
1048
+ .dark .winner-table th {
1049
+ color: #9fead1;
1050
+ }
1051
+
1052
+ .winner-table td {
1053
+ padding: 10px 4px;
1054
+ vertical-align: middle;
1055
+ border-bottom: 1px solid #eee;
1056
+ }
1057
+
1058
+ .dark .winner-table td {
1059
+ border-bottom-color: #2a3a3a;
1060
+ }
1061
+
1062
+ .winner-table tbody tr:last-child td {
1063
+ border-bottom: none;
1064
+ }
1065
+
1066
+ .winner-table .rank-col {
1067
+ width: 32px;
1068
+ text-align: center;
1069
+ font-size: 14px;
1070
+ }
1071
+
1072
+ .winner-table .score-col {
1073
+ width: 60px;
1074
+ text-align: right;
1075
+ color: #F0529C;
1076
+ font-size: 14px;
1077
+ }
1078
+
1079
+ .dark .winner-table .score-col {
1080
+ color: #0FCB8C;
1081
+ }
1082
+
1083
+ .system-info {
1084
+ display: flex;
1085
+ align-items: center;
1086
+ gap: 10px;
1087
+ }
1088
+
1089
+ .company-logo-small {
1090
+ width: 20px;
1091
+ height: 20px;
1092
+ flex-shrink: 0;
1093
+ }
1094
+
1095
+ .system-details {
1096
+ display: flex;
1097
+ flex-direction: column;
1098
+ overflow: hidden;
1099
+ }
1100
+
1101
+ .sdk-name {
1102
+ font-weight: 600;
1103
+ color: #032629;
1104
+ font-size: 13px;
1105
+ white-space: nowrap;
1106
+ overflow: hidden;
1107
+ text-overflow: ellipsis;
1108
+ }
1109
+
1110
+ .dark .sdk-name {
1111
+ color: #fff;
1112
+ }
1113
+
1114
+ .model-name {
1115
+ font-size: 11px;
1116
+ color: #667876;
1117
+ white-space: nowrap;
1118
+ overflow: hidden;
1119
+ text-overflow: ellipsis;
1120
+ }
1121
+
1122
+ .dark .model-name {
1123
+ color: #9fead1;
1124
+ }
1125
+
1126
+ .no-data {
1127
+ text-align: center;
1128
+ color: #999;
1129
+ font-style: italic;
1130
+ padding: 20px !important;
1131
+ }
1132
+
1133
+ /* Responsive adjustments for winners section */
1134
+ @media (max-width: 600px) {
1135
+ .winners-by-category-container {
1136
+ grid-template-columns: 1fr;
1137
+ }
1138
+
1139
+ .winner-category-card {
1140
+ padding: 12px;
1141
+ }
1142
+
1143
+ .winner-table .score-col,
1144
+ .winner-table .rank-col {
1145
+ font-size: 12px;
1146
+ }
1147
+
1148
+ .sdk-name {
1149
+ font-size: 12px;
1150
+ }
1151
+
1152
+ .model-name {
1153
+ font-size: 10px;
1154
+ }
1155
+ }
1156
  """
main_page.py CHANGED
@@ -3,7 +3,11 @@ matplotlib.use('Agg')
3
  import gradio as gr
4
 
5
 
6
- from ui_components import create_leaderboard_display, get_full_leaderboard_data
 
 
 
 
7
 
8
  from content import (
9
  CITATION_BUTTON_LABEL,
@@ -39,6 +43,14 @@ def build_page():
39
  split_name="test"
40
  )
41
 
 
 
 
 
 
 
 
 
42
  # --- New Visualization Sections ---
43
  gr.Markdown("---")
44
 
 
3
  import gradio as gr
4
 
5
 
6
+ from ui_components import (
7
+ create_leaderboard_display,
8
+ get_full_leaderboard_data,
9
+ create_winners_by_category_html,
10
+ )
11
 
12
  from content import (
13
  CITATION_BUTTON_LABEL,
 
43
  split_name="test"
44
  )
45
 
46
+ # --- Winners by Category Section ---
47
+ gr.Markdown("---")
48
+ gr.HTML('<h2>Winners by Category</h2>', elem_id="winners-header")
49
+ gr.Markdown("Top 5 performing systems in each benchmark category.")
50
+
51
+ winners_html = create_winners_by_category_html(test_df, top_n=5)
52
+ gr.HTML(winners_html, elem_id="winners-by-category")
53
+
54
  # --- New Visualization Sections ---
55
  gr.Markdown("---")
56
 
ui_components.py CHANGED
@@ -414,6 +414,162 @@ except ImportError:
414
  pass # setup_data may not be available during import
415
 
416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  class DummyViewer:
418
  """A mock viewer to be cached on error. It has a ._load() method
419
  to ensure it behaves like the real LeaderboardViewer."""
 
414
  pass # setup_data may not be available during import
415
 
416
 
417
+ # Category definitions for Winners by Category section
418
+ CATEGORY_DEFINITIONS = [
419
+ {"name": "Issue Resolution", "score_col": "Issue Resolution Score", "icon": "bug-fixing.svg"},
420
+ {"name": "Greenfield", "score_col": "Greenfield Score", "icon": "app-creation.svg"},
421
+ {"name": "Frontend", "score_col": "Frontend Score", "icon": "frontend-development.svg"},
422
+ {"name": "Testing", "score_col": "Testing Score", "icon": "test-generation.svg"},
423
+ {"name": "Information Gathering", "score_col": "Information Gathering Score", "icon": "information-gathering.svg"},
424
+ ]
425
+
426
+
427
+ def get_winners_by_category(df: pd.DataFrame, top_n: int = 5) -> dict:
428
+ """
429
+ Extract the top N systems for each category based on their score.
430
+
431
+ Args:
432
+ df: The full leaderboard DataFrame with all scores
433
+ top_n: Number of top systems to return per category (default: 5)
434
+
435
+ Returns:
436
+ Dictionary mapping category name to list of top systems with their scores
437
+ """
438
+ winners = {}
439
+
440
+ for cat_def in CATEGORY_DEFINITIONS:
441
+ cat_name = cat_def["name"]
442
+ score_col = cat_def["score_col"]
443
+
444
+ if score_col not in df.columns:
445
+ winners[cat_name] = []
446
+ continue
447
+
448
+ # Filter to rows that have a valid score for this category
449
+ cat_df = df[df[score_col].notna()].copy()
450
+
451
+ if cat_df.empty:
452
+ winners[cat_name] = []
453
+ continue
454
+
455
+ # Sort by score descending and take top N
456
+ cat_df = cat_df.sort_values(score_col, ascending=False).head(top_n)
457
+
458
+ # Extract relevant info
459
+ top_systems = []
460
+ for rank, (_, row) in enumerate(cat_df.iterrows(), 1):
461
+ system_info = {
462
+ "rank": rank,
463
+ "sdk_version": row.get("SDK Version", "Unknown"),
464
+ "language_model": row.get("Language Model", "Unknown"),
465
+ "score": row[score_col],
466
+ }
467
+ top_systems.append(system_info)
468
+
469
+ winners[cat_name] = top_systems
470
+
471
+ return winners
472
+
473
+
474
+ def create_winners_by_category_html(df: pd.DataFrame, top_n: int = 5) -> str:
475
+ """
476
+ Create an HTML table displaying the top N winners for each category.
477
+
478
+ Args:
479
+ df: The full leaderboard DataFrame
480
+ top_n: Number of top systems to show per category
481
+
482
+ Returns:
483
+ HTML string with the winners table
484
+ """
485
+ winners = get_winners_by_category(df, top_n)
486
+
487
+ # Build the HTML table
488
+ html_parts = ['<div class="winners-by-category-container">']
489
+
490
+ # Create a card for each category
491
+ for cat_def in CATEGORY_DEFINITIONS:
492
+ cat_name = cat_def["name"]
493
+ icon_file = cat_def["icon"]
494
+ icon_uri = get_svg_as_data_uri(f"assets/{icon_file}")
495
+
496
+ top_systems = winners.get(cat_name, [])
497
+
498
+ html_parts.append(f'''
499
+ <div class="winner-category-card">
500
+ <div class="winner-category-header">
501
+ <img src="{icon_uri}" alt="{cat_name}" class="winner-category-icon">
502
+ <h3>{cat_name}</h3>
503
+ </div>
504
+ <table class="winner-table">
505
+ <thead>
506
+ <tr>
507
+ <th class="rank-col">#</th>
508
+ <th class="system-col">System</th>
509
+ <th class="score-col">Score</th>
510
+ </tr>
511
+ </thead>
512
+ <tbody>
513
+ ''')
514
+
515
+ if top_systems:
516
+ for system in top_systems:
517
+ rank = system["rank"]
518
+ sdk_version = system["sdk_version"]
519
+ language_model = system["language_model"]
520
+ score = system["score"]
521
+
522
+ # Get company logo for the model
523
+ company_info = get_company_from_model(language_model)
524
+ logo_uri = get_svg_as_data_uri(company_info["path"])
525
+
526
+ # Format model name - clean it if it's a list
527
+ if isinstance(language_model, list):
528
+ language_model = language_model[0] if language_model else "Unknown"
529
+ model_display = str(language_model).split('/')[-1] # Remove provider prefix
530
+
531
+ # Add medal emoji for top 3
532
+ rank_display = rank
533
+ if rank == 1:
534
+ rank_display = "🥇"
535
+ elif rank == 2:
536
+ rank_display = "🥈"
537
+ elif rank == 3:
538
+ rank_display = "🥉"
539
+
540
+ html_parts.append(f'''
541
+ <tr>
542
+ <td class="rank-col">{rank_display}</td>
543
+ <td class="system-col">
544
+ <div class="system-info">
545
+ <img src="{logo_uri}" alt="{company_info['name']}" class="company-logo-small">
546
+ <div class="system-details">
547
+ <span class="sdk-name">{sdk_version}</span>
548
+ <span class="model-name">{model_display}</span>
549
+ </div>
550
+ </div>
551
+ </td>
552
+ <td class="score-col"><strong>{score:.1f}</strong></td>
553
+ </tr>
554
+ ''')
555
+ else:
556
+ html_parts.append('''
557
+ <tr>
558
+ <td colspan="3" class="no-data">No submissions yet</td>
559
+ </tr>
560
+ ''')
561
+
562
+ html_parts.append('''
563
+ </tbody>
564
+ </table>
565
+ </div>
566
+ ''')
567
+
568
+ html_parts.append('</div>')
569
+
570
+ return ''.join(html_parts)
571
+
572
+
573
  class DummyViewer:
574
  """A mock viewer to be cached on error. It has a ._load() method
575
  to ensure it behaves like the real LeaderboardViewer."""