manueldeprada HF Staff commited on
Commit
84cf73f
·
verified ·
1 Parent(s): 891c332

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. app.py +296 -261
  2. data.py +17 -18
  3. requirements.txt +1 -0
  4. styles.css +32 -41
  5. time_series_gradio.py +120 -0
app.py CHANGED
@@ -2,12 +2,18 @@ import matplotlib.pyplot as plt
2
  import matplotlib
3
  import pandas as pd
4
  import gradio as gr
 
5
 
6
  from data import CIResults
7
  from utils import logger
8
  from summary_page import create_summary_page
9
  from model_page import plot_model_stats
10
- from time_series_gradio import create_time_series_summary_gradio, create_model_time_series_gradio
 
 
 
 
 
11
 
12
 
13
  # Configure matplotlib to prevent memory warnings and set dark background
@@ -20,6 +26,12 @@ plt.ioff() # Turn off interactive mode to prevent figure accumulation
20
  # Load data once at startup
21
  Ci_results = CIResults()
22
  Ci_results.load_data()
 
 
 
 
 
 
23
  # Start the auto-reload scheduler
24
  Ci_results.schedule_data_reload()
25
 
@@ -78,9 +90,19 @@ def load_css():
78
  logger.warning("styles.css not found, using minimal default styles")
79
  return "body { background: #000; color: #fff; }"
80
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  # Create the Gradio interface with sidebar and dark theme
83
- with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
84
 
85
 
86
  with gr.Row():
@@ -92,50 +114,6 @@ with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
92
  description_text = get_description_text()
93
  description_display = gr.Markdown(description_text, elem_classes=["sidebar-description"])
94
 
95
- # View toggle buttons
96
- with gr.Row(elem_classes=["view-toggle-row"]):
97
- current_view_button = gr.Button(
98
- "current\n📊",
99
- variant="primary",
100
- size="lg",
101
- elem_classes=["view-toggle-button", "view-toggle-active"]
102
- )
103
- historical_view_button = gr.Button(
104
- "history\n📈",
105
- variant="secondary",
106
- size="lg",
107
- elem_classes=["view-toggle-button"]
108
- )
109
-
110
- # Date selection toggle button (initially hidden)
111
- date_toggle_button = gr.Button(
112
- "► Date Selection",
113
- variant="secondary",
114
- elem_classes=["date-header"],
115
- visible=False
116
- )
117
-
118
- # Date selection container (collapsible) - start folded
119
- with gr.Column(visible=True, elem_classes=["date-selection", "date-selection-hidden"]) as date_selection:
120
- start_date = gr.Dropdown(
121
- choices=Ci_results.available_dates,
122
- value=Ci_results.available_dates[-1] if Ci_results.available_dates else None, # Last date (oldest)
123
- label="Start Date",
124
- elem_classes=["date-dropdown"]
125
- )
126
- end_date = gr.Dropdown(
127
- choices=Ci_results.available_dates,
128
- value=Ci_results.available_dates[0] if Ci_results.available_dates else None, # First date (newest)
129
- label="End Date",
130
- elem_classes=["date-dropdown"]
131
- )
132
- load_historical_button = gr.Button(
133
- "Reload Historical Data",
134
- variant="primary",
135
- size="sm",
136
- elem_classes=["load-historical-button"]
137
- )
138
-
139
  # Summary button (for current view)
140
  summary_button = gr.Button(
141
  "summary\n📊",
@@ -144,6 +122,14 @@ with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
144
  elem_classes=["summary-button"]
145
  )
146
 
 
 
 
 
 
 
 
 
147
  # Model selection header (clickable toggle)
