Spaces:
Sleeping
Sleeping
| """ | |
| 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(""" | |
| <style> | |
| /* Make plots responsive */ | |
| #responsive_apr_plot, #responsive_roi_plot, #responsive_volume_plot, #responsive_apr_hash_plot { | |
| width: 100% !important; | |
| max-width: 100% !important; | |
| } | |
| #responsive_apr_plot > div, #responsive_roi_plot > div, #responsive_volume_plot > div, #responsive_apr_hash_plot > div { | |
| width: 100% !important; | |
| height: auto !important; | |
| min-height: 500px !important; | |
| } | |
| /* Toggle checkbox styling */ | |
| #apr_toggle .gr-checkbox { | |
| accent-color: #e74c3c !important; | |
| } | |
| #adjusted_apr_toggle .gr-checkbox { | |
| accent-color: #2ecc71 !important; | |
| } | |
| #roi_toggle .gr-checkbox { | |
| accent-color: #3498db !important; | |
| } | |
| #volume_toggle .gr-checkbox { | |
| accent-color: #9b59b6 !important; | |
| } | |
| /* Make the toggle section more compact */ | |
| #apr_toggle_title, #roi_toggle_title, #volume_toggle_title { | |
| margin-bottom: 0; | |
| margin-top: 10px; | |
| } | |
| #apr_toggle_container, #roi_toggle_container, #volume_toggle_container { | |
| margin-top: 5px; | |
| } | |
| /* Style the checkbox labels */ | |
| .gr-form.gr-box { | |
| border: none !important; | |
| background: transparent !important; | |
| } | |
| /* Make checkboxes and labels appear on the same line */ | |
| .gr-checkbox-container { | |
| display: flex !important; | |
| align-items: center !important; | |
| } | |
| /* Add colored indicators */ | |
| #apr_toggle .gr-checkbox-label::before { | |
| content: "β"; | |
| color: #e74c3c; | |
| margin-right: 5px; | |
| } | |
| #adjusted_apr_toggle .gr-checkbox-label::before { | |
| content: "β"; | |
| color: #2ecc71; | |
| margin-right: 5px; | |
| } | |
| #roi_toggle .gr-checkbox-label::before { | |
| content: "β"; | |
| color: #3498db; | |
| margin-right: 5px; | |
| } | |
| #volume_toggle .gr-checkbox-label::before { | |
| content: "β"; | |
| color: #9b59b6; | |
| margin-right: 5px; | |
| } | |
| </style> | |
| """) | |
| def create_dashboard() -> gr.Blocks: | |
| """Create and return the dashboard.""" | |
| dashboard = ModiusDashboard() | |
| return dashboard.create_dashboard() | |