Yuxuan-Zhang-Dexter commited on
Commit
6fcfa6c
·
1 Parent(s): fe7cb32

correct gallery tab

Browse files
Files changed (1) hide show
  1. gallery_tab.py +231 -753
gallery_tab.py CHANGED
@@ -1,73 +1,6 @@
1
  import gradio as gr
2
- import os
3
- import pandas as pd
4
  import json
5
- from PIL import Image, ImageSequence
6
- import io
7
- from functools import reduce
8
- import numpy as np
9
- from datetime import datetime, timedelta
10
- import matplotlib.pyplot as plt
11
- from leaderboard_utils import (
12
- get_organization,
13
- get_mario_leaderboard,
14
- get_sokoban_leaderboard,
15
- get_2048_leaderboard,
16
- get_candy_leaderboard,
17
- get_tetris_leaderboard,
18
- get_tetris_planning_leaderboard,
19
- get_combined_leaderboard,
20
- GAME_ORDER
21
- )
22
- from data_visualization import (
23
- get_combined_leaderboard_with_group_bar,
24
- create_organization_radar_chart,
25
- create_top_players_radar_chart,
26
- create_player_radar_chart,
27
- create_horizontal_bar_chart,
28
- normalize_values,
29
- get_combined_leaderboard_with_single_radar
30
- )
31
- from gallery_tab import create_video_gallery
32
-
33
- # Try to import enhanced leaderboard, use standard DataFrame if not available
34
-
35
- from gradio_leaderboard import Leaderboard, SelectColumns, ColumnFilter
36
- from leaderboard_config import ON_LOAD_COLUMNS, TYPES
37
- HAS_ENHANCED_LEADERBOARD = True
38
-
39
-
40
- # Define time points and their corresponding data files
41
- TIME_POINTS = {
42
- "03/25/2025": "rank_data_03_25_2025.json",
43
- # Add more time points here as they become available
44
- }
45
-
46
- # Load the initial JSON file with rank data
47
- with open(TIME_POINTS["03/25/2025"], "r") as f:
48
- rank_data = json.load(f)
49
-
50
- # Add leaderboard state at the top level
51
- leaderboard_state = {
52
- "current_game": None,
53
- "previous_overall": {
54
- "Super Mario Bros": True,
55
- "Sokoban": True,
56
- "2048": True,
57
- "Candy Crash": True,
58
- "Tetris (complete)": True,
59
- "Tetris (planning only)": True
60
- },
61
- "previous_details": {
62
- "Super Mario Bros": False,
63
- "Sokoban": False,
64
- "2048": False,
65
- "Candy Crash": False,
66
- "Tetris (complete)": False,
67
- "Tetris (planning only)": False
68
- }
69
- }
70
-
71
 
72
  # Load video links and news data
73
  with open('assets/game_video_link.json', 'r') as f:
@@ -76,702 +9,247 @@ with open('assets/game_video_link.json', 'r') as f:
76
  with open('assets/news.json', 'r') as f:
77
  NEWS_DATA = json.load(f)
78
 
79
- def load_rank_data(time_point):
80
- """Load rank data for a specific time point"""
81
- if time_point in TIME_POINTS:
82
- try:
83
- with open(TIME_POINTS[time_point], "r") as f:
84
- return json.load(f)
85
- except FileNotFoundError:
86
- return None
87
- return None
88
-
89
- # Add a note about score values
90
- def add_score_note():
91
- return gr.Markdown("*Note: '-1' in the table indicates no data point for that model.*", elem_classes="score-note")
92
-
93
- # Function to prepare DataFrame for display
94
- def prepare_dataframe_for_display(df, for_game=None):
95
- """Format DataFrame for better display in the UI"""
96
- # Clone the DataFrame to avoid modifying the original
97
- display_df = df.copy()
98
-
99
- # Filter out normalized score columns
100
- norm_columns = [col for col in display_df.columns if col.startswith('norm_')]
101
- if norm_columns:
102
- display_df = display_df.drop(columns=norm_columns)
103
-
104
- # Replace '_' with '-' for better display
105
- for col in display_df.columns:
106
- if col.endswith(' Score'):
107
- display_df[col] = display_df[col].apply(lambda x: '-' if x == '_' else x)
108
-
109
- # If we're in detailed view, add a formatted rank column
110
- if for_game:
111
- # Sort by relevant score column
112
- score_col = f"{for_game} Score"
113
- if score_col in display_df.columns:
114
- # Convert to numeric for sorting, treating '-' as NaN
115
- display_df[score_col] = pd.to_numeric(display_df[score_col], errors='coerce')
116
- # Sort by score in descending order
117
- display_df = display_df.sort_values(by=score_col, ascending=False)
118
- # Add rank column based on the sort
119
- display_df.insert(0, 'Rank', range(1, len(display_df) + 1))
120
- # Filter out models that didn't participate
121
- display_df = display_df[~display_df[score_col].isna()]
122
-
123
- return display_df
124
-
125
- # Helper function to ensure leaderboard updates maintain consistent height
126
- def update_df_with_height(df):
127
- """Update DataFrame with consistent height parameter."""
128
- return gr.update(value=df, height=800)
129
-
130
- def update_leaderboard(mario_overall, mario_details,
131
- sokoban_overall, sokoban_details,
132
- _2048_overall, _2048_details,
133
- candy_overall, candy_details,
134
- tetris_overall, tetris_details,
135
- tetris_plan_overall, tetris_plan_details):
136
- global leaderboard_state
137
-
138
- # Convert current checkbox states to dictionary for easier comparison
139
- current_overall = {
140
- "Super Mario Bros": mario_overall,
141
- "Sokoban": sokoban_overall,
142
- "2048": _2048_overall,
143
- "Candy Crash": candy_overall,
144
- "Tetris (complete)": tetris_overall,
145
- "Tetris (planning only)": tetris_plan_overall
146
- }
147
-
148
- current_details = {
149
- "Super Mario Bros": mario_details,
150
- "Sokoban": sokoban_details,
151
- "2048": _2048_details,
152
- "Candy Crash": candy_details,
153
- "Tetris (complete)": tetris_details,
154
- "Tetris (planning only)": tetris_plan_details
155
- }
156
-
157
- # Find which game's state changed
158
- changed_game = None
159
- for game in current_overall.keys():
160
- if (current_overall[game] != leaderboard_state["previous_overall"][game] or
161
- current_details[game] != leaderboard_state["previous_details"][game]):
162
- changed_game = game
163
- break
164
-
165
- if changed_game:
166
- # If a game's details checkbox was checked
167
- if current_details[changed_game] and not leaderboard_state["previous_details"][changed_game]:
168
- # Reset all other games' states
169
- for game in current_overall.keys():
170
- if game != changed_game:
171
- current_overall[game] = False
172
- current_details[game] = False
173
- leaderboard_state["previous_overall"][game] = False
174
- leaderboard_state["previous_details"][game] = False
175
-
176
- # Update state for the selected game
177
- leaderboard_state["current_game"] = changed_game
178
- leaderboard_state["previous_overall"][changed_game] = True
179
- leaderboard_state["previous_details"][changed_game] = True
180
- current_overall[changed_game] = True
181
-
182
- # If a game's overall checkbox was checked
183
- elif current_overall[changed_game] and not leaderboard_state["previous_overall"][changed_game]:
184
- # If we were in details view for another game, switch to overall view
185
- if leaderboard_state["current_game"] and leaderboard_state["previous_details"][leaderboard_state["current_game"]]:
186
- # Reset previous game's details
187
- leaderboard_state["previous_details"][leaderboard_state["current_game"]] = False
188
- current_details[leaderboard_state["current_game"]] = False
189
- leaderboard_state["current_game"] = None
190
-
191
- # Update state
192
- leaderboard_state["previous_overall"][changed_game] = True
193
- leaderboard_state["previous_details"][changed_game] = False
194
-
195
- # If a game's overall checkbox was unchecked
196
- elif not current_overall[changed_game] and leaderboard_state["previous_overall"][changed_game]:
197
- # If we're in details view, don't allow unchecking the overall checkbox
198
- if leaderboard_state["current_game"] == changed_game:
199
- current_overall[changed_game] = True
200
- else:
201
- leaderboard_state["previous_overall"][changed_game] = False
202
- if leaderboard_state["current_game"] == changed_game:
203
- leaderboard_state["current_game"] = None
204
-
205
- # If a game's details checkbox was unchecked
206
- elif not current_details[changed_game] and leaderboard_state["previous_details"][changed_game]:
207
- leaderboard_state["previous_details"][changed_game] = False
208
- if leaderboard_state["current_game"] == changed_game:
209
- leaderboard_state["current_game"] = None
210
- # When exiting details view, reset to show all games
211
- for game in current_overall.keys():
212
- current_overall[game] = True
213
- current_details[game] = False
214
- leaderboard_state["previous_overall"][game] = True
215
- leaderboard_state["previous_details"][game] = False
216
-
217
- # Special case: If all games are selected and we're trying to view details
218
- all_games_selected = all(current_overall.values()) and not any(current_details.values())
219
- if all_games_selected and changed_game and current_details[changed_game]:
220
- # Reset all other games' states
221
- for game in current_overall.keys():
222
- if game != changed_game:
223
- current_overall[game] = False
224
- current_details[game] = False
225
- leaderboard_state["previous_overall"][game] = False
226
- leaderboard_state["previous_details"][game] = False
227
-
228
- # Update state for the selected game
229
- leaderboard_state["current_game"] = changed_game
230
- leaderboard_state["previous_overall"][changed_game] = True
231
- leaderboard_state["previous_details"][changed_game] = True
232
- current_overall[changed_game] = True
233
-
234
- # Build dictionary for selected games
235
- selected_games = {
236
- "Super Mario Bros": current_overall["Super Mario Bros"],
237
- "Sokoban": current_overall["Sokoban"],
238
- "2048": current_overall["2048"],
239
- "Candy Crash": current_overall["Candy Crash"],
240
- "Tetris (complete)": current_overall["Tetris (complete)"],
241
- "Tetris (planning only)": current_overall["Tetris (planning only)"]
242
- }
243
-
244
- # Get the appropriate DataFrame and charts based on current state
245
- if leaderboard_state["current_game"]:
246
- # For detailed view
247
- if leaderboard_state["current_game"] == "Super Mario Bros":
248
- df = get_mario_leaderboard(rank_data)
249
- elif leaderboard_state["current_game"] == "Sokoban":
250
- df = get_sokoban_leaderboard(rank_data)
251
- elif leaderboard_state["current_game"] == "2048":
252
- df = get_2048_leaderboard(rank_data)
253
- elif leaderboard_state["current_game"] == "Candy Crash":
254
- df = get_candy_leaderboard(rank_data)
255
- elif leaderboard_state["current_game"] == "Tetris (complete)":
256
- df = get_tetris_leaderboard(rank_data)
257
- else: # Tetris (planning only)
258
- df = get_tetris_planning_leaderboard(rank_data)
259
-
260
- # Format the DataFrame for display
261
- display_df = prepare_dataframe_for_display(df, leaderboard_state["current_game"])
262
-
263
- # Always create a new chart for detailed view
264
- chart = create_horizontal_bar_chart(df, leaderboard_state["current_game"])
265
- # For detailed view, we'll use the same chart for all visualizations
266
- radar_chart = chart
267
- group_bar_chart = chart
268
- else:
269
- # For overall view
270
- df, group_bar_chart = get_combined_leaderboard_with_group_bar(rank_data, selected_games)
271
- # Format the DataFrame for display
272
- display_df = prepare_dataframe_for_display(df)
273
- # Use the same selected_games for radar chart
274
- _, radar_chart = get_combined_leaderboard_with_single_radar(rank_data, selected_games)
275
- chart = group_bar_chart
276
-
277
- # Return exactly 16 values to match the expected outputs
278
- return (update_df_with_height(display_df), chart, radar_chart, group_bar_chart,
279
- current_overall["Super Mario Bros"], current_details["Super Mario Bros"],
280
- current_overall["Sokoban"], current_details["Sokoban"],
281
- current_overall["2048"], current_details["2048"],
282
- current_overall["Candy Crash"], current_details["Candy Crash"],
283
- current_overall["Tetris (complete)"], current_details["Tetris (complete)"],
284
- current_overall["Tetris (planning only)"], current_details["Tetris (planning only)"])
285
-
286
- def update_leaderboard_with_time(time_point, mario_overall, mario_details,
287
- sokoban_overall, sokoban_details,
288
- _2048_overall, _2048_details,
289
- candy_overall, candy_details,
290
- tetris_overall, tetris_details,
291
- tetris_plan_overall, tetris_plan_details):
292
- # Load rank data for the selected time point
293
- global rank_data
294
- new_rank_data = load_rank_data(time_point)
295
- if new_rank_data is not None:
296
- rank_data = new_rank_data
297
-
298
- # Use the existing update_leaderboard function
299
- return update_leaderboard(mario_overall, mario_details,
300
- sokoban_overall, sokoban_details,
301
- _2048_overall, _2048_details,
302
- candy_overall, candy_details,
303
- tetris_overall, tetris_details,
304
- tetris_plan_overall, tetris_plan_details)
305
-
306
- def get_initial_state():
307
- """Get the initial state for the leaderboard"""
308
- return {
309
- "current_game": None,
310
- "previous_overall": {
311
- "Super Mario Bros": True,
312
- "Sokoban": True,
313
- "2048": True,
314
- "Candy Crash": True,
315
- "Tetris (complete)": True,
316
- "Tetris (planning only)": True
317
- },
318
- "previous_details": {
319
- "Super Mario Bros": False,
320
- "Sokoban": False,
321
- "2048": False,
322
- "Candy Crash": False,
323
- "Tetris (complete)": False,
324
- "Tetris (planning only)": False
325
- }
326
- }
327
-
328
- def clear_filters():
329
- global leaderboard_state
330
-
331
- # Reset all checkboxes to default state
332
- selected_games = {
333
- "Super Mario Bros": True,
334
- "Sokoban": True,
335
- "2048": True,
336
- "Candy Crash": True,
337
- "Tetris (complete)": True,
338
- "Tetris (planning only)": True
339
- }
340
-
341
- # Get the combined leaderboard and group bar chart
342
- df, group_bar_chart = get_combined_leaderboard_with_group_bar(rank_data, selected_games)
343
-
344
- # Format the DataFrame for display
345
- display_df = prepare_dataframe_for_display(df)
346
-
347
- # Get the radar chart using the same selected games
348
- _, radar_chart = get_combined_leaderboard_with_single_radar(rank_data, selected_games)
349
 
350
- # Reset the leaderboard state to match the default checkbox states
351
- leaderboard_state = get_initial_state()
352
 
353
- # Return exactly 16 values to match the expected outputs
354
- return (update_df_with_height(display_df), group_bar_chart, radar_chart, group_bar_chart,
355
- True, False, # mario
356
- True, False, # sokoban
357
- True, False, # 2048
358
- True, False, # candy
359
- True, False, # tetris
360
- True, False) # tetris plan
361
-
362
- def create_timeline_slider():
363
- """Create a custom timeline slider component"""
364
- timeline_html = """
365
- <div class="timeline-container">
366
  <style>
367
- .timeline-container {
368
- width: 85%; /* Increased from 70% to 85% */
369
- padding: 8px;
370
- font-family: Arial, sans-serif;
371
- height: 40px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
  display: flex;
373
  align-items: center;
374
- }
375
- .timeline-track {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  position: relative;
377
- height: 6px;
378
- background: #e0e0e0;
379
- border-radius: 3px;
380
- margin: 0;
381
- width: 100%;
382
- }
383
- .timeline-progress {
384
  position: absolute;
385
- height: 100%;
386
- background: #2196F3;
387
- border-radius: 3px;
388
  width: 100%;
389
- }
390
- .timeline-handle {
391
- position: absolute;
392
- right: 0;
393
- top: 50%;
394
- transform: translate(50%, -50%);
395
- width: 20px;
396
- height: 20px;
397
- background: #2196F3;
398
- border: 3px solid white;
399
- border-radius: 50%;
400
- cursor: pointer;
401
- box-shadow: 0 2px 6px rgba(0,0,0,0.3);
402
- }
403
- .timeline-date {
404
- position: absolute;
405
- top: -25px;
406
- transform: translateX(-50%);
407
- background: #2196F3; /* Changed to match slider blue color */
408
- color: #ffffff !important;
409
- padding: 3px 8px;
410
- border-radius: 4px;
411
- font-size: 12px;
412
- white-space: nowrap;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  font-weight: 600;
414
- box-shadow: 0 2px 6px rgba(0,0,0,0.2);
415
- letter-spacing: 0.5px;
416
- text-shadow: 0 1px 2px rgba(0,0,0,0.2);
417
- }
 
 
 
 
 
418
  </style>
419
- <div class="timeline-track">
420
- <div class="timeline-progress"></div>
421
- <div class="timeline-handle">
422
- <div class="timeline-date">03/25/2025</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  </div>
424
  </div>
425
- </div>
426
- <script>
427
- (function() {
428
- const container = document.querySelector('.timeline-container');
429
- const track = container.querySelector('.timeline-track');
430
- const handle = container.querySelector('.timeline-handle');
431
- let isDragging = false;
432
-
433
- // For now, we only have one time point
434
- const timePoints = {
435
- "03/25/2025": 1.0
436
- };
437
-
438
- function updatePosition(e) {
439
- if (!isDragging) return;
440
-
441
- const rect = track.getBoundingClientRect();
442
- let x = (e.clientX - rect.left) / rect.width;
443
- x = Math.max(0, Math.min(1, x));
444
-
445
- // For now, snap to the only available time point
446
- x = 1.0;
447
-
448
- handle.style.right = `${(1 - x) * 100}%`;
449
- }
450
-
451
- handle.addEventListener('mousedown', (e) => {
452
- isDragging = true;
453
- e.preventDefault();
454
- });
455
-
456
- document.addEventListener('mousemove', updatePosition);
457
- document.addEventListener('mouseup', () => {
458
- isDragging = false;
459
- });
460
-
461
- // Prevent text selection while dragging
462
- container.addEventListener('selectstart', (e) => {
463
- if (isDragging) e.preventDefault();
464
- });
465
- })();
466
- </script>
467
- """
468
- return gr.HTML(timeline_html)
469
-
470
- def build_app():
471
- with gr.Blocks(css="""
472
- .visualization-container .js-plotly-plot {
473
- margin-left: auto !important;
474
- margin-right: auto !important;
475
- display: block !important;
476
- }
477
-
478
- /* Optional: limit width for better layout on large screens */
479
- .visualization-container .js-plotly-plot {
480
- max-width: 1000px;
481
- }
482
-
483
- .section-title {
484
- font-size: 1.5em;
485
- font-weight: bold;
486
- color: #2c3e50;
487
- margin-bottom: 15px;
488
- padding-bottom: 10px;
489
- border-bottom: 2px solid #e9ecef;
490
- text-align: center;
491
- }
492
- .container {
493
- max-width: 1400px;
494
- margin: 0 auto;
495
- padding: 0 20px;
496
- }
497
 
498
- /* Enhanced table styling - SIMPLIFIED */
499
- .table-container {
500
- height: 800px !important;
501
- max-height: 1000px !important;
502
- overflow-y: auto !important; /* ONLY the outer container gets scrolling */
503
- border-radius: 8px;
504
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
505
- }
506
-
507
- /* Prevent inner containers from having scrollbars */
508
- .table-container > div,
509
- .table-container > div > div,
510
- .gradio-dataframe > div,
511
- [data-testid="dataframe"] > div {
512
- overflow: visible !important;
513
- height: auto !important;
514
- }
515
-
516
- /* Fix table styling */
517
- .table-container table {
518
- width: 100%;
519
- border-collapse: separate;
520
- border-spacing: 0;
521
- }
522
-
523
- /* Make headers sticky */
524
- .table-container th {
525
- position: sticky !important;
526
- top: 0 !important;
527
- background-color: #f8f9fa !important;
528
- z-index: 10 !important;
529
- font-weight: bold;
530
- padding: 12px;
531
- border-bottom: 2px solid #e9ecef;
532
- }
533
-
534
- /* Simple cell styling */
535
- .table-container td {
536
- padding: 10px 12px;
537
- border-bottom: 1px solid #e9ecef;
538
- }
539
-
540
- /* Visual enhancements */
541
- .table-container tr:hover {
542
- background-color: #f1f3f4;
543
- }
544
-
545
- .table-container tr:nth-child(even) {
546
- background-color: #f8fafc;
547
- }
548
-
549
- /* Row containing the table */
550
- .gradio-container .gr-row {
551
- min-height: auto !important;
552
- height: auto !important;
553
- overflow: visible !important;
554
- margin-bottom: 20px;
555
- }
556
- """) as demo:
557
- gr.Markdown("# 🎮 Game Arena: Gaming Agent 🎲")
558
 
559
- with gr.Tabs():
560
- with gr.Tab("🏆 Leaderboard"):
561
- # Visualization section
562
- with gr.Row():
563
- gr.Markdown("### 📊 Data Visualization")
564
-
565
- # Detailed view visualization (single chart)
566
- detailed_visualization = gr.Plot(
567
- label="Performance Visualization",
568
- visible=False,
569
- elem_classes="visualization-container"
570
- )
571
-
572
- with gr.Column(visible=True) as overall_visualizations:
573
- with gr.Tabs():
574
- with gr.Tab("📈 Radar Chart"):
575
- radar_visualization = gr.Plot(
576
- label="Comparative Analysis (Radar Chart)",
577
- elem_classes="visualization-container"
578
- )
579
- with gr.Tab("📊 Group Bar Chart"):
580
- group_bar_visualization = gr.Plot(
581
- label="Comparative Analysis (Group Bar Chart)",
582
- elem_classes="visualization-container"
583
- )
584
-
585
-
586
- # Game selection section
587
- with gr.Row():
588
- gr.Markdown("### 🎮 Game Selection")
589
- with gr.Row():
590
- with gr.Column():
591
- gr.Markdown("**🎮 Super Mario Bros**")
592
- mario_overall = gr.Checkbox(label="Super Mario Bros Score", value=True)
593
- mario_details = gr.Checkbox(label="Super Mario Bros Details", value=False)
594
- with gr.Column():
595
- gr.Markdown("**📦 Sokoban**")
596
- sokoban_overall = gr.Checkbox(label="Sokoban Score", value=True)
597
- sokoban_details = gr.Checkbox(label="Sokoban Details", value=False)
598
- with gr.Column():
599
- gr.Markdown("**🔢 2048**")
600
- _2048_overall = gr.Checkbox(label="2048 Score", value=True)
601
- _2048_details = gr.Checkbox(label="2048 Details", value=False)
602
- with gr.Column():
603
- gr.Markdown("**🍬 Candy Crash**")
604
- candy_overall = gr.Checkbox(label="Candy Crash Score", value=True)
605
- candy_details = gr.Checkbox(label="Candy Crash Details", value=False)
606
- with gr.Column():
607
- gr.Markdown("**🎯 Tetris (complete)**")
608
- tetris_overall = gr.Checkbox(label="Tetris (complete) Score", value=True)
609
- tetris_details = gr.Checkbox(label="Tetris (complete) Details", value=False)
610
- with gr.Column():
611
- gr.Markdown("**📋 Tetris (planning)**")
612
- tetris_plan_overall = gr.Checkbox(label="Tetris (planning) Score", value=True)
613
- tetris_plan_details = gr.Checkbox(label="Tetris (planning) Details", value=False)
614
-
615
- # Controls
616
- with gr.Row():
617
- with gr.Column(scale=2):
618
- gr.Markdown("**⏰ Time Tracker**")
619
- timeline = create_timeline_slider()
620
- with gr.Column(scale=1):
621
- gr.Markdown("**🔄 Controls**")
622
- clear_btn = gr.Button("Reset Filters", variant="secondary")
623
-
624
- # Leaderboard table
625
- with gr.Row():
626
- gr.Markdown("### 📋 Detailed Results")
627
-
628
- # Add leaderboard search box in its own row
629
- with gr.Row():
630
- with gr.Column(scale=8):
631
- search_box = gr.Textbox(
632
- label="🔍 Search by Player or Organization",
633
- placeholder="Type to filter the table...",
634
- show_label=True
635
- )
636
- with gr.Column(scale=1):
637
- search_clear_btn = gr.Button("Clear", variant="secondary")
638
-
639
- # Get initial leaderboard dataframe
640
- initial_df = get_combined_leaderboard(rank_data, {
641
- "Super Mario Bros": True,
642
- "Sokoban": True,
643
- "2048": True,
644
- "Candy Crash": True,
645
- "Tetris (complete)": True,
646
- "Tetris (planning only)": True
647
- })
648
-
649
- # Format the DataFrame for display
650
- initial_display_df = prepare_dataframe_for_display(initial_df)
651
-
652
- # Create a standard DataFrame component with enhanced styling
653
- with gr.Row():
654
- leaderboard_df = gr.DataFrame(
655
- value=initial_display_df,
656
- interactive=True,
657
- elem_id="leaderboard-table",
658
- elem_classes="table-container",
659
- wrap=True,
660
- column_widths={"Player": "25%", "Organization": "20%"},
661
- height=800
662
- )
663
-
664
- # Add the score note below the table
665
- with gr.Row():
666
- score_note = add_score_note()
667
-
668
- # Add search functionality
669
- def filter_table(search_term, current_df):
670
- # Create a fresh copy of the DataFrame to avoid modifying the original
671
- filtered_df = current_df.copy()
672
-
673
- # Only apply search if a term is provided
674
- if search_term:
675
- # Filter the DataFrame by Player or Organization
676
- filtered_df = filtered_df[
677
- filtered_df["Player"].str.contains(search_term, case=False) |
678
- filtered_df["Organization"].str.contains(search_term, case=False)
679
- ]
680
-
681
- return filtered_df
682
-
683
- # Connect search box to the table
684
- search_box.change(
685
- filter_table,
686
- inputs=[search_box, leaderboard_df],
687
- outputs=[leaderboard_df]
688
- )
689
-
690
- # Clear search button functionality
691
- def clear_search():
692
- return "", initial_display_df
693
-
694
- search_clear_btn.click(
695
- clear_search,
696
- inputs=[],
697
- outputs=[search_box, leaderboard_df]
698
- )
699
-
700
- # List of all checkboxes
701
- checkbox_list = [
702
- mario_overall, mario_details,
703
- sokoban_overall, sokoban_details,
704
- _2048_overall, _2048_details,
705
- candy_overall, candy_details,
706
- tetris_overall, tetris_details,
707
- tetris_plan_overall, tetris_plan_details
708
- ]
709
-
710
- # Update visualizations when checkboxes change
711
- def update_visualizations(*checkbox_states):
712
- # Check if any details checkbox is selected
713
- is_details_view = any([
714
- checkbox_states[1], checkbox_states[3], checkbox_states[5],
715
- checkbox_states[7], checkbox_states[9], checkbox_states[11]
716
- ])
717
-
718
- # Update visibility of visualization blocks
719
- return {
720
- detailed_visualization: gr.update(visible=is_details_view),
721
- overall_visualizations: gr.update(visible=not is_details_view)
722
- }
723
-
724
- # Add change event to all checkboxes
725
- for checkbox in checkbox_list:
726
- checkbox.change(
727
- update_visualizations,
728
- inputs=checkbox_list,
729
- outputs=[detailed_visualization, overall_visualizations]
730
- )
731
-
732
- # Update leaderboard and visualizations when checkboxes change
733
- for checkbox in checkbox_list:
734
- checkbox.change(
735
- update_leaderboard,
736
- inputs=checkbox_list,
737
- outputs=[
738
- leaderboard_df,
739
- detailed_visualization,
740
- radar_visualization,
741
- group_bar_visualization
742
- ] + checkbox_list
743
- )
744
-
745
- # Update when clear button is clicked
746
- clear_btn.click(
747
- clear_filters,
748
- inputs=[],
749
- outputs=[
750
- leaderboard_df,
751
- detailed_visualization,
752
- radar_visualization,
753
- group_bar_visualization
754
- ] + checkbox_list
755
- )
756
-
757
- # Initialize the app
758
- demo.load(
759
- fn=clear_filters,
760
- inputs=[],
761
- outputs=[
762
- leaderboard_df,
763
- detailed_visualization,
764
- radar_visualization,
765
- group_bar_visualization
766
- ] + checkbox_list
767
- )
768
-
769
- with gr.Tab("🎥 Gallery"):
770
- video_gallery = create_video_gallery()
771
-
772
- return demo
773
-
774
- if __name__ == "__main__":
775
- demo_app = build_app()
776
- # Add file serving configuration
777
- demo_app.launch(debug=True, show_error=True, share=True)
 
1
  import gradio as gr
2
+ from datetime import datetime
 
3
  import json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  # Load video links and news data
6
  with open('assets/game_video_link.json', 'r') as f:
 
9
  with open('assets/news.json', 'r') as f:
10
  NEWS_DATA = json.load(f)
11
 
12
+ def create_video_gallery():
13
+ """Create a custom HTML/JS component for video gallery"""
14
+ # Extract video IDs
15
+ mario_id = VIDEO_LINKS["super_mario"].split("?v=")[1]
16
+ sokoban_id = VIDEO_LINKS["sokoban"].split("?v=")[1]
17
+ game_2048_id = VIDEO_LINKS["2048"].split("?v=")[1]
18
+ candy_id = VIDEO_LINKS["candy"].split("?v=")[1]
19
+
20
+ # Get the latest video from news data
21
+ latest_news = NEWS_DATA["news"][0] # First item is the latest
22
+ latest_video_id = latest_news["video_link"].split("?v=")[1]
23
+ latest_date = datetime.strptime(latest_news["date"], "%Y-%m-%d")
24
+ formatted_latest_date = latest_date.strftime("%B %d, %Y")
25
+
26
+ # Generate news HTML
27
+ news_items = []
28
+ for item in NEWS_DATA["news"]:
29
+ video_id = item["video_link"].split("?v=")[1]
30
+ date_obj = datetime.strptime(item["date"], "%Y-%m-%d")
31
+ formatted_date = date_obj.strftime("%B %d, %Y")
32
+ news_items.append(f'''
33
+ <div class="news-item">
34
+ <div class="news-date">{formatted_date}</div>
35
+ <div class="news-content">
36
+ <div class="news-video">
37
+ <div class="video-wrapper">
38
+ <iframe src="https://www.youtube.com/embed/{video_id}"></iframe>
39
+ </div>
40
+ </div>
41
+ <div class="news-text">
42
+ <a href="{item["twitter_link"]}" target="_blank" class="twitter-link">
43
+ <span class="twitter-icon">📢</span>
44
+ {item["twitter_text"]}
45
+ </a>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ ''')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
+ news_html = '\n'.join(news_items)
 
52
 
53
+ gallery_html = f'''
54
+ <div class="video-gallery-container">
 
 
 
 
 
 
 
 
 
 
 
55
  <style>
56
+ .video-gallery-container {{
57
+ width: 100%;
58
+ max-width: 1400px;
59
+ margin: 0 auto;
60
+ padding: 20px;
61
+ }}
62
+ .highlight-section {{
63
+ margin-bottom: 40px;
64
+ }}
65
+ .highlight-card {{
66
+ background: #ffffff;
67
+ border-radius: 10px;
68
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
69
+ overflow: hidden;
70
+ transition: transform 0.3s;
71
+ border: 2px solid #2196F3;
72
+ }}
73
+ .highlight-card:hover {{
74
+ transform: translateY(-5px);
75
+ }}
76
+ .highlight-header {{
77
+ background: #2196F3;
78
+ color: white;
79
+ padding: 15px 20px;
80
+ font-size: 1.2em;
81
+ font-weight: bold;
82
  display: flex;
83
  align-items: center;
84
+ gap: 10px;
85
+ }}
86
+ .highlight-date {{
87
+ font-size: 0.9em;
88
+ opacity: 0.9;
89
+ }}
90
+ .highlight-content {{
91
+ padding: 20px;
92
+ }}
93
+ .video-grid {{
94
+ display: grid;
95
+ grid-template-columns: repeat(2, 1fr);
96
+ gap: 20px;
97
+ margin-top: 20px;
98
+ margin-bottom: 40px;
99
+ }}
100
+ .video-card {{
101
+ background: #ffffff;
102
+ border-radius: 10px;
103
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
104
+ overflow: hidden;
105
+ transition: transform 0.2s;
106
+ }}
107
+ .video-card:hover {{
108
+ transform: translateY(-5px);
109
+ }}
110
+ .video-wrapper {{
111
  position: relative;
112
+ padding-bottom: 56.25%;
113
+ height: 0;
114
+ overflow: hidden;
115
+ }}
116
+ .video-wrapper iframe {{
 
 
117
  position: absolute;
118
+ top: 0;
119
+ left: 0;
 
120
  width: 100%;
121
+ height: 100%;
122
+ border: none;
123
+ }}
124
+ .video-title {{
125
+ padding: 15px;
126
+ font-size: 1.2em;
127
+ font-weight: bold;
128
+ color: #2c3e50;
129
+ text-align: center;
130
+ background: #f8f9fa;
131
+ border-top: 1px solid #eee;
132
+ }}
133
+ .news-section {{
134
+ margin-top: 40px;
135
+ border-top: 2px solid #e9ecef;
136
+ padding-top: 20px;
137
+ }}
138
+ .news-section-title {{
139
+ font-size: 1.8em;
140
+ font-weight: bold;
141
+ color: #2c3e50;
142
+ margin-bottom: 20px;
143
+ text-align: center;
144
+ }}
145
+ .news-item {{
146
+ background: #ffffff;
147
+ border-radius: 10px;
148
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
149
+ margin-bottom: 20px;
150
+ overflow: hidden;
151
+ }}
152
+ .news-date {{
153
+ padding: 10px 20px;
154
+ background: #f8f9fa;
155
+ color: #666;
156
+ font-size: 0.9em;
157
+ border-bottom: 1px solid #eee;
158
+ }}
159
+ .news-content {{
160
+ display: flex;
161
+ padding: 20px;
162
+ align-items: center;
163
+ gap: 30px;
164
+ }}
165
+ .news-video {{
166
+ flex: 0 0 300px;
167
+ }}
168
+ .news-text {{
169
+ flex: 1;
170
+ display: flex;
171
+ align-items: center;
172
+ min-height: 169px;
173
+ }}
174
+ .twitter-link {{
175
+ color: #2c3e50;
176
+ text-decoration: none;
177
+ display: flex;
178
+ align-items: center;
179
+ gap: 15px;
180
+ font-size: 1.4em;
181
  font-weight: 600;
182
+ line-height: 1.4;
183
+ }}
184
+ .twitter-link:hover {{
185
+ color: #1da1f2;
186
+ }}
187
+ .twitter-icon {{
188
+ font-size: 1.5em;
189
+ color: #1da1f2;
190
+ }}
191
  </style>
192
+
193
+ <!-- Highlight Section -->
194
+ <div class="highlight-section">
195
+ <div class="highlight-card">
196
+ <div class="highlight-header">
197
+ <span>🌟 Latest Update</span>
198
+ <span class="highlight-date">{formatted_latest_date}</span>
199
+ </div>
200
+ <div class="highlight-content">
201
+ <div class="video-wrapper">
202
+ <iframe src="https://www.youtube.com/embed/{latest_video_id}"></iframe>
203
+ </div>
204
+ <div class="video-title">
205
+ <a href="{latest_news["twitter_link"]}" target="_blank" class="twitter-link">
206
+ <span class="twitter-icon">📢</span>
207
+ {latest_news["twitter_text"]}
208
+ </a>
209
+ </div>
210
+ </div>
211
  </div>
212
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
+ <!-- Regular Video Grid -->
215
+ <div class="video-grid">
216
+ <div class="video-card">
217
+ <div class="video-wrapper">
218
+ <iframe src="https://www.youtube.com/embed/{mario_id}"></iframe>
219
+ </div>
220
+ <div class="video-title">🎮 Super Mario Bros</div>
221
+ </div>
222
+ <div class="video-card">
223
+ <div class="video-wrapper">
224
+ <iframe src="https://www.youtube.com/embed/{sokoban_id}"></iframe>
225
+ </div>
226
+ <div class="video-title">📦 Sokoban</div>
227
+ </div>
228
+ <div class="video-card">
229
+ <div class="video-wrapper">
230
+ <iframe src="https://www.youtube.com/embed/{game_2048_id}"></iframe>
231
+ </div>
232
+ <div class="video-title">🔢 2048</div>
233
+ </div>
234
+ <div class="video-card">
235
+ <div class="video-wrapper">
236
+ <iframe src="https://www.youtube.com/embed/{candy_id}"></iframe>
237
+ </div>
238
+ <div class="video-title">🍬 Candy Crash</div>
239
+ </div>
240
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
 
242
+ <!-- News Section -->
243
+ <div class="news-section">
244
+ <div class="news-section-title">📰 Latest News</div>
245
+ {news_html}
246
+ </div>
247
+ </div>
248
+ '''
249
+ return gr.HTML(gallery_html)
250
+
251
+ def create_gallery_tab():
252
+ """Create and return the gallery tab component"""
253
+ with gr.Tab("🎥 Gallery") as gallery_tab:
254
+ video_gallery = create_video_gallery()
255
+ return gallery_tab