gauravlochab
feat: enable auto-ranging for x-axis and y-axis in APR and ROI charts
9bcd921
"""
ROI chart implementations.
"""
import plotly.graph_objects as go
import pandas as pd
import logging
from datetime import datetime
from typing import Dict, Any, Optional, Tuple
from ..config.constants import DATE_RANGES, Y_AXIS_RANGES, FILE_PATHS
from .base_chart import BaseChart
logger = logging.getLogger(__name__)
class ROIChart(BaseChart):
"""Chart for ROI visualizations."""
def create_chart(self, df: pd.DataFrame, **kwargs) -> go.Figure:
"""Create ROI time series chart."""
if df.empty:
return self._create_empty_chart("No ROI data available")
# Filter for ROI data only
roi_data = df[df['metric_type'] == 'ROI'].copy()
if roi_data.empty:
return self._create_empty_chart("No ROI data available")
# ROI data is already in percentage format from corrected CSV
# No conversion needed
# Apply daily median aggregation to reduce outliers
roi_data = self.data_processor.aggregate_daily_medians(roi_data, ['roi'])
if roi_data.empty:
return self._create_empty_chart("No ROI data available after aggregation")
# Apply high APR filtering to ROI data (need to get APR data first to determine what to filter)
# Fetch APR data to determine which agent-timestamp combinations to filter
apr_df, _ = self.data_processor.fetch_apr_data_from_db()
if not apr_df.empty:
# Apply same processing to APR data to get the filtering reference
apr_processed = apr_df[apr_df['metric_type'] == 'APR'].copy()
apr_processed = self.data_processor.aggregate_daily_medians(apr_processed, ['apr', 'adjusted_apr'])
# Apply high APR filtering to both datasets
_, roi_data = self.data_processor.filter_high_apr_values(apr_processed, roi_data)
if roi_data.empty:
return self._create_empty_chart("No ROI data available after high APR filtering")
# Save processed ROI data for verification (after all processing steps)
processed_csv_path = self.data_processor.save_to_csv(roi_data, FILE_PATHS['roi_processed_csv'])
if processed_csv_path:
logger.info(f"Saved processed ROI data to {processed_csv_path} for verification")
logger.info(f"Processed ROI data contains {len(roi_data)} rows after agent exclusion, daily median aggregation, and high APR filtering")
# Filter outliers (disabled but keeping for compatibility)
roi_data = self._filter_outliers(roi_data, 'roi')
# Get time range
min_time = roi_data['timestamp'].min()
max_time = roi_data['timestamp'].max()
x_start_date = min_time # Use actual start date for ROI
# Calculate average runtime for title
fixed_start_date = DATE_RANGES['feb_start']
agent_runtimes = {}
for agent_id in roi_data['agent_id'].unique():
agent_data = roi_data[roi_data['agent_id'] == agent_id]
agent_name = agent_data['agent_name'].iloc[0]
last_report = agent_data['timestamp'].max()
runtime_days = (last_report - fixed_start_date).total_seconds() / (24 * 3600)
agent_runtimes[agent_id] = {
'agent_name': agent_name,
'last_report': last_report,
'runtime_days': runtime_days
}
avg_runtime = sum(data['runtime_days'] for data in agent_runtimes.values()) / len(agent_runtimes) if agent_runtimes else 0
# Create figure
fig = self._create_base_figure()
# Add background shapes
y_range = Y_AXIS_RANGES['roi']
self._add_background_shapes(fig, min_time, max_time, y_range['min'], y_range['max'])
self._add_zero_line(fig, min_time, max_time)
# Calculate moving averages
avg_roi_data = self._calculate_moving_average(roi_data, 'roi')
# Add individual agent data points
unique_agents = roi_data['agent_name'].unique()
color_map = self._get_color_map(unique_agents)
self._add_agent_data_points(fig, roi_data, 'roi', color_map)
# Add ROI moving average line
self._add_moving_average_line(
fig, avg_roi_data, 'roi',
'Average ROI (3d window)',
self.colors['roi'],
width=2
)
# Update layout and axes
title = f"Modius Agents ROI (over avg. {avg_runtime:.1f} days runtime)"
self._update_layout(
fig,
title=title,
y_axis_title="ROI [%]",
y_range=[y_range['min'], y_range['max']]
)
# Use auto-range for both x-axis and y-axis to show all available data
self._update_axes(
fig,
x_range=None, # Let plotly auto-determine the best x-axis range
y_auto=True # Let plotly auto-determine the best y-axis range
)
# Save chart
self._save_chart(
fig,
FILE_PATHS['roi_graph_html'],
FILE_PATHS['roi_graph_png']
)
return fig
def generate_roi_visualizations(data_processor=None) -> Tuple[go.Figure, Optional[str]]:
"""Generate ROI visualizations."""
from ..data.data_processor import DataProcessor
if data_processor is None:
data_processor = DataProcessor()
# Fetch data
_, roi_df = data_processor.fetch_apr_data_from_db()
# Create chart
roi_chart = ROIChart(data_processor)
fig, csv_path = roi_chart.generate_visualization(
roi_df,
csv_filename=FILE_PATHS['roi_csv']
)
return fig, csv_path