gauravlochab commited on
Commit
464321b
·
1 Parent(s): 7ac1cac

chore: add line for ajusted apr graph button to select the graph line

Browse files
Files changed (1) hide show
  1. app.py +222 -61
app.py CHANGED
@@ -156,7 +156,7 @@ def get_agent_name(agent_id: int, agents: List[Dict[str, Any]]) -> str:
156
  return "Unknown"
157
 
158
  def extract_apr_value(attr: Dict[str, Any]) -> Dict[str, Any]:
159
- """Extract APR value and timestamp from JSON value"""
160
  try:
161
  agent_id = attr.get("agent_id", "unknown")
162
  logger.debug(f"Extracting APR value for agent {agent_id}")
@@ -164,7 +164,7 @@ def extract_apr_value(attr: Dict[str, Any]) -> Dict[str, Any]:
164
  # The APR value is stored in the json_value field
165
  if attr["json_value"] is None:
166
  logger.debug(f"Agent {agent_id}: json_value is None")
167
- return {"apr": None, "timestamp": None, "agent_id": agent_id, "is_dummy": False}
168
 
169
  # If json_value is a string, parse it
170
  if isinstance(attr["json_value"], str):
@@ -174,22 +174,23 @@ def extract_apr_value(attr: Dict[str, Any]) -> Dict[str, Any]:
174
  json_data = attr["json_value"]
175
 
176
  apr = json_data.get("apr")
 
177
  timestamp = json_data.get("timestamp")
178
 
179
- logger.debug(f"Agent {agent_id}: Raw APR value: {apr}, timestamp: {timestamp}")
180
 
181
  # Convert timestamp to datetime if it exists
182
  timestamp_dt = None
183
  if timestamp:
184
  timestamp_dt = datetime.fromtimestamp(timestamp)
185
 
186
- result = {"apr": apr, "timestamp": timestamp_dt, "agent_id": agent_id, "is_dummy": False}
187
  logger.debug(f"Agent {agent_id}: Extracted result: {result}")
188
  return result
189
  except (json.JSONDecodeError, KeyError, TypeError) as e:
190
  logger.error(f"Error parsing JSON value: {e} for agent_id: {attr.get('agent_id')}")
191
  logger.error(f"Problematic json_value: {attr.get('json_value')}")
192
- return {"apr": None, "timestamp": None, "agent_id": attr.get('agent_id'), "is_dummy": False}
193
 
194
  def fetch_apr_data_from_db():
