"""
Enhanced investor-grade visualizations - FIXED VERSION
"""
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
import numpy as np
from plotly.subplots import make_subplots
from datetime import datetime
from typing import Dict, Optional, List
from core.data_models import IncidentScenario
class EnhancedVisualizationEngine:
"""Interactive, investor-grade visualizations"""
def __init__(self):
self.color_palette = px.colors.qualitative.Set3
self.theme = {
"background": "rgba(0,0,0,0)",
"grid": "rgba(200,200,200,0.1)",
"critical": "#FF6B6B",
"warning": "#FFE66D",
"success": "#4ECDC4",
"info": "#45B7D1"
}
def create_interactive_timeline(self, scenario: IncidentScenario) -> go.Figure:
"""Create truly interactive timeline with annotations"""
fig = go.Figure()
# Create timeline events
events = [
{"time": "T-5m", "event": "📉 Cache hit rate drops below 20%", "type": "problem"},
{"time": "T-4m", "event": "⚠️ Database load exceeds 90%", "type": "alert"},
{"time": "T-3m", "event": "🤖 ARF detects pattern", "type": "detection"},
{"time": "T-2m", "event": "🧠 Analysis complete", "type": "analysis"},
{"time": "T-1m", "event": "⚡ Healing executed", "type": "action"},
{"time": "T-0m", "event": "✅ System recovered", "type": "recovery"}
]
# Color mapping
color_map = {
"problem": self.theme["critical"],
"alert": self.theme["warning"],
"detection": self.theme["info"],
"analysis": "#9B59B6",
"action": self.theme["success"],
"recovery": "#2ECC71"
}
# Add events
for event in events:
fig.add_trace(go.Scatter(
x=[event["time"]],
y=[1],
mode='markers+text',
marker=dict(
size=20,
color=color_map[event["type"]],
symbol='circle',
line=dict(width=2, color='white')
),
text=[event["event"]],
textposition="top center",
hoverinfo='text',
name=event["type"].capitalize(),
hovertemplate="%{text}
Click for details"
))
# Add connecting line
fig.add_trace(go.Scatter(
x=[e["time"] for e in events],
y=[1] * len(events),
mode='lines',
line=dict(color='gray', width=2, dash='dash'),
hoverinfo='none',
showlegend=False
))
fig.update_layout(
title="Interactive Incident Timeline
Click events for details",
height=450,
paper_bgcolor=self.theme["background"],
plot_bgcolor=self.theme["background"],
hovermode='closest',
clickmode='event+select',
yaxis=dict(
showticklabels=False,
range=[0.5, 1.5],
gridcolor=self.theme["grid"]
),
xaxis=dict(
gridcolor=self.theme["grid"]
),
showlegend=True,
legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.01
)
)
return fig
def create_executive_dashboard(self, user_roi: Optional[Dict] = None) -> go.Figure:
"""Create comprehensive executive dashboard - FIXED VERSION"""
# FIX: Handle None or empty user_roi
if user_roi is None:
user_roi = {}
# Get ROI value safely
roi_value = user_roi.get('roi_multiplier', 5.2)
# If roi_value is a string with "×", convert it
if isinstance(roi_value, str):
if "×" in roi_value:
try:
roi_value = float(roi_value.replace("×", ""))
except:
roi_value = 5.2
else:
try:
roi_value = float(roi_value)
except:
roi_value = 5.2
fig = make_subplots(
rows=2, cols=2,
subplot_titles=(
'Cost Transformation',
'Team Capacity Shift',
'MTTR Comparison',
'ROI Analysis'
),
vertical_spacing=0.2,
horizontal_spacing=0.15
)
# 1. Cost Transformation
cost_labels = ['Without ARF', 'With ARF Enterprise', 'Net Savings']
cost_values = [2.96, 1.0, 1.96] # In millions
fig.add_trace(
go.Bar(
x=cost_labels,
y=cost_values,
marker_color=[self.theme["critical"], self.theme["success"], '#45B7D1'],
text=[f'${v}M' for v in cost_values],
textposition='auto',
name='Annual Cost',
hovertemplate="%{x}
Cost: $%{y}M"
),
row=1, col=1
)
# 2. Team Capacity Shift
activities = ['Firefighting', 'Innovation', 'Strategic Work']
before = [60, 20, 20]
after = [10, 60, 30]
fig.add_trace(
go.Bar(
x=activities,
y=before,
name='Before ARF',
marker_color=self.theme["critical"],
opacity=0.7,
hovertemplate="Before: %{y}%%"
),
row=1, col=2
)
fig.add_trace(
go.Bar(
x=activities,
y=after,
name='After ARF Enterprise',
marker_color=self.theme["success"],
opacity=0.7,
hovertemplate="After: %{y}%%"
),
row=1, col=2
)
# 3. MTTR Comparison
mttr_methods = ['Manual', 'Traditional', 'ARF OSS', 'ARF Enterprise']
mttr_times = [120, 45, 25, 8]
fig.add_trace(
go.Bar(
x=mttr_methods,
y=mttr_times,
marker_color=[self.theme["critical"], '#FFE66D', '#45B7D1', self.theme["success"]],
text=[f'{t} min' for t in mttr_times],
textposition='auto',
name='MTTR',
hovertemplate="%{x}
Resolution: %{y} min"
),
row=2, col=1
)
# 4. ROI Gauge
fig.add_trace(
go.Indicator(
mode="gauge+number+delta",
value=roi_value,
title={'text': "ROI Multiplier", 'font': {'size': 16}},
delta={'reference': 1.0, 'increasing': {'color': self.theme["success"]}},
gauge={
'axis': {'range': [0, 10], 'tickwidth': 1},
'bar': {'color': self.theme["success"]},
'steps': [
{'range': [0, 2], 'color': 'lightgray'},
{'range': [2, 4], 'color': 'gray'},
{'range': [4, 6], 'color': 'lightgreen'},
{'range': [6, 10], 'color': self.theme["success"]}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': roi_value
}
}
),
row=2, col=2
)
# Update layout
fig.update_layout(
height=700,
showlegend=True,
paper_bgcolor=self.theme["background"],
plot_bgcolor=self.theme["background"],
title_text="Executive Business Dashboard",
barmode='group',
legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.01,
bgcolor='rgba(255,255,255,0.9)'
)
)
# Update axes
fig.update_xaxes(title_text="Cost Model", row=1, col=1, gridcolor=self.theme["grid"])
fig.update_yaxes(title_text="Annual Cost ($M)", row=1, col=1, gridcolor=self.theme["grid"])
fig.update_xaxes(title_text="Activity", row=1, col=2, gridcolor=self.theme["grid"])
fig.update_yaxes(title_text="Time Allocation (%)", row=1, col=2, gridcolor=self.theme["grid"])
fig.update_xaxes(title_text="Resolution Method", row=2, col=1, gridcolor=self.theme["grid"])
fig.update_yaxes(title_text="Minutes to Resolve", row=2, col=1, gridcolor=self.theme["grid"])
return fig