148
  model_toggle_button = gr.Button(
149
  f"► Select model ({len(Ci_results.available_models)})",
@@ -192,14 +178,6 @@ with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
192
 
193
  # Detailed view components (hidden by default)
194
  with gr.Column(visible=False, elem_classes=["detail-view"]) as detail_view:
195
- # Back button for current view detail
196
- back_to_summary_current_button = gr.Button(
197
- "← Back to Summary",
198
- variant="secondary",
199
- size="sm",
200
- elem_classes=["back-button"]
201
- )
202
-
203
  # Create the plot output
204
  plot_output = gr.Plot(
205
  label="",
@@ -235,51 +213,77 @@ with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
235
  # Time-series summary displays (multiple Gradio plots)
236
  time_series_failure_rates = gr.LinePlot(
237
  label="",
 
 
 
 
 
 
 
 
 
238
  elem_classes=["plot-container"]
239
  )
240
 
241
  time_series_amd_tests = gr.LinePlot(
242
  label="",
 
 
 
 
 
 
 
 
 
243
  elem_classes=["plot-container"]
244
  )
245
 
246
  time_series_nvidia_tests = gr.LinePlot(
247
  label="",
 
 
 
 
 
 
 
 
 
248
  elem_classes=["plot-container"]
249
  )
250
 
251
  # Time-series model view (hidden by default)
252
  with gr.Column(visible=False, elem_classes=["time-series-detail-view"]) as time_series_detail_view:
253
- # Back button for time-series model view
254
- back_to_summary_button = gr.Button(
255
- "← Back to Summary",
256
- variant="secondary",
257
- size="sm",
258
- elem_classes=["back-button"]
259
- )
260
-
261
  # Time-series plots for specific model (with spacing)
262
  time_series_amd_model_plot = gr.LinePlot(
263
  label="",
 
 
 
 
 
 
 
 
 
264
  elem_classes=["plot-container"]
265
  )
266
 
267
  time_series_nvidia_model_plot = gr.LinePlot(
268
  label="",
 
 
 
 
 
 
 
 
 
269
  elem_classes=["plot-container"]
270
  )
271
 
272
- # Set up click handlers for model buttons
273
- for i, btn in enumerate(model_buttons):
274
- model_name = model_choices[i]
275
- btn.click(
276
- fn=lambda selected_model=model_name: plot_model_stats(Ci_results.df, selected_model),
277
- outputs=[plot_output, amd_failed_tests_output, nvidia_failed_tests_output]
278
- ).then(
279
- fn=lambda: [gr.update(visible=False), gr.update(visible=True)],
280
- outputs=[summary_display, detail_view]
281
- )
282
-
283
  # Model toggle functionality
284
  def toggle_model_list(current_visible):
285
  """Toggle the visibility of the model list."""
@@ -298,6 +302,10 @@ with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
298
 
299
  # Track model list visibility state
300
  model_list_visible = gr.State(False)
 
 
 
 
301
 
302
  model_toggle_button.click(
303
  fn=toggle_model_list,
@@ -305,42 +313,58 @@ with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
305
  outputs=[model_toggle_button, model_list_container, model_list_visible]
306
  )
307
 
308
- # Date toggle functionality
309
- def toggle_date_selection(current_visible):
310
- """Toggle the visibility of the date selection."""
311
- new_visible = not current_visible
312
- arrow = "▼" if new_visible else "►"
313
- button_text = f"{arrow} Date Selection"
314
-
315
- # Use CSS classes instead of Gradio visibility
316
- css_classes = ["date-selection"]
317
- if new_visible:
318
- css_classes.append("date-selection-visible")
319
- else:
320
- css_classes.append("date-selection-hidden")
321
-
322
- return gr.update(value=button_text), gr.update(elem_classes=css_classes), new_visible
323
-
324
- # Track date selection visibility state
325
- date_selection_visible = gr.State(False)
326
-
327
- date_toggle_button.click(
328
- fn=toggle_date_selection,
329
- inputs=[date_selection_visible],
330
- outputs=[date_toggle_button, date_selection, date_selection_visible]
331
- )
332
 
333
- # Summary button click handler
334
- def show_summary_and_update_links():
335
- """Show summary page and update CI links."""
336
- return create_summary_page(Ci_results.df, Ci_results.available_models), get_description_text(), get_ci_links()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
 
338
  summary_button.click(
339
- fn=show_summary_and_update_links,
340
- outputs=[summary_display, description_display, ci_links_display]
341
- ).then(
342
- fn=lambda: [gr.update(visible=True), gr.update(visible=False)],
343
- outputs=[summary_display, detail_view]
 
 
 
 
 
 
 
 
 
 
344
  )
345
 
346
  # Function to get CI job links
@@ -413,174 +437,185 @@ with gr.Blocks(title="Model Test Results Dashboard", css=load_css()) as demo:
413
  return "🔗 **CI Jobs:** *Error loading links*\n\n❓ **[FAQ](README.md)**"
414
 
415
 
416
- # View toggle functionality
417
- def toggle_to_current_view():
418
- """Switch to current view."""
419
- return [
420
- gr.update(visible=True), # current_view
421
- gr.update(visible=False), # historical_view
422
- gr.update(visible=False), # date_toggle_button
423
- gr.update(visible=True), # summary_button
424
- gr.update(variant="primary", elem_classes=["view-toggle-button", "view-toggle-active"]), # current_view_button
425
- gr.update(variant="secondary", elem_classes=["view-toggle-button"]) # historical_view_button
426
- ]
427
-
428
- def toggle_to_historical_view():
429
- """Switch to historical view first, then auto-load data."""
430
- # First, just switch the view
431
- return [
432
- gr.update(visible=False), # current_view
433
- gr.update(visible=True), # historical_view
434
- gr.update(visible=True), # date_toggle_button
435
- gr.update(visible=False), # summary_button
436
- gr.update(variant="secondary", elem_classes=["view-toggle-button"]), # current_view_button
437
- gr.update(variant="primary", elem_classes=["view-toggle-button", "view-toggle-active"]), # historical_view_button
438
- gr.update(), # time_series_failure_rates
439
- gr.update(), # time_series_amd_tests
440
- gr.update(), # time_series_nvidia_tests
441
- ]
442
-
443
- def auto_load_historical_data():
444
- """Auto-load data for preselected dates after view switch."""
445
- # Get the preselected dates
446
- start_date_val = Ci_results.available_dates[-1] if Ci_results.available_dates else None
447
- end_date_val = Ci_results.available_dates[0] if Ci_results.available_dates else None
448
-
449
- # Check if we already have data for these dates
450
- if (hasattr(Ci_results, 'cached_start_date') and hasattr(Ci_results, 'cached_end_date') and
451
- Ci_results.cached_start_date == start_date_val and Ci_results.cached_end_date == end_date_val and
452
- not Ci_results.historical_df.empty):
453
- # Use cached data - no loading indicator needed
454
- plots = create_time_series_summary_gradio(Ci_results.historical_df)
455
- return plots['failure_rates'], plots['amd_tests'], plots['nvidia_tests']
456
-
457
- # Auto-load historical data if dates are available
458
- if start_date_val and end_date_val:
459
- try:
460
- Ci_results.load_historical_data(start_date_val, end_date_val)
461
-
462
- if not Ci_results.historical_df.empty:
463
- # Cache the loaded data
464
- Ci_results.cached_start_date = start_date_val
465
- Ci_results.cached_end_date = end_date_val
466
-
467
- plots = create_time_series_summary_gradio(Ci_results.historical_df)
468
- return plots['failure_rates'], plots['amd_tests'], plots['nvidia_tests']
469
- else:
470
- return gr.update(), gr.update(), gr.update()
471
- except Exception as e:
472
- logger.error(f"Error auto-loading historical data: {e}")
473
- return gr.update(), gr.update(), gr.update()
474
  else:
475
- return gr.update(), gr.update(), gr.update()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
 
477
- current_view_button.click(
478
- fn=toggle_to_current_view,
479
- outputs=[current_view, historical_view, date_toggle_button, summary_button, current_view_button, historical_view_button]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  )
481
 
482
- historical_view_button.click(
483
- fn=toggle_to_historical_view,
484
- outputs=[current_view, historical_view, date_toggle_button, summary_button, current_view_button, historical_view_button, time_series_failure_rates, time_series_amd_tests, time_series_nvidia_tests]
485
- ).then(
486
- fn=auto_load_historical_data,
487
- outputs=[time_series_failure_rates, time_series_amd_tests, time_series_nvidia_tests]
488
- )
489
-
490
- # Historical data loading functionality
491
- def load_historical_data(start_date, end_date):
492
- """Load and display historical data."""
493
- if not start_date or not end_date:
494
- logger.error("No start or end date provided")
495
- return gr.update(), gr.update(), gr.update()
496
-
497
- try:
498
- Ci_results.load_historical_data(start_date, end_date)
499
-
500
- if Ci_results.historical_df.empty:
501
- logger.error("No historical data found for the selected date range")
502
- return gr.update(), gr.update(), gr.update()
503
-
504
- # Create time-series summary plots
505
- plots = create_time_series_summary_gradio(Ci_results.historical_df)
506
-
507
- # Cache the loaded data
508
- Ci_results.cached_start_date = start_date
509
- Ci_results.cached_end_date = end_date
510
-
511
- return plots['failure_rates'], plots['amd_tests'], plots['nvidia_tests']
512
-
513
- except Exception as e:
514
- logger.error(f"Error loading historical data: {e}")
515
- return gr.update(), gr.update(), gr.update()
516
-
517
- load_historical_button.click(
518
- fn=load_historical_data,
519
- inputs=[start_date, end_date],
520
- outputs=[time_series_failure_rates, time_series_amd_tests, time_series_nvidia_tests]
521
- )
522
 
523
  # Time-series model selection functionality
524
  def show_time_series_model(selected_model):
525
  """Show time-series view for a specific model."""
526
- if Ci_results.historical_df.empty:
527
- return gr.update(), gr.update()
528
-
529
- try:
530
- plots = create_model_time_series_gradio(Ci_results.historical_df, selected_model)
531
- return plots['amd_plot'], plots['nvidia_plot']
532
- except Exception as e:
533
- logger.error(f"Error creating time-series for model {selected_model}: {e}")
534
- return gr.update(), gr.update()
535
-
536
- # Back button functionality
537
- def back_to_summary():
538
- """Return from model time-series view to summary time-series view."""
539
- return [
540
- gr.update(visible=True), # time_series_failure_rates
541
- gr.update(visible=True), # time_series_amd_tests
542
- gr.update(visible=True), # time_series_nvidia_tests
543
- gr.update(visible=False) # time_series_detail_view
544
- ]
545
-
546
- back_to_summary_button.click(
547
- fn=back_to_summary,
548
- outputs=[time_series_failure_rates, time_series_amd_tests, time_series_nvidia_tests, time_series_detail_view]
549
- )
550
 
551
- # Back button functionality for current view
552
- def back_to_summary_current():
553
- """Return from model detail view to summary view in current view."""
554
- return [
555
- gr.update(visible=True), # summary_display
556
- gr.update(visible=False) # detail_view
557
- ]
558
-
559
- back_to_summary_current_button.click(
560
- fn=back_to_summary_current,
561
- outputs=[summary_display, detail_view]
562
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
 
564
- # Update model button handlers to work with both views
565
  for i, btn in enumerate(model_buttons):
566
  model_name = model_choices[i]
567
-
568
- # Current view handler (existing functionality)
569
- btn.click(
570
- fn=lambda selected_model=model_name: plot_model_stats(Ci_results.df, selected_model),
571
- outputs=[plot_output, amd_failed_tests_output, nvidia_failed_tests_output]
572
- ).then(
573
- fn=lambda: [gr.update(visible=False), gr.update(visible=True)],
574
- outputs=[summary_display, detail_view]
575
- )
576
-
577
- # Historical view handler (new functionality)
578
  btn.click(
579
- fn=lambda selected_model=model_name: show_time_series_model(selected_model),
580
- outputs=[time_series_amd_model_plot, time_series_nvidia_model_plot]
581
- ).then(
582
- fn=lambda: [gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)],
583
- outputs=[time_series_failure_rates, time_series_amd_tests, time_series_nvidia_tests, time_series_detail_view]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
  )
585
 
586
  # Auto-update CI links when the interface loads
 
2
  import matplotlib
3
  import pandas as pd
4
  import gradio as gr
5
+ from gradio_toggle import Toggle
6
 
7
  from data import CIResults
8
  from utils import logger
9
  from summary_page import create_summary_page
10
  from model_page import plot_model_stats
11
+ from time_series_gradio import (
12
+ create_time_series_summary_gradio,
13
+ create_model_time_series_gradio,
14
+ get_time_series_summary_dfs,
15
+ get_model_time_series_dfs,
16
+ )
17
 
18
 
19
  # Configure matplotlib to prevent memory warnings and set dark background
 
26
  # Load data once at startup
27
  Ci_results = CIResults()
28
  Ci_results.load_data()
29
+ # Preload historical data at startup
30
+ if Ci_results.available_dates:
31
+ start_date_val = Ci_results.available_dates[-1] # Last date (oldest)
32
+ end_date_val = Ci_results.available_dates[0] # First date (newest)
33
+ Ci_results.load_historical_data(start_date_val, end_date_val)
34
+ logger.info(f"Preloaded historical data: {len(Ci_results.historical_df)} records")
35
  # Start the auto-reload scheduler
36
  Ci_results.schedule_data_reload()
37
 
 
90
  logger.warning("styles.css not found, using minimal default styles")
91
  return "body { background: #000; color: #fff; }"
92
 
93
+ js_func = """
94
+ function refresh() {
95
+ const url = new URL(window.location);
96
+
97
+ if (url.searchParams.get('__theme') !== 'dark') {
98
+ url.searchParams.set('__theme', 'dark');
99
+ window.location.href = url.href;
100
+ }
101
+ }
102
+ """
103
 
104
  # Create the Gradio interface with sidebar and dark theme
105
+ with gr.Blocks(title="Model Test Results Dashboard", css=load_css(), js=js_func) as demo:
106
 
107
 
108
  with gr.Row():
 
114
  description_text = get_description_text()
115
  description_display = gr.Markdown(description_text, elem_classes=["sidebar-description"])
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  # Summary button (for current view)
118
  summary_button = gr.Button(
119
  "summary\n📊",
 
122
  elem_classes=["summary-button"]
123
  )
124
 
125
+ history_view_button = Toggle(
126
+ label="History view",
127
+ value=False,
128
+ interactive=True,
129
+ elem_classes=["history-view-button"]
130
+ )
131
+
132
+
133
  # Model selection header (clickable toggle)
134
  model_toggle_button = gr.Button(
135
  f"► Select model ({len(Ci_results.available_models)})",
 
178
 
179
  # Detailed view components (hidden by default)
180
  with gr.Column(visible=False, elem_classes=["detail-view"]) as detail_view:
 
 
 
 
 
 
 
 
181
  # Create the plot output
182
  plot_output = gr.Plot(
183
  label="",
 
213
  # Time-series summary displays (multiple Gradio plots)
214
  time_series_failure_rates = gr.LinePlot(
215
  label="",
216
+ x="date",
217
+ y="failure_rate",
218
+ color="platform",
219
+ color_map={"AMD": "#FF6B6B", "NVIDIA": "#4ECDC4"},
220
+ title="Overall Failure Rates Over Time",
221
+ tooltip=["failure_rate", "date", "change"],
222
+ height=300,
223
+ x_label_angle=45,
224
+ y_title="Failure Rate (%)",
225
  elem_classes=["plot-container"]
226
  )
227
 
228
  time_series_amd_tests = gr.LinePlot(
229
  label="",
230
+ x="date",
231
+ y="count",
232
+ color="test_type",
233
+ color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"},
234
+ title="AMD Test Results Over Time",
235
+ tooltip=["count", "date", "change"],
236
+ height=300,
237
+ x_label_angle=45,
238
+ y_title="Number of Tests",
239
  elem_classes=["plot-container"]
240
  )
241
 
242
  time_series_nvidia_tests = gr.LinePlot(
243
  label="",
244
+ x="date",
245
+ y="count",
246
+ color="test_type",
247
+ color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"},
248
+ title="NVIDIA Test Results Over Time",
249
+ tooltip=["count", "date", "change"],
250
+ height=300,
251
+ x_label_angle=45,
252
+ y_title="Number of Tests",
253
  elem_classes=["plot-container"]
254
  )
255
 
256
  # Time-series model view (hidden by default)
257
  with gr.Column(visible=False, elem_classes=["time-series-detail-view"]) as time_series_detail_view:
 
 
 
 
 
 
 
 
258
  # Time-series plots for specific model (with spacing)
259
  time_series_amd_model_plot = gr.LinePlot(
260
  label="",
261
+ x="date",
262
+ y="count",
263
+ color="test_type",
264
+ color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"},
265
+ title="AMD Results Over Time",
266
+ tooltip=["count", "date", "change"],
267
+ height=300,
268
+ x_label_angle=45,
269
+ y_title="Number of Tests",
270
  elem_classes=["plot-container"]
271
  )
272
 
273
  time_series_nvidia_model_plot = gr.LinePlot(
274
  label="",
275
+ x="date",
276
+ y="count",
277
+ color="test_type",
278
+ color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"},
279
+ title="NVIDIA Results Over Time",
280
+ tooltip=["count", "date", "change"],
281
+ height=300,
282
+ x_label_angle=45,
283
+ y_title="Number of Tests",
284
  elem_classes=["plot-container"]
285
  )
286
 
 
 
 
 
 
 
 
 
 
 
 
287
  # Model toggle functionality
288
  def toggle_model_list(current_visible):
289
  """Toggle the visibility of the model list."""
 
302
 
303
  # Track model list visibility state
304
  model_list_visible = gr.State(False)
305
+ # Track last selected model for mode switches
306
+ selected_model_state = gr.State(None)
307
+ # Track whether current view is model detail (True) or summary (False)
308
+ in_model_view_state = gr.State(False)
309
 
310
  model_toggle_button.click(
311
  fn=toggle_model_list,
 
313
  outputs=[model_toggle_button, model_list_container, model_list_visible]
314
  )
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
+ # Unified summary handler: respects History toggle
318
+ def handle_summary_click(history_mode: bool):
319
+ description = get_description_text()
320
+ links = get_ci_links()
321
+ fr_plot, amd_plot, nvidia_plot = get_historical_summary_plots()
322
+ if history_mode:
323
+ return (
324
+ description,
325
+ links,
326
+ gr.update(visible=False), # current_view
327
+ gr.update(visible=True), # historical_view
328
+ gr.update(visible=False), # summary_display
329
+ gr.update(visible=False), # detail_view
330
+ fr_plot,
331
+ amd_plot,
332
+ nvidia_plot,
333
+ gr.update(visible=False), # time_series_detail_view
334
+ False, # in_model_view_state
335
+ )
336
+ else:
337
+ fig = create_summary_page(Ci_results.df, Ci_results.available_models)
338
+ return (
339
+ description,
340
+ links,
341
+ gr.update(visible=True), # current_view
342
+ gr.update(visible=False), # historical_view
343
+ gr.update(value=fig, visible=True), # summary_display
344
+ gr.update(visible=False), # detail_view
345
+ gr.update(visible=False), # time_series_failure_rates
346
+ gr.update(visible=False), # time_series_amd_tests
347
+ gr.update(visible=False), # time_series_nvidia_tests
348
+ gr.update(visible=False), # time_series_detail_view
349
+ False, # in_model_view_state
350
+ )
351
 
352
  summary_button.click(
353
+ fn=handle_summary_click,
354
+ inputs=[history_view_button],
355
+ outputs=[
356
+ description_display,
357
+ ci_links_display,
358
+ current_view,
359
+ historical_view,
360
+ summary_display,
361
+ detail_view,
362
+ time_series_failure_rates,
363
+ time_series_amd_tests,
364
+ time_series_nvidia_tests,
365
+ time_series_detail_view,
366
+ in_model_view_state,
367
+ ],
368
  )
369
 
370
  # Function to get CI job links
 
437
  return "🔗 **CI Jobs:** *Error loading links*\n\n❓ **[FAQ](README.md)**"
438
 
439
 
440
+
441
+
442
+ def get_historical_summary_plots():
443
+ """Get historical summary plots from preloaded data."""
444
+ dfs = get_time_series_summary_dfs(Ci_results.historical_df)
445
+ return (
446
+ gr.update(value=dfs['failure_rates_df'], visible=True),
447
+ gr.update(value=dfs['amd_tests_df'], visible=True),
448
+ gr.update(value=dfs['nvidia_tests_df'], visible=True),
449
+ )
450
+
451
+ def handle_history_toggle(history_mode, last_selected_model, in_model_view):
452
+ if history_mode:
453
+ # If currently in model view and valid model, show historical model detail
454
+ if in_model_view and last_selected_model:
455
+ amd_ts, nvidia_ts = show_time_series_model(last_selected_model)
456
+ return (
457
+ gr.update(visible=False), # current_view
458
+ gr.update(visible=True), # historical_view
459
+ gr.update(visible=False), # summary_display
460
+ gr.update(visible=False), # detail_view
461
+ gr.update(visible=False), # time_series_failure_rates
462
+ gr.update(visible=False), # time_series_amd_tests
463
+ gr.update(visible=False), # time_series_nvidia_tests
464
+ amd_ts, # time_series_amd_model_plot
465
+ nvidia_ts, # time_series_nvidia_model_plot
466
+ gr.update(visible=True), # time_series_detail_view
467
+ gr.update(), # plot_output
468
+ gr.update(), # amd_failed_tests_output
469
+ gr.update(), # nvidia_failed_tests_output
470
+ True, # in_model_view_state (still in model view)
471
+ )
472
+ # Otherwise show historical summary
473
+ fr_plot, amd_plot, nvidia_plot = get_historical_summary_plots()
474
+ return (
475
+ gr.update(visible=False), # current_view
476
+ gr.update(visible=True), # historical_view
477
+ gr.update(visible=False), # summary_display
478
+ gr.update(visible=False), # detail_view
479
+ fr_plot, # time_series_failure_rates (value + keep visibility)
480
+ amd_plot, # time_series_amd_tests
481
+ nvidia_plot, # time_series_nvidia_tests
482
+ gr.update(), # time_series_amd_model_plot
483
+ gr.update(), # time_series_nvidia_model_plot
484
+ gr.update(visible=False), # time_series_detail_view
485
+ gr.update(), # plot_output
486
+ gr.update(), # amd_failed_tests_output
487
+ gr.update(), # nvidia_failed_tests_output
488
+ False, # in_model_view_state
489
+ )
 
 
 
 
 
 
 
 
490
  else:
491
+ # Switch to current mode: show model if selected; otherwise summary
492
+ if last_selected_model and Ci_results.df is not None and not Ci_results.df.empty and last_selected_model in Ci_results.df.index:
493
+ fig, amd_txt, nvidia_txt = plot_model_stats(Ci_results.df, last_selected_model)
494
+ return (
495
+ gr.update(visible=True), # current_view
496
+ gr.update(visible=False), # historical_view
497
+ gr.update(visible=False), # summary_display
498
+ gr.update(visible=True), # detail_view
499
+ gr.update(visible=False), # time_series_failure_rates
500
+ gr.update(visible=False), # time_series_amd_tests
501
+ gr.update(visible=False), # time_series_nvidia_tests
502
+ gr.update(), # time_series_amd_model_plot
503
+ gr.update(), # time_series_nvidia_model_plot
504
+ gr.update(visible=False), # time_series_detail_view
505
+ fig, # plot_output
506
+ amd_txt, # amd_failed_tests_output
507
+ nvidia_txt, # nvidia_failed_tests_output
508
+ True, # in_model_view_state
509
+ )
510
+ else:
511
+ fig = create_summary_page(Ci_results.df, Ci_results.available_models)
512
+ return (
513
+ gr.update(visible=True), # current_view
514
+ gr.update(visible=False), # historical_view
515
+ gr.update(value=fig, visible=True), # summary_display
516
+ gr.update(visible=False), # detail_view
517
+ gr.update(visible=False), # time_series_failure_rates
518
+ gr.update(visible=False), # time_series_amd_tests
519
+ gr.update(visible=False), # time_series_nvidia_tests
520
+ gr.update(), # time_series_amd_model_plot
521
+ gr.update(), # time_series_nvidia_model_plot
522
+ gr.update(visible=False), # time_series_detail_view
523
+ gr.update(), # plot_output
524
+ gr.update(), # amd_failed_tests_output
525
+ gr.update(), # nvidia_failed_tests_output
526
+ False, # in_model_view_state
527
+ )
528
 
529
+ history_view_button.change(
530
+ fn=handle_history_toggle,
531
+ inputs=[history_view_button, selected_model_state, in_model_view_state],
532
+ outputs=[
533
+ current_view,
534
+ historical_view,
535
+ summary_display,
536
+ detail_view,
537
+ time_series_failure_rates,
538
+ time_series_amd_tests,
539
+ time_series_nvidia_tests,
540
+ time_series_amd_model_plot,
541
+ time_series_nvidia_model_plot,
542
+ time_series_detail_view,
543
+ plot_output,
544
+ amd_failed_tests_output,
545
+ nvidia_failed_tests_output,
546
+ in_model_view_state,
547
+ ],
548
  )
549
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
 
551
  # Time-series model selection functionality
552
  def show_time_series_model(selected_model):
553
  """Show time-series view for a specific model."""
554
+ dfs = get_model_time_series_dfs(Ci_results.historical_df, selected_model)
555
+ return (
556
+ gr.update(value=dfs['amd_df'], visible=True, title=f"{selected_model.upper()} - AMD Results Over Time"),
557
+ gr.update(value=dfs['nvidia_df'], visible=True, title=f"{selected_model.upper()} - NVIDIA Results Over Time"),
558
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
 
560
+ # Unified model click handler: respects History toggle
561
+ def handle_model_click(selected_model: str, history_mode: bool):
562
+ if history_mode:
563
+ amd_ts, nvidia_ts = show_time_series_model(selected_model)
564
+ return (
565
+ gr.update(), # plot_output
566
+ gr.update(), # amd_failed_tests_output
567
+ gr.update(), # nvidia_failed_tests_output
568
+ gr.update(visible=False), # current_view
569
+ gr.update(visible=True), # historical_view
570
+ gr.update(visible=False), # summary_display
571
+ gr.update(visible=False), # detail_view
572
+ gr.update(visible=False), # time_series_failure_rates
573
+ gr.update(visible=False), # time_series_amd_tests
574
+ gr.update(visible=False), # time_series_nvidia_tests
575
+ amd_ts, # time_series_amd_model_plot
576
+ nvidia_ts, # time_series_nvidia_model_plot
577
+ gr.update(visible=True), # time_series_detail_view
578
+ selected_model, True) # selected_model_state, in_model_view_state
579
+ else:
580
+ fig, amd_txt, nvidia_txt = plot_model_stats(Ci_results.df, selected_model)
581
+ return (
582
+ fig,
583
+ amd_txt,
584
+ nvidia_txt,
585
+ gr.update(visible=True), # current_view
586
+ gr.update(visible=False), # historical_view
587
+ gr.update(visible=False), # summary_display
588
+ gr.update(visible=True), # detail_view
589
+ gr.update(), # time_series_failure_rates
590
+ gr.update(), # time_series_amd_tests
591
+ gr.update(), # time_series_nvidia_tests
592
+ gr.update(), # time_series_amd_model_plot
593
+ gr.update(), # time_series_nvidia_model_plot
594
+ gr.update(visible=False), # time_series_detail_view
595
+ selected_model, True) # selected_model_state, in_model_view_state
596
 
 
597
  for i, btn in enumerate(model_buttons):
598
  model_name = model_choices[i]
 
 
 
 
 
 
 
 
 
 
 
599
  btn.click(
600
+ fn=lambda history_mode, m=model_name: handle_model_click(m, history_mode),
601
+ inputs=[history_view_button],
602
+ outputs=[
603
+ plot_output,
604
+ amd_failed_tests_output,
605
+ nvidia_failed_tests_output,
606
+ current_view,
607
+ historical_view,
608
+ summary_display,
609
+ detail_view,
610
+ time_series_failure_rates,
611
+ time_series_amd_tests,
612
+ time_series_nvidia_tests,
613
+ time_series_amd_model_plot,
614
+ time_series_nvidia_model_plot,
615
+ time_series_detail_view,
616
+ selected_model_state,
617
+ in_model_view_state,
618
+ ],
619
  )
620
 
621
  # Auto-update CI links when the interface loads
data.py CHANGED
@@ -57,6 +57,8 @@ def log_dataframe_link(link: str) -> str:
57
  Adds the link to the dataset in the logs, modifies it to get a clockable link and then returns the date of the
58
  report.
59
  """
 
 
60
  logger.info(f"Reading df located at {link}")
61
  # Make sure the links starts with an http adress
62
  if link.startswith("hf://"):
@@ -231,8 +233,10 @@ def get_data_for_date(target_date: str) -> tuple[pd.DataFrame, str]:
231
  return pd.DataFrame(), target_date
232
 
233
 
234
- def get_historical_data(start_date: str, end_date: str) -> pd.DataFrame:
235
  """Get historical data for a date range."""
 
 
236
  try:
237
  start_dt = datetime.strptime(start_date, "%Y-%m-%d")
238
  end_dt = datetime.strptime(end_date, "%Y-%m-%d")
@@ -256,11 +260,6 @@ def get_historical_data(start_date: str, end_date: str) -> pd.DataFrame:
256
 
257
  current_dt += timedelta(days=1)
258
 
259
- if not historical_data:
260
- logger.warning("No historical data found for the specified range, falling back to fake data")
261
- # Fall back to fake data when no real data is available
262
- return get_fake_historical_data(start_date, end_date)
263
-
264
  # Combine all dataframes
265
  combined_df = pd.concat(historical_data, ignore_index=False)
266
  return combined_df
@@ -418,6 +417,7 @@ class CIResults:
418
  self.available_dates = []
419
  self.historical_df = pd.DataFrame()
420
  self.all_historical_data = pd.DataFrame() # Store all historical data at startup
 
421
 
422
  def load_data(self) -> None:
423
  """Load data from the data source."""
@@ -442,16 +442,10 @@ class CIResults:
442
  "Falling back on sample data."
443
  ]
444
  logger.error("\n".join(error_msg))
 
445
  new_df, latest_update_msg = get_sample_data()
446
  self.latest_update_msg = latest_update_msg
447
- # Generate fake dates when no real dates are available
448
- fake_dates = []
449
- today = datetime.now()
450
- for i in range(7):
451
- date = today - timedelta(days=i)
452
- fake_dates.append(date.strftime("%Y-%m-%d"))
453
- self.available_dates = fake_dates
454
- logger.info(f"Generated {len(self.available_dates)} fake dates: {self.available_dates[:3]}...")
455
 
456
  # Update attributes
457
  self.df = new_df
@@ -481,15 +475,20 @@ class CIResults:
481
  """Load all available historical data at startup."""
482
  try:
483
  if not self.available_dates:
484
- logger.warning("No available dates found, skipping historical data loading")
485
- self.all_historical_data = pd.DataFrame()
486
- return
 
 
 
 
 
487
 
488
  logger.info(f"Loading all historical data for {len(self.available_dates)} dates...")
489
  start_date = self.available_dates[-1] # Oldest date
490
  end_date = self.available_dates[0] # Newest date
491
 
492
- self.all_historical_data = get_historical_data(start_date, end_date)
493
  logger.info(f"All historical data loaded: {len(self.all_historical_data)} records")
494
  except Exception as e:
495
  logger.error(f"Error loading all historical data: {e}")
 
57
  Adds the link to the dataset in the logs, modifies it to get a clockable link and then returns the date of the
58
  report.
59
  """
60
+ if link.startswith("sample_"):
61
+ return "9999-99-99"
62
  logger.info(f"Reading df located at {link}")
63
  # Make sure the links starts with an http adress
64
  if link.startswith("hf://"):
 
233
  return pd.DataFrame(), target_date
234
 
235
 
236
+ def get_historical_data(start_date: str, end_date: str, sample_data = False) -> pd.DataFrame:
237
  """Get historical data for a date range."""
238
+ if sample_data:
239
+ return get_fake_historical_data(start_date, end_date)
240
  try:
241
  start_dt = datetime.strptime(start_date, "%Y-%m-%d")
242
  end_dt = datetime.strptime(end_date, "%Y-%m-%d")
 
260
 
261
  current_dt += timedelta(days=1)
262
 
 
 
 
 
 
263
  # Combine all dataframes
264
  combined_df = pd.concat(historical_data, ignore_index=False)
265
  return combined_df
 
417
  self.available_dates = []
418
  self.historical_df = pd.DataFrame()
419
  self.all_historical_data = pd.DataFrame() # Store all historical data at startup
420
+ self.sample_data = False
421
 
422
  def load_data(self) -> None:
423
  """Load data from the data source."""
 
442
  "Falling back on sample data."
443
  ]
444
  logger.error("\n".join(error_msg))
445
+ self.sample_data = True
446
  new_df, latest_update_msg = get_sample_data()
447
  self.latest_update_msg = latest_update_msg
448
+ self.available_dates = None
 
 
 
 
 
 
 
449
 
450
  # Update attributes
451
  self.df = new_df
 
475
  """Load all available historical data at startup."""
476
  try:
477
  if not self.available_dates:
478
+ # Generate fake dates when no real dates are available
479
+ fake_dates = []
480
+ today = datetime.now()
481
+ for i in range(7):
482
+ date = today - timedelta(days=i)
483
+ fake_dates.append(date.strftime("%Y-%m-%d"))
484
+ self.available_dates = fake_dates
485
+ logger.info(f"No available dates found, generated {len(self.available_dates)} sample dates.")
486
 
487
  logger.info(f"Loading all historical data for {len(self.available_dates)} dates...")
488
  start_date = self.available_dates[-1] # Oldest date
489
  end_date = self.available_dates[0] # Newest date
490
 
491
+ self.all_historical_data = get_historical_data(start_date, end_date, self.sample_data)
492
  logger.info(f"All historical data loaded: {len(self.all_historical_data)} records")
493
  except Exception as e:
494
  logger.error(f"Error loading all historical data: {e}")
requirements.txt CHANGED
@@ -1 +1,2 @@
1
  matplotlib>=3.8
 
 
1
  matplotlib>=3.8
2
+ gradio_toggle
styles.css CHANGED
@@ -175,6 +175,25 @@ div[data-testid="column"]:has(.sidebar) {
175
  transition: max-height 0.3s ease !important;
176
  }
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
  /* Model button styling */
180
  .model-button {
@@ -373,53 +392,28 @@ div[data-testid="column"]:has(.sidebar) {
373
 
374
  /* Plot container with smooth transitions and controlled scrolling */
375
  .plot-container {
376
- background-color: #000000 !important;
377
  border: none !important;
378
  transition: opacity 0.6s ease-in-out !important;
379
  flex: 1 1 auto !important;
380
  min-height: 0 !important;
381
  overflow-y: auto !important;
382
  scrollbar-width: thin !important;
383
- scrollbar-color: #333333 #000000 !important;
384
  padding: 0 !important;
385
  }
386
 
387
  /* Custom scrollbar for plot container */
388
  .plot-container::-webkit-scrollbar {
389
  width: 8px !important;
390
- background: #000000 !important;
391
- }
392
-
393
- .plot-container::-webkit-scrollbar-track {
394
- background: #000000 !important;
395
- }
396
-
397
- .plot-container::-webkit-scrollbar-thumb {
398
- background-color: #333333 !important;
399
- border-radius: 4px !important;
400
- }
401
-
402
- .plot-container::-webkit-scrollbar-thumb:hover {
403
- background-color: #555555 !important;
404
  }
405
 
406
- /* Gradio plot component styling */
407
- .gr-plot {
408
- background-color: #000000 !important;
409
- transition: opacity 0.6s ease-in-out !important;
410
- }
411
 
412
- .gr-plot .gradio-plot {
413
- background-color: #000000 !important;
414
- transition: opacity 0.6s ease-in-out !important;
415
- }
416
 
417
  .gr-plot img {
418
  transition: opacity 0.6s ease-in-out !important;
419
  }
420
 
421
  /* Target the plot wrapper */
422
- div[data-testid="plot"] {
423
  background-color: #000000 !important;
424
  }
425
 
@@ -430,11 +424,6 @@ div[data-testid="plot"] {
430
  background-color: #000000 !important;
431
  }
432
 
433
- /* Ensure plot area background */
434
- .gr-plot > div,
435
- .plot-container > div {
436
- background-color: #000000 !important;
437
- }
438
 
439
  /* Prevent white flash during plot updates */
440
  .plot-container::before {
@@ -448,24 +437,26 @@ div[data-testid="plot"] {
448
  z-index: -1;
449
  }
450
 
451
- /* Force all plot elements to have black background */
452
- .plot-container *,
453
- .gr-plot *,
454
- div[data-testid="plot"] * {
455
- background-color: #000000 !important;
456
  }
457
 
458
- /* Override any white backgrounds in matplotlib */
459
- .plot-container canvas,
460
- .gr-plot canvas {
461
- background-color: #000000 !important;
462
- }
463
 
464
  /* Text elements */
465
  h1, h2, h3, p, .markdown {
466
  color: white !important;
467
  }
468
 
 
 
 
 
 
 
 
 
 
 
469
  /* Sidebar header enhancement */
470
  .sidebar h1 {
471
  background: linear-gradient(45deg, #74b9ff, #a29bfe) !important;
 
175
  transition: max-height 0.3s ease !important;
176
  }
177
 
178
+ .history-view-button {
179
+ background: linear-gradient(135deg, #2a2a2a, #1e1e1e) !important;
180
+ color: white !important;
181
+ margin: 0px 0px !important;
182
+ padding: 8px 12px !important;
183
+ font-weight: 600 !important;
184
+ font-size: 14px !important;
185
+ text-transform: uppercase !important;
186
+ letter-spacing: 0.3px !important;
187
+ font-family: monospace !important;
188
+ width: 100% !important;
189
+ max-width: 100% !important;
190
+ white-space: nowrap !important;
191
+ text-overflow: ellipsis !important;
192
+ display: block !important;
193
+ cursor: pointer !important;
194
+ transition: all 0.3s ease !important;
195
+ }
196
+
197
 
198
  /* Model button styling */
199
  .model-button {
 
392
 
393
  /* Plot container with smooth transitions and controlled scrolling */
394
  .plot-container {
 
395
  border: none !important;
396
  transition: opacity 0.6s ease-in-out !important;
397
  flex: 1 1 auto !important;
398
  min-height: 0 !important;
399
  overflow-y: auto !important;
400
  scrollbar-width: thin !important;
 
401
  padding: 0 !important;
402
  }
403
 
404
  /* Custom scrollbar for plot container */
405
  .plot-container::-webkit-scrollbar {
406
  width: 8px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  }
408
 
 
 
 
 
 
409
 
 
 
 
 
410
 
411
  .gr-plot img {
412
  transition: opacity 0.6s ease-in-out !important;
413
  }
414
 
415
  /* Target the plot wrapper */
416
+ div[data-testid="matplotlib"] {
417
  background-color: #000000 !important;
418
  }
419
 
 
424
  background-color: #000000 !important;
425
  }
426
 
 
 
 
 
 
427
 
428
  /* Prevent white flash during plot updates */
429
  .plot-container::before {
 
437
  z-index: -1;
438
  }
439
 
440
+ .vega-embed {
441
+ position: absolute !important;
 
 
 
442
  }
443
 
 
 
 
 
 
444
 
445
  /* Text elements */
446
  h1, h2, h3, p, .markdown {
447
  color: white !important;
448
  }
449
 
450
+ .toggle {
451
+ margin: 0 auto !important;
452
+ }
453
+
454
+ .toggle-label {
455
+ color: white !important;
456
+ font-family: monospace !important;
457
+ font-size: 14px !important;
458
+ }
459
+
460
  /* Sidebar header enhancement */
461
  .sidebar h1 {
462
  background: linear-gradient(45deg, #74b9ff, #a29bfe) !important;
time_series_gradio.py CHANGED
@@ -4,6 +4,126 @@ from datetime import datetime
4
  from data import extract_model_data
5
  import gradio as gr
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  def create_time_series_summary_gradio(historical_df: pd.DataFrame) -> dict:
8
  """Create time-series visualization for overall failure rates over time using Gradio native plots."""
9
  if historical_df.empty or 'date' not in historical_df.columns:
 
4
  from data import extract_model_data
5
  import gradio as gr
6
 
7
+ def get_time_series_summary_dfs(historical_df: pd.DataFrame) -> dict:
8
+ """Return dataframes for historical summary plots (failure rates, AMD tests, NVIDIA tests)."""
9
+
10
+ # Group by date to get daily statistics
11
+ daily_stats = []
12
+ dates = sorted(historical_df['date'].unique())
13
+ for date in dates:
14
+ date_data = historical_df[historical_df['date'] == date]
15
+ amd_passed = date_data['success_amd'].sum() if 'success_amd' in date_data.columns else 0
16
+ amd_failed = (date_data['failed_multi_no_amd'].sum() + date_data['failed_single_no_amd'].sum()) if 'failed_multi_no_amd' in date_data.columns else 0
17
+ amd_skipped = date_data['skipped_amd'].sum() if 'skipped_amd' in date_data.columns else 0
18
+ amd_total = amd_passed + amd_failed + amd_skipped
19
+ amd_failure_rate = (amd_failed / amd_total * 100) if amd_total > 0 else 0
20
+
21
+ nvidia_passed = date_data['success_nvidia'].sum() if 'success_nvidia' in date_data.columns else 0
22
+ nvidia_failed = (date_data['failed_multi_no_nvidia'].sum() + date_data['failed_single_no_nvidia'].sum()) if 'failed_multi_no_nvidia' in date_data.columns else 0
23
+ nvidia_skipped = date_data['skipped_nvidia'].sum() if 'skipped_nvidia' in date_data.columns else 0
24
+ nvidia_total = nvidia_passed + nvidia_failed + nvidia_skipped
25
+ nvidia_failure_rate = (nvidia_failed / nvidia_total * 100) if nvidia_total > 0 else 0
26
+
27
+ daily_stats.append({
28
+ 'date': date,
29
+ 'amd_failure_rate': amd_failure_rate,
30
+ 'nvidia_failure_rate': nvidia_failure_rate,
31
+ 'amd_passed': amd_passed,
32
+ 'amd_failed': amd_failed,
33
+ 'amd_skipped': amd_skipped,
34
+ 'nvidia_passed': nvidia_passed,
35
+ 'nvidia_failed': nvidia_failed,
36
+ 'nvidia_skipped': nvidia_skipped
37
+ })
38
+
39
+ # Failure rate dataframe
40
+ failure_rate_data = []
41
+ for i, stat in enumerate(daily_stats):
42
+ amd_change = stat['amd_failure_rate'] - daily_stats[i-1]['amd_failure_rate'] if i > 0 else 0
43
+ nvidia_change = stat['nvidia_failure_rate'] - daily_stats[i-1]['nvidia_failure_rate'] if i > 0 else 0
44
+ failure_rate_data.extend([
45
+ {'date': stat['date'], 'failure_rate': stat['amd_failure_rate'], 'platform': 'AMD', 'change': amd_change},
46
+ {'date': stat['date'], 'failure_rate': stat['nvidia_failure_rate'], 'platform': 'NVIDIA', 'change': nvidia_change}
47
+ ])
48
+ failure_rate_df = pd.DataFrame(failure_rate_data)
49
+
50
+ # AMD tests dataframe
51
+ amd_data = []
52
+ for i, stat in enumerate(daily_stats):
53
+ passed_change = stat['amd_passed'] - daily_stats[i-1]['amd_passed'] if i > 0 else 0
54
+ failed_change = stat['amd_failed'] - daily_stats[i-1]['amd_failed'] if i > 0 else 0
55
+ skipped_change = stat['amd_skipped'] - daily_stats[i-1]['amd_skipped'] if i > 0 else 0
56
+ amd_data.extend([
57
+ {'date': stat['date'], 'count': stat['amd_passed'], 'test_type': 'Passed', 'change': passed_change},
58
+ {'date': stat['date'], 'count': stat['amd_failed'], 'test_type': 'Failed', 'change': failed_change},
59
+ {'date': stat['date'], 'count': stat['amd_skipped'], 'test_type': 'Skipped', 'change': skipped_change}
60
+ ])
61
+ amd_df = pd.DataFrame(amd_data)
62
+
63
+ # NVIDIA tests dataframe
64
+ nvidia_data = []
65
+ for i, stat in enumerate(daily_stats):
66
+ passed_change = stat['nvidia_passed'] - daily_stats[i-1]['nvidia_passed'] if i > 0 else 0
67
+ failed_change = stat['nvidia_failed'] - daily_stats[i-1]['nvidia_failed'] if i > 0 else 0
68
+ skipped_change = stat['nvidia_skipped'] - daily_stats[i-1]['nvidia_skipped'] if i > 0 else 0
69
+ nvidia_data.extend([
70
+ {'date': stat['date'], 'count': stat['nvidia_passed'], 'test_type': 'Passed', 'change': passed_change},
71
+ {'date': stat['date'], 'count': stat['nvidia_failed'], 'test_type': 'Failed', 'change': failed_change},
72
+ {'date': stat['date'], 'count': stat['nvidia_skipped'], 'test_type': 'Skipped', 'change': skipped_change}
73
+ ])
74
+ nvidia_df = pd.DataFrame(nvidia_data)
75
+
76
+ return {
77
+ 'failure_rates_df': failure_rate_df,
78
+ 'amd_tests_df': amd_df,
79
+ 'nvidia_tests_df': nvidia_df,
80
+ }
81
+
82
+ def get_model_time_series_dfs(historical_df: pd.DataFrame, model_name: str) -> dict:
83
+ """Return dataframes for a specific model's historical plots (AMD, NVIDIA)."""
84
+ model_data = historical_df[historical_df.index.str.lower() == model_name.lower()]
85
+
86
+ if model_data.empty:
87
+ empty_df = pd.DataFrame({'date': [], 'count': [], 'test_type': [], 'change': []})
88
+ return {'amd_df': empty_df.copy(), 'nvidia_df': empty_df.copy()}
89
+
90
+ dates = sorted(model_data['date'].unique())
91
+ amd_data = []
92
+ nvidia_data = []
93
+ for i, date in enumerate(dates):
94
+ date_data = model_data[model_data['date'] == date]
95
+ row = date_data.iloc[0]
96
+
97
+ amd_passed = row.get('success_amd', 0)
98
+ amd_failed = row.get('failed_multi_no_amd', 0) + row.get('failed_single_no_amd', 0)
99
+ amd_skipped = row.get('skipped_amd', 0)
100
+ prev_row = model_data[model_data['date'] == dates[i-1]].iloc[0] if i > 0 and not model_data[model_data['date'] == dates[i-1]].empty else None
101
+ amd_passed_change = amd_passed - (prev_row.get('success_amd', 0) if prev_row is not None else 0)
102
+ amd_failed_change = amd_failed - (prev_row.get('failed_multi_no_amd', 0) + prev_row.get('failed_single_no_amd', 0) if prev_row is not None else 0)
103
+ amd_skipped_change = amd_skipped - (prev_row.get('skipped_amd', 0) if prev_row is not None else 0)
104
+ amd_data.extend([
105
+ {'date': date, 'count': amd_passed, 'test_type': 'Passed', 'change': amd_passed_change},
106
+ {'date': date, 'count': amd_failed, 'test_type': 'Failed', 'change': amd_failed_change},
107
+ {'date': date, 'count': amd_skipped, 'test_type': 'Skipped', 'change': amd_skipped_change}
108
+ ])
109
+
110
+ nvidia_passed = row.get('success_nvidia', 0)
111
+ nvidia_failed = row.get('failed_multi_no_nvidia', 0) + row.get('failed_single_no_nvidia', 0)
112
+ nvidia_skipped = row.get('skipped_nvidia', 0)
113
+ if prev_row is not None:
114
+ prev_nvidia_passed = prev_row.get('success_nvidia', 0)
115
+ prev_nvidia_failed = prev_row.get('failed_multi_no_nvidia', 0) + prev_row.get('failed_single_no_nvidia', 0)
116
+ prev_nvidia_skipped = prev_row.get('skipped_nvidia', 0)
117
+ else:
118
+ prev_nvidia_passed = prev_nvidia_failed = prev_nvidia_skipped = 0
119
+ nvidia_data.extend([
120
+ {'date': date, 'count': nvidia_passed, 'test_type': 'Passed', 'change': nvidia_passed - prev_nvidia_passed},
121
+ {'date': date, 'count': nvidia_failed, 'test_type': 'Failed', 'change': nvidia_failed - prev_nvidia_failed},
122
+ {'date': date, 'count': nvidia_skipped, 'test_type': 'Skipped', 'change': nvidia_skipped - prev_nvidia_skipped}
123
+ ])
124
+
125
+ return {'amd_df': pd.DataFrame(amd_data), 'nvidia_df': pd.DataFrame(nvidia_data)}
126
+
127
  def create_time_series_summary_gradio(historical_df: pd.DataFrame) -> dict:
128
  """Create time-series visualization for overall failure rates over time using Gradio native plots."""
129
  if historical_df.empty or 'date' not in historical_df.columns: