Spaces:
Running
Running
""" | |
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() | |