""" Main dashboard UI for the Modius Agent Performance application. """ import gradio as gr import plotly.graph_objects as go import logging from typing import Tuple, Optional from ..data.data_processor import DataProcessor from ..visualization.apr_charts import generate_apr_visualizations, generate_apr_vs_agent_hash_visualizations from ..visualization.roi_charts import generate_roi_visualizations from ..visualization.volume_charts import generate_volume_visualizations from ..utils.logging_config import get_logger logger = get_logger(__name__) class ModiusDashboard: """Main dashboard class for the Modius Agent Performance application.""" def __init__(self): self.data_processor = DataProcessor() self.global_df = None self.global_roi_df = None self.global_volume_df = None def create_dashboard(self) -> gr.Blocks: """Create the main dashboard interface.""" with gr.Blocks() as demo: gr.Markdown("# Average Modius Agent Performance") # Create tabs for different metrics with gr.Tabs(): # APR Metrics tab with gr.Tab("APR Metrics"): apr_tab = self._create_apr_tab() # ROI Metrics tab with gr.Tab("ROI Metrics"): roi_tab = self._create_roi_tab() # Volume Metrics tab - COMMENTED OUT # with gr.Tab("Volume Metrics"): # volume_tab = self._create_volume_tab() # Performance Graph tab - COMMENTED OUT # with gr.Tab("Performance Graph"): # performance_tab = self._create_performance_tab() # Add custom CSS for responsive design self._add_custom_css() return demo def _create_apr_tab(self) -> gr.Column: """Create the APR metrics tab.""" with gr.Column() as apr_tab: refresh_apr_btn = gr.Button("Refresh APR Data") with gr.Column(): combined_apr_graph = gr.Plot(label="APR for All Agents", elem_id="responsive_apr_plot") # Toggle controls with gr.Row(visible=True): gr.Markdown("##### Toggle Graph Lines", elem_id="apr_toggle_title") with gr.Row(): with gr.Column(): with gr.Row(elem_id="apr_toggle_container"): with gr.Column(scale=1, min_width=150): apr_toggle = gr.Checkbox(label="APR Average (7-Day MA)", value=True, elem_id="apr_toggle") with gr.Column(scale=1, min_width=150): adjusted_apr_toggle = gr.Checkbox(label="ETH Adjusted APR Average (7-Day MA)", value=True, elem_id="adjusted_apr_toggle") apr_status_text = gr.Textbox(label="Status", value="Ready", interactive=False) # Set up event handlers self._setup_apr_events(refresh_apr_btn, combined_apr_graph, apr_toggle, adjusted_apr_toggle, apr_status_text) # Initialize with placeholder apr_placeholder_fig = self._create_placeholder_chart("Click 'Refresh APR Data' to load APR graph") combined_apr_graph.value = apr_placeholder_fig return apr_tab def _create_roi_tab(self) -> gr.Column: """Create the ROI metrics tab.""" with gr.Column() as roi_tab: refresh_roi_btn = gr.Button("Refresh ROI Data") with gr.Column(): combined_roi_graph = gr.Plot(label="ROI for All Agents", elem_id="responsive_roi_plot") # Toggle controls with gr.Row(visible=True): gr.Markdown("##### Toggle Graph Lines", elem_id="roi_toggle_title") with gr.Row(): with gr.Column(): with gr.Row(elem_id="roi_toggle_container"): with gr.Column(scale=1, min_width=150): roi_toggle = gr.Checkbox(label="ROI Average (7-Day MA)", value=True, elem_id="roi_toggle") roi_status_text = gr.Textbox(label="Status", value="Ready", interactive=False) # Set up event handlers self._setup_roi_events(refresh_roi_btn, combined_roi_graph, roi_toggle, roi_status_text) # Initialize with placeholder roi_placeholder_fig = self._create_placeholder_chart("Click 'Refresh ROI Data' to load ROI graph") combined_roi_graph.value = roi_placeholder_fig return roi_tab def _create_volume_tab(self) -> gr.Column: """Create the Volume metrics tab.""" with gr.Column() as volume_tab: refresh_volume_btn = gr.Button("Refresh Volume Data") with gr.Column(): combined_volume_graph = gr.Plot(label="Daily Volume Change (%) with 7-Day SMA", elem_id="responsive_volume_plot") # Toggle controls with gr.Row(visible=True): gr.Markdown("##### Toggle Chart Display", elem_id="volume_toggle_title") with gr.Row(): with gr.Column(): with gr.Row(elem_id="volume_toggle_container"): with gr.Column(scale=1, min_width=150): volume_bars_toggle = gr.Checkbox(label="Daily Volume Bars", value=True, elem_id="volume_bars_toggle") with gr.Column(scale=1, min_width=150): volume_sma_toggle = gr.Checkbox(label="7-Day SMA Line", value=True, elem_id="volume_sma_toggle") volume_status_text = gr.Textbox(label="Status", value="Ready", interactive=False) # Set up event handlers self._setup_volume_events(refresh_volume_btn, combined_volume_graph, volume_bars_toggle, volume_sma_toggle, volume_status_text) # Initialize with placeholder volume_placeholder_fig = self._create_placeholder_chart("Click 'Refresh Volume Data' to load Volume graph") combined_volume_graph.value = volume_placeholder_fig return volume_tab def _create_performance_tab(self) -> gr.Column: """Create the Performance Graph tab.""" with gr.Column() as performance_tab: refresh_apr_hash_btn = gr.Button("Refresh APR vs Agent Hash Data") with gr.Column(): apr_vs_agent_hash_graph = gr.Plot(label="APR vs Agent Hash", elem_id="responsive_apr_hash_plot") apr_hash_status_text = gr.Textbox(label="Status", value="Ready", interactive=False) # Set up event handlers self._setup_performance_events(refresh_apr_hash_btn, apr_vs_agent_hash_graph, apr_hash_status_text) # Initialize with placeholder apr_hash_placeholder_fig = self._create_placeholder_chart("Click 'Refresh APR vs Agent Hash Data' to load APR vs Agent Hash graph") apr_vs_agent_hash_graph.value = apr_hash_placeholder_fig return performance_tab def _setup_apr_events(self, refresh_btn, graph, apr_toggle, adjusted_apr_toggle, status_text): """Set up event handlers for APR tab.""" def update_apr_graph(show_apr_ma=True, show_adjusted_apr_ma=True): try: combined_fig, _ = generate_apr_visualizations(self.data_processor) # Update visibility of traces based on toggle values for trace in combined_fig.data: if 'Average APR (7d window)' in trace.name and 'Adjusted' not in trace.name: trace.visible = show_apr_ma elif 'Average ETH Adjusted APR (7d window)' in trace.name: trace.visible = show_adjusted_apr_ma return combined_fig except Exception as e: logger.exception("Error generating APR visualization") return self._create_error_chart(f"Error: {str(e)}") def refresh_apr_data(): try: logger.info("Manually refreshing APR data...") self.global_df, self.global_roi_df = self.data_processor.fetch_apr_data_from_db() if self.global_df is None or len(self.global_df) == 0: logger.error("Failed to fetch APR data") return graph.value, "Error: Failed to fetch APR data. Check the logs for details." logger.info("Generating new APR visualization...") new_graph = update_apr_graph(apr_toggle.value, adjusted_apr_toggle.value) return new_graph, "APR data refreshed successfully" except Exception as e: logger.error(f"Error refreshing APR data: {e}") return graph.value, f"Error: {str(e)}" # Set up event handlers refresh_btn.click( fn=refresh_apr_data, inputs=[], outputs=[graph, status_text] ) apr_toggle.change( fn=update_apr_graph, inputs=[apr_toggle, adjusted_apr_toggle], outputs=[graph] ) adjusted_apr_toggle.change( fn=update_apr_graph, inputs=[apr_toggle, adjusted_apr_toggle], outputs=[graph] ) def _setup_roi_events(self, refresh_btn, graph, roi_toggle, status_text): """Set up event handlers for ROI tab.""" def update_roi_graph(show_roi_ma=True): try: combined_fig, _ = generate_roi_visualizations(self.data_processor) # Update visibility of traces based on toggle values for trace in combined_fig.data: if trace.name == 'Average ROI (3d window)': trace.visible = show_roi_ma return combined_fig except Exception as e: logger.exception("Error generating ROI visualization") return self._create_error_chart(f"Error: {str(e)}") def refresh_roi_data(): try: logger.info("Manually refreshing ROI data...") self.global_df, self.global_roi_df = self.data_processor.fetch_apr_data_from_db() if self.global_roi_df is None or len(self.global_roi_df) == 0: logger.error("Failed to fetch ROI data") return graph.value, "Error: Failed to fetch ROI data. Check the logs for details." logger.info("Generating new ROI visualization...") new_graph = update_roi_graph(roi_toggle.value) return new_graph, "ROI data refreshed successfully" except Exception as e: logger.error(f"Error refreshing ROI data: {e}") return graph.value, f"Error: {str(e)}" # Set up event handlers refresh_btn.click( fn=refresh_roi_data, inputs=[], outputs=[graph, status_text] ) roi_toggle.change( fn=update_roi_graph, inputs=[roi_toggle], outputs=[graph] ) def _setup_volume_events(self, refresh_btn, graph, volume_bars_toggle, volume_sma_toggle, status_text): """Set up event handlers for Volume tab.""" def update_volume_graph(show_bars=True, show_sma=True): try: combined_fig, _ = generate_volume_visualizations(self.data_processor) # Update visibility of traces based on toggle values for trace in combined_fig.data: if trace.name == 'Daily Volume Change': trace.visible = show_bars elif trace.name == '7-Day SMA': trace.visible = show_sma return combined_fig except Exception as e: logger.exception("Error generating Volume visualization") return self._create_error_chart(f"Error: {str(e)}") def refresh_volume_data(): try: logger.info("Manually refreshing volume data...") self.global_df, _ = self.data_processor.fetch_apr_data_from_db() if self.global_df is None or len(self.global_df) == 0: logger.error("Failed to fetch volume data") return graph.value, "Error: Failed to fetch volume data. Check the logs for details." logger.info("Generating new volume visualization...") new_graph = update_volume_graph(volume_bars_toggle.value, volume_sma_toggle.value) return new_graph, "Volume data refreshed successfully" except Exception as e: logger.error(f"Error refreshing volume data: {e}") return graph.value, f"Error: {str(e)}" # Set up event handlers refresh_btn.click( fn=refresh_volume_data, inputs=[], outputs=[graph, status_text] ) volume_bars_toggle.change( fn=update_volume_graph, inputs=[volume_bars_toggle, volume_sma_toggle], outputs=[graph] ) volume_sma_toggle.change( fn=update_volume_graph, inputs=[volume_bars_toggle, volume_sma_toggle], outputs=[graph] ) def _setup_performance_events(self, refresh_btn, graph, status_text): """Set up event handlers for Performance tab.""" def update_apr_vs_agent_hash_graph(): try: if self.global_df is None or self.global_df.empty: return self._create_error_chart("No data available. Please refresh APR data first.") fig, _ = generate_apr_vs_agent_hash_visualizations(self.global_df) return fig except Exception as e: logger.exception("Error generating APR vs Agent Hash visualization") return self._create_error_chart(f"Error: {str(e)}") def refresh_apr_vs_agent_hash_data(): try: logger.info("Manually refreshing APR vs Agent Hash data...") if self.global_df is None or self.global_df.empty: self.global_df, _ = self.data_processor.fetch_apr_data_from_db() if self.global_df is None or len(self.global_df) == 0: logger.error("Failed to fetch APR data for APR vs Agent Hash visualization") return graph.value, "Error: Failed to fetch APR data. Check the logs for details." if 'agent_hash' not in self.global_df.columns: logger.error("agent_hash column not found in DataFrame") return graph.value, "Error: agent_hash column not found in data. Check the logs for details." logger.info("Generating new APR vs Agent Hash visualization...") new_graph = update_apr_vs_agent_hash_graph() return new_graph, "APR vs Agent Hash data refreshed successfully" except Exception as e: logger.error(f"Error refreshing APR vs Agent Hash data: {e}") return graph.value, f"Error: {str(e)}" # Set up event handlers refresh_btn.click( fn=refresh_apr_vs_agent_hash_data, inputs=[], outputs=[graph, status_text] ) def _create_placeholder_chart(self, message: str) -> go.Figure: """Create a placeholder chart with a message.""" fig = go.Figure() fig.add_annotation( text=message, x=0.5, y=0.5, showarrow=False, font=dict(size=15) ) return fig def _create_error_chart(self, message: str) -> go.Figure: """Create an error chart with a message.""" fig = go.Figure() fig.add_annotation( text=message, x=0.5, y=0.5, showarrow=False, font=dict(size=15, color="red") ) return fig def _add_custom_css(self): """Add custom CSS for responsive design.""" gr.HTML(""" """) def create_dashboard() -> gr.Blocks: """Create and return the dashboard.""" dashboard = ModiusDashboard() return dashboard.create_dashboard()