195
  """
@@ -278,6 +279,22 @@ def fetch_apr_data_from_db():
278
  logger.info(f"Created DataFrame with {len(global_df)} rows")
279
  logger.info(f"DataFrame columns: {global_df.columns.tolist()}")
280
  logger.info(f"APR statistics: min={global_df['apr'].min()}, max={global_df['apr'].max()}, mean={global_df['apr'].mean()}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  # All values are APR type (excluding zero and -100 values)
282
  logger.info("All values are APR type (excluding zero and -100 values)")
283
  logger.info(f"Agents count: {global_df['agent_name'].value_counts().to_dict()}")
@@ -622,46 +639,38 @@ def create_combined_time_series_graph(df):
622
  avg_apr_data_with_ma = avg_apr_data.copy()
623
  avg_apr_data_with_ma['moving_avg'] = None # Initialize the moving average column
624
 
625
- # Define the time window for the moving average (6 hours)
626
- time_window = pd.Timedelta(hours=6)
627
  logger.info(f"Calculating moving average with time window of {time_window}")
628
 
629
- # Calculate two moving averages: one with 2-hour window and one with infinite window
630
- avg_apr_data_with_ma['moving_avg'] = None # 2-hour window
631
- avg_apr_data_with_ma['infinite_avg'] = None # Infinite window (all data up to timestamp)
632
 
633
  # Calculate the moving averages for each timestamp
634
  for i, row in avg_apr_data_with_ma.iterrows():
635
  current_time = row['timestamp']
636
  window_start = current_time - time_window
637
 
638
- # Get all data points within the 2-hour time window
639
  window_data = apr_data_sorted[
640
  (apr_data_sorted['timestamp'] >= window_start) &
641
  (apr_data_sorted['timestamp'] <= current_time)
642
  ]
643
 
644
- # Get all data points up to the current timestamp (infinite window)
645
- infinite_window_data = apr_data_sorted[
646
- apr_data_sorted['timestamp'] <= current_time
647
- ]
648
-
649
- # Calculate the average APR for the 2-hour time window
650
  if not window_data.empty:
651
  avg_apr_data_with_ma.at[i, 'moving_avg'] = window_data['apr'].mean()
652
- logger.debug(f"Time window {window_start} to {current_time}: {len(window_data)} points, avg={window_data['apr'].mean()}")
 
 
 
 
 
653
  else:
654
  # If no data points in the window, use the current value
655
  avg_apr_data_with_ma.at[i, 'moving_avg'] = row['apr']
656
  logger.debug(f"No data points in time window for {current_time}, using current value {row['apr']}")
657
-
658
- # Calculate the average APR for the infinite window
659
- if not infinite_window_data.empty:
660
- avg_apr_data_with_ma.at[i, 'infinite_avg'] = infinite_window_data['apr'].mean()
661
- logger.debug(f"Infinite window up to {current_time}: {len(infinite_window_data)} points, avg={infinite_window_data['apr'].mean()}")
662
- else:
663
- # This should never happen, but just in case
664
- avg_apr_data_with_ma.at[i, 'infinite_avg'] = row['apr']
665
 
666
  logger.info(f"Calculated time-based moving averages with {len(avg_apr_data_with_ma)} points")
667
 
@@ -694,7 +703,7 @@ def create_combined_time_series_graph(df):
694
  # Determine if this agent should be visible by default
695
  is_visible = agent_name in top_agents
696
 
697
- # Add data points as markers
698
  fig.add_trace(
699
  go.Scatter(
700
  x=x_values,
@@ -706,23 +715,46 @@ def create_combined_time_series_graph(df):
706
  size=10,
707
  line=dict(width=1, color='black')
708
  ),
709
- name=f'Agent: {agent_name}',
710
  hovertemplate='Time: %{x}<br>APR: %{y:.2f}<br>Agent: ' + agent_name + '<extra></extra>',
711
  visible=is_visible # Only top agents visible by default
712
  )
713
  )
714
- logger.info(f"Added data points for agent {agent_name} with {len(x_values)} points (visible: {is_visible})")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
 
716
- # Add 2-hour moving average as a smooth line
717
  x_values_ma = avg_apr_data_with_ma['timestamp'].tolist()
718
  y_values_ma = avg_apr_data_with_ma['moving_avg'].tolist()
719
 
720
- # Create hover template for the 2-hour moving average line
721
- hover_data_2h = []
722
  for idx, row in avg_apr_data_with_ma.iterrows():
723
  timestamp = row['timestamp']
724
- hover_data_2h.append(
725
- f"Time: {timestamp}<br>Moving Avg APR (6h window): {row['moving_avg']:.2f}"
726
  )
727
 
728
  fig.add_trace(
@@ -731,36 +763,47 @@ def create_combined_time_series_graph(df):
731
  y=y_values_ma,
732
  mode='lines', # Only lines for moving average
733
  line=dict(color='red', width=2), # Thinner line
734
- name='Moving Average APR (6h window)',
735
- hovertext=hover_data_2h,
736
- hoverinfo='text'
 
737
  )
738
  )
739
- logger.info(f"Added 2-hour moving average APR trace with {len(x_values_ma)} points")
740
-
741
- # Add infinite window moving average as another line
742
- y_values_infinite = avg_apr_data_with_ma['infinite_avg'].tolist()
743
 
744
- # Create hover template for the infinite window moving average line
745
- hover_data_infinite = []
746
- for idx, row in avg_apr_data_with_ma.iterrows():
747
- timestamp = row['timestamp']
748
- hover_data_infinite.append(
749
- f"Time: {timestamp}<br>Cumulative Avg APR (all data): {row['infinite_avg']:.2f}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
750
  )
 
751
 
752
- fig.add_trace(
753
- go.Scatter(
754
- x=x_values_ma,
755
- y=y_values_infinite,
756
- mode='lines', # Only lines for moving average
757
- line=dict(color='green', width=4), # Thicker solid line
758
- name='Cumulative Average APR (all data)',
759
- hovertext=hover_data_infinite,
760
- hoverinfo='text'
761
- )
762
- )
763
- logger.info(f"Added infinite window moving average APR trace with {len(x_values_ma)} points")
764
 
765
  # Update layout - use simple boolean values everywhere
766
  # Increase the width and height for better visualization
@@ -1015,6 +1058,20 @@ def save_to_csv(df):
1015
  stats_df.to_csv(stats_csv, index=False)
1016
  logger.info(f"Statistics saved to {stats_csv}")
1017
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1018
  return csv_file
1019
 
1020
  def generate_statistics_from_data(df):
@@ -1039,6 +1096,9 @@ def generate_statistics_from_data(df):
1039
  perf_data = agent_data[agent_data['metric_type'] == 'Performance']
1040
  real_perf = perf_data[perf_data['is_dummy'] == False]
1041
 
 
 
 
1042
  stats = {
1043
  'agent_id': agent_id,
1044
  'agent_name': agent_name,
@@ -1051,6 +1111,9 @@ def generate_statistics_from_data(df):
1051
  'avg_performance': perf_data['apr'].mean() if not perf_data.empty else None,
1052
  'max_apr': apr_data['apr'].max() if not apr_data.empty else None,
1053
  'min_apr': apr_data['apr'].min() if not apr_data.empty else None,
 
 
 
1054
  'latest_timestamp': agent_data['timestamp'].max().strftime('%Y-%m-%d %H:%M:%S') if not agent_data.empty else None
1055
  }
1056
  stats_list.append(stats)
@@ -1059,6 +1122,9 @@ def generate_statistics_from_data(df):
1059
  apr_only = df[df['metric_type'] == 'APR']
1060
  perf_only = df[df['metric_type'] == 'Performance']
1061
 
 
 
 
1062
  overall_stats = {
1063
  'agent_id': 'ALL',
1064
  'agent_name': 'All Agents',
@@ -1071,6 +1137,9 @@ def generate_statistics_from_data(df):
1071
  'avg_performance': perf_only['apr'].mean() if not perf_only.empty else None,
1072
  'max_apr': apr_only['apr'].max() if not apr_only.empty else None,
1073
  'min_apr': apr_only['apr'].min() if not apr_only.empty else None,
 
 
 
1074
  'latest_timestamp': df['timestamp'].max().strftime('%Y-%m-%d %H:%M:%S') if not df.empty else None
1075
  }
1076
  stats_list.append(overall_stats)
@@ -1419,11 +1488,82 @@ def dashboard():
1419
  # Create container for plotly figure (combined graph only)
1420
  combined_graph = gr.Plot(label="APR for All Agents")
1421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1422
  # Function to update the graph
1423
- def update_apr_graph():
1424
  # Generate visualization and get figure object directly
1425
  try:
1426
  combined_fig, _ = generate_apr_visualizations()
 
 
 
 
 
 
 
 
 
1427
  return combined_fig
1428
  except Exception as e:
1429
  logger.exception("Error generating APR visualization")
@@ -1447,8 +1587,29 @@ def dashboard():
1447
  )
1448
  combined_graph.value = placeholder_fig
1449
 
 
 
 
 
 
 
 
 
1450
  # Set up the button click event
1451
- refresh_btn.click(fn=update_apr_graph, outputs=[combined_graph])
 
 
 
 
 
 
 
 
 
 
 
 
 
1452
 
1453
  return demo
1454
 
 
156
  return "Unknown"
157
 
158
  def extract_apr_value(attr: Dict[str, Any]) -> Dict[str, Any]:
159
+ """Extract APR value, adjusted APR value, and timestamp from JSON value"""
160
  try:
161
  agent_id = attr.get("agent_id", "unknown")
162
  logger.debug(f"Extracting APR value for agent {agent_id}")
 
164
  # The APR value is stored in the json_value field
165
  if attr["json_value"] is None:
166
  logger.debug(f"Agent {agent_id}: json_value is None")
167
+ return {"apr": None, "adjusted_apr": None, "timestamp": None, "agent_id": agent_id, "is_dummy": False}
168
 
169
  # If json_value is a string, parse it
170
  if isinstance(attr["json_value"], str):
 
174
  json_data = attr["json_value"]
175
 
176
  apr = json_data.get("apr")
177
+ adjusted_apr = json_data.get("adjusted_apr") # Extract adjusted_apr if present
178
  timestamp = json_data.get("timestamp")
179
 
180
+ logger.debug(f"Agent {agent_id}: Raw APR value: {apr}, adjusted APR value: {adjusted_apr}, timestamp: {timestamp}")
181
 
182
  # Convert timestamp to datetime if it exists
183
  timestamp_dt = None
184
  if timestamp:
185
  timestamp_dt = datetime.fromtimestamp(timestamp)
186
 
187
+ result = {"apr": apr, "adjusted_apr": adjusted_apr, "timestamp": timestamp_dt, "agent_id": agent_id, "is_dummy": False}
188
  logger.debug(f"Agent {agent_id}: Extracted result: {result}")
189
  return result
190
  except (json.JSONDecodeError, KeyError, TypeError) as e:
191
  logger.error(f"Error parsing JSON value: {e} for agent_id: {attr.get('agent_id')}")
192
  logger.error(f"Problematic json_value: {attr.get('json_value')}")
193
+ return {"apr": None, "adjusted_apr": None, "timestamp": None, "agent_id": attr.get('agent_id'), "is_dummy": False}
194
 
195
  def fetch_apr_data_from_db():
196
  """
 
279
  logger.info(f"Created DataFrame with {len(global_df)} rows")
280
  logger.info(f"DataFrame columns: {global_df.columns.tolist()}")
281
  logger.info(f"APR statistics: min={global_df['apr'].min()}, max={global_df['apr'].max()}, mean={global_df['apr'].mean()}")
282
+
283
+ # Log adjusted APR statistics if available
284
+ if 'adjusted_apr' in global_df.columns and global_df['adjusted_apr'].notna().any():
285
+ logger.info(f"Adjusted APR statistics: min={global_df['adjusted_apr'].min()}, max={global_df['adjusted_apr'].max()}, mean={global_df['adjusted_apr'].mean()}")
286
+ logger.info(f"Number of records with adjusted_apr: {global_df['adjusted_apr'].notna().sum()} out of {len(global_df)}")
287
+
288
+ # Log the difference between APR and adjusted APR
289
+ valid_rows = global_df[global_df['adjusted_apr'].notna()]
290
+ if not valid_rows.empty:
291
+ avg_diff = (valid_rows['apr'] - valid_rows['adjusted_apr']).mean()
292
+ max_diff = (valid_rows['apr'] - valid_rows['adjusted_apr']).max()
293
+ min_diff = (valid_rows['apr'] - valid_rows['adjusted_apr']).min()
294
+ logger.info(f"APR vs Adjusted APR difference: avg={avg_diff:.2f}, max={max_diff:.2f}, min={min_diff:.2f}")
295
+ else:
296
+ logger.info("No adjusted APR values found in the data")
297
+
298
  # All values are APR type (excluding zero and -100 values)
299
  logger.info("All values are APR type (excluding zero and -100 values)")
300
  logger.info(f"Agents count: {global_df['agent_name'].value_counts().to_dict()}")
 
639
  avg_apr_data_with_ma = avg_apr_data.copy()
640
  avg_apr_data_with_ma['moving_avg'] = None # Initialize the moving average column
641
 
642
+ # Define the time window for the moving average (3 days)
643
+ time_window = pd.Timedelta(days=3)
644
  logger.info(f"Calculating moving average with time window of {time_window}")
645
 
646
+ # Calculate moving averages: one for APR and one for adjusted APR
647
+ avg_apr_data_with_ma['moving_avg'] = None # 3-day window for APR
648
+ avg_apr_data_with_ma['adjusted_moving_avg'] = None # 3-day window for adjusted APR
649
 
650
  # Calculate the moving averages for each timestamp
651
  for i, row in avg_apr_data_with_ma.iterrows():
652
  current_time = row['timestamp']
653
  window_start = current_time - time_window
654
 
655
+ # Get all data points within the 3-day time window
656
  window_data = apr_data_sorted[
657
  (apr_data_sorted['timestamp'] >= window_start) &
658
  (apr_data_sorted['timestamp'] <= current_time)
659
  ]
660
 
661
+ # Calculate the average APR for the 3-day time window
 
 
 
 
 
662
  if not window_data.empty:
663
  avg_apr_data_with_ma.at[i, 'moving_avg'] = window_data['apr'].mean()
664
+ logger.debug(f"APR time window {window_start} to {current_time}: {len(window_data)} points, avg={window_data['apr'].mean()}")
665
+
666
+ # Calculate adjusted APR moving average if data exists
667
+ if 'adjusted_apr' in window_data.columns and window_data['adjusted_apr'].notna().any():
668
+ avg_apr_data_with_ma.at[i, 'adjusted_moving_avg'] = window_data['adjusted_apr'].mean()
669
+ logger.debug(f"Adjusted APR time window {window_start} to {current_time}: {len(window_data)} points, avg={window_data['adjusted_apr'].mean()}")
670
  else:
671
  # If no data points in the window, use the current value
672
  avg_apr_data_with_ma.at[i, 'moving_avg'] = row['apr']
673
  logger.debug(f"No data points in time window for {current_time}, using current value {row['apr']}")
 
 
 
 
 
 
 
 
674
 
675
  logger.info(f"Calculated time-based moving averages with {len(avg_apr_data_with_ma)} points")
676
 
 
703
  # Determine if this agent should be visible by default
704
  is_visible = agent_name in top_agents
705
 
706
+ # Add data points as markers for APR
707
  fig.add_trace(
708
  go.Scatter(
709
  x=x_values,
 
715
  size=10,
716
  line=dict(width=1, color='black')
717
  ),
718
+ name=f'Agent: {agent_name} (APR)',
719
  hovertemplate='Time: %{x}<br>APR: %{y:.2f}<br>Agent: ' + agent_name + '<extra></extra>',
720
  visible=is_visible # Only top agents visible by default
721
  )
722
  )
723
+ logger.info(f"Added APR data points for agent {agent_name} with {len(x_values)} points (visible: {is_visible})")
724
+
725
+ # Add data points for adjusted APR if it exists
726
+ if 'adjusted_apr' in agent_data.columns and agent_data['adjusted_apr'].notna().any():
727
+ x_values_adj = agent_data['timestamp'].tolist()
728
+ y_values_adj = agent_data['adjusted_apr'].tolist()
729
+
730
+ fig.add_trace(
731
+ go.Scatter(
732
+ x=x_values_adj,
733
+ y=y_values_adj,
734
+ mode='markers', # Only markers for original data
735
+ marker=dict(
736
+ color=color_map[agent_name],
737
+ symbol='diamond', # Different symbol for adjusted APR
738
+ size=10,
739
+ line=dict(width=1, color='black')
740
+ ),
741
+ name=f'Agent: {agent_name} (Adjusted APR)',
742
+ hovertemplate='Time: %{x}<br>Adjusted APR: %{y:.2f}<br>Agent: ' + agent_name + '<extra></extra>',
743
+ visible=is_visible # Only top agents visible by default
744
+ )
745
+ )
746
+ logger.info(f"Added Adjusted APR data points for agent {agent_name} with {len(x_values_adj)} points (visible: {is_visible})")
747
 
748
+ # Add APR moving average as a smooth line
749
  x_values_ma = avg_apr_data_with_ma['timestamp'].tolist()
750
  y_values_ma = avg_apr_data_with_ma['moving_avg'].tolist()
751
 
752
+ # Create hover template for the APR moving average line
753
+ hover_data_apr = []
754
  for idx, row in avg_apr_data_with_ma.iterrows():
755
  timestamp = row['timestamp']
756
+ hover_data_apr.append(
757
+ f"Time: {timestamp}<br>Moving Avg APR (3d window): {row['moving_avg']:.2f}"
758
  )
759
 
760
  fig.add_trace(
 
763
  y=y_values_ma,
764
  mode='lines', # Only lines for moving average
765
  line=dict(color='red', width=2), # Thinner line
766
+ name='Moving Average APR (3d window)',
767
+ hovertext=hover_data_apr,
768
+ hoverinfo='text',
769
+ visible=True # Visible by default
770
  )
771
  )
772
+ logger.info(f"Added 3-day moving average APR trace with {len(x_values_ma)} points")
 
 
 
773
 
774
+ # Add adjusted APR moving average line if it exists
775
+ if 'adjusted_moving_avg' in avg_apr_data_with_ma.columns and avg_apr_data_with_ma['adjusted_moving_avg'].notna().any():
776
+ y_values_adj_ma = avg_apr_data_with_ma['adjusted_moving_avg'].tolist()
777
+
778
+ # Create hover template for the adjusted APR moving average line
779
+ hover_data_adj = []
780
+ for idx, row in avg_apr_data_with_ma.iterrows():
781
+ timestamp = row['timestamp']
782
+ if pd.notna(row['adjusted_moving_avg']):
783
+ hover_data_adj.append(
784
+ f"Time: {timestamp}<br>Moving Avg Adjusted APR (3d window): {row['adjusted_moving_avg']:.2f}"
785
+ )
786
+ else:
787
+ hover_data_adj.append(
788
+ f"Time: {timestamp}<br>Moving Avg Adjusted APR (3d window): N/A"
789
+ )
790
+
791
+ fig.add_trace(
792
+ go.Scatter(
793
+ x=x_values_ma,
794
+ y=y_values_adj_ma,
795
+ mode='lines', # Only lines for moving average
796
+ line=dict(color='green', width=4), # Thicker solid line for adjusted APR
797
+ name='Moving Average Adjusted APR (3d window)',
798
+ hovertext=hover_data_adj,
799
+ hoverinfo='text',
800
+ visible=True # Visible by default
801
+ )
802
  )
803
+ logger.info(f"Added 3-day moving average Adjusted APR trace with {len(x_values_ma)} points")
804
 
805
+ # Removed cumulative APR as requested
806
+ logger.info("Cumulative APR graph line has been removed as requested")
 
 
 
 
 
 
 
 
 
 
807
 
808
  # Update layout - use simple boolean values everywhere
809
  # Increase the width and height for better visualization
 
1058
  stats_df.to_csv(stats_csv, index=False)
1059
  logger.info(f"Statistics saved to {stats_csv}")
1060
 
1061
+ # Log detailed statistics about adjusted APR
1062
+ if 'adjusted_apr' in df.columns and df['adjusted_apr'].notna().any():
1063
+ adjusted_stats = stats_df[stats_df['avg_adjusted_apr'].notna()]
1064
+ logger.info(f"Agents with adjusted APR data: {len(adjusted_stats)} out of {len(stats_df)}")
1065
+
1066
+ for _, row in adjusted_stats.iterrows():
1067
+ if row['agent_id'] != 'ALL': # Skip the overall stats row
1068
+ logger.info(f"Agent {row['agent_name']} adjusted APR stats: avg={row['avg_adjusted_apr']:.2f}, min={row['min_adjusted_apr']:.2f}, max={row['max_adjusted_apr']:.2f}")
1069
+
1070
+ # Log overall adjusted APR stats
1071
+ overall_row = stats_df[stats_df['agent_id'] == 'ALL']
1072
+ if not overall_row.empty and pd.notna(overall_row['avg_adjusted_apr'].iloc[0]):
1073
+ logger.info(f"Overall adjusted APR stats: avg={overall_row['avg_adjusted_apr'].iloc[0]:.2f}, min={overall_row['min_adjusted_apr'].iloc[0]:.2f}, max={overall_row['max_adjusted_apr'].iloc[0]:.2f}")
1074
+
1075
  return csv_file
1076
 
1077
  def generate_statistics_from_data(df):
 
1096
  perf_data = agent_data[agent_data['metric_type'] == 'Performance']
1097
  real_perf = perf_data[perf_data['is_dummy'] == False]
1098
 
1099
+ # Check if adjusted_apr exists and has non-null values
1100
+ has_adjusted_apr = 'adjusted_apr' in apr_data.columns and apr_data['adjusted_apr'].notna().any()
1101
+
1102
  stats = {
1103
  'agent_id': agent_id,
1104
  'agent_name': agent_name,
 
1111
  'avg_performance': perf_data['apr'].mean() if not perf_data.empty else None,
1112
  'max_apr': apr_data['apr'].max() if not apr_data.empty else None,
1113
  'min_apr': apr_data['apr'].min() if not apr_data.empty else None,
1114
+ 'avg_adjusted_apr': apr_data['adjusted_apr'].mean() if has_adjusted_apr else None,
1115
+ 'max_adjusted_apr': apr_data['adjusted_apr'].max() if has_adjusted_apr else None,
1116
+ 'min_adjusted_apr': apr_data['adjusted_apr'].min() if has_adjusted_apr else None,
1117
  'latest_timestamp': agent_data['timestamp'].max().strftime('%Y-%m-%d %H:%M:%S') if not agent_data.empty else None
1118
  }
1119
  stats_list.append(stats)
 
1122
  apr_only = df[df['metric_type'] == 'APR']
1123
  perf_only = df[df['metric_type'] == 'Performance']
1124
 
1125
+ # Check if adjusted_apr exists and has non-null values for overall stats
1126
+ has_adjusted_apr_overall = 'adjusted_apr' in apr_only.columns and apr_only['adjusted_apr'].notna().any()
1127
+
1128
  overall_stats = {
1129
  'agent_id': 'ALL',
1130
  'agent_name': 'All Agents',
 
1137
  'avg_performance': perf_only['apr'].mean() if not perf_only.empty else None,
1138
  'max_apr': apr_only['apr'].max() if not apr_only.empty else None,
1139
  'min_apr': apr_only['apr'].min() if not apr_only.empty else None,
1140
+ 'avg_adjusted_apr': apr_only['adjusted_apr'].mean() if has_adjusted_apr_overall else None,
1141
+ 'max_adjusted_apr': apr_only['adjusted_apr'].max() if has_adjusted_apr_overall else None,
1142
+ 'min_adjusted_apr': apr_only['adjusted_apr'].min() if has_adjusted_apr_overall else None,
1143
  'latest_timestamp': df['timestamp'].max().strftime('%Y-%m-%d %H:%M:%S') if not df.empty else None
1144
  }
1145
  stats_list.append(overall_stats)
 
1488
  # Create container for plotly figure (combined graph only)
1489
  combined_graph = gr.Plot(label="APR for All Agents")
1490
 
1491
+ # Create compact toggle controls at the bottom of the graph
1492
+ with gr.Row(visible=True):
1493
+ gr.Markdown("##### Toggle Graph Lines", elem_id="toggle_title")
1494
+
1495
+ with gr.Row():
1496
+ with gr.Column():
1497
+ with gr.Row(elem_id="toggle_container"):
1498
+ with gr.Column(scale=1, min_width=150):
1499
+ apr_toggle = gr.Checkbox(label="APR Moving Average", value=True, elem_id="apr_toggle")
1500
+
1501
+ with gr.Column(scale=1, min_width=150):
1502
+ adjusted_apr_toggle = gr.Checkbox(label="Adjusted APR Moving Average", value=True, elem_id="adjusted_apr_toggle")
1503
+
1504
+ # Add custom CSS for styling the toggle checkboxes
1505
+ gr.HTML("""
1506
+ <style>
1507
+ /* Style for toggle checkboxes */
1508
+ #apr_toggle .gr-checkbox {
1509
+ accent-color: #e74c3c !important;
1510
+ }
1511
+
1512
+ #adjusted_apr_toggle .gr-checkbox {
1513
+ accent-color: #2ecc71 !important;
1514
+ }
1515
+
1516
+ /* Make the toggle section more compact */
1517
+ #toggle_title {
1518
+ margin-bottom: 0;
1519
+ margin-top: 10px;
1520
+ }
1521
+
1522
+ #toggle_container {
1523
+ margin-top: 5px;
1524
+ }
1525
+
1526
+ /* Style the checkbox labels */
1527
+ .gr-form.gr-box {
1528
+ border: none !important;
1529
+ background: transparent !important;
1530
+ }
1531
+
1532
+ /* Make checkboxes and labels appear on the same line */
1533
+ .gr-checkbox-container {
1534
+ display: flex !important;
1535
+ align-items: center !important;
1536
+ }
1537
+
1538
+ /* Add colored indicators */
1539
+ #apr_toggle .gr-checkbox-label::before {
1540
+ content: "●";
1541
+ color: #e74c3c;
1542
+ margin-right: 5px;
1543
+ }
1544
+
1545
+ #adjusted_apr_toggle .gr-checkbox-label::before {
1546
+ content: "●";
1547
+ color: #2ecc71;
1548
+ margin-right: 5px;
1549
+ }
1550
+ </style>
1551
+ """)
1552
+
1553
  # Function to update the graph
1554
+ def update_apr_graph(show_apr_ma=True, show_adjusted_apr_ma=True):
1555
  # Generate visualization and get figure object directly
1556
  try:
1557
  combined_fig, _ = generate_apr_visualizations()
1558
+
1559
+ # Update visibility of traces based on toggle values
1560
+ for i, trace in enumerate(combined_fig.data):
1561
+ # Check if this is a moving average trace
1562
+ if trace.name == 'Moving Average APR (3d window)':
1563
+ trace.visible = show_apr_ma
1564
+ elif trace.name == 'Moving Average Adjusted APR (3d window)':
1565
+ trace.visible = show_adjusted_apr_ma
1566
+
1567
  return combined_fig
1568
  except Exception as e:
1569
  logger.exception("Error generating APR visualization")
 
1587
  )
1588
  combined_graph.value = placeholder_fig
1589
 
1590
+ # Function to update the graph based on toggle states
1591
+ def update_graph_with_toggles(apr_visible, adjusted_apr_visible):
1592
+ return update_apr_graph(apr_visible, adjusted_apr_visible)
1593
+
1594
+ # Function to update the graph without parameters (for refresh button)
1595
+ def refresh_graph():
1596
+ return update_apr_graph(apr_toggle.value, adjusted_apr_toggle.value)
1597
+
1598
  # Set up the button click event
1599
+ refresh_btn.click(fn=refresh_graph, inputs=None, outputs=[combined_graph])
1600
+
1601
+ # Set up the toggle switch events
1602
+ apr_toggle.change(
1603
+ fn=update_graph_with_toggles,
1604
+ inputs=[apr_toggle, adjusted_apr_toggle],
1605
+ outputs=[combined_graph]
1606
+ )
1607
+
1608
+ adjusted_apr_toggle.change(
1609
+ fn=update_graph_with_toggles,
1610
+ inputs=[apr_toggle, adjusted_apr_toggle],
1611
+ outputs=[combined_graph]
1612
+ )
1613
 
1614
  return demo
1615