Spaces:
Running
Running
File size: 5,814 Bytes
174e0f0 9bcd921 174e0f0 9bcd921 174e0f0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
"""
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
|