use crate::calibration::{
slo_system::{SloSystem, SloReport, SloState, SloType, SloStatus, SystemHealth, ActiveAlert},
CalibrationResult, CalibrationSample,
monitoring::ECEMeasurement,
};
use anyhow::bail;
use anyhow::{Context, Result};
use chrono::{DateTime, Utc, Duration};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque, BTreeMap};
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{info, warn, error, debug};
#[derive(Debug, Clone)]
pub struct SloDashboard {
slo_system: Arc<SloSystem>,
config: DashboardConfig,
dashboard_state: Arc<RwLock<DashboardState>>,
visualizations: Arc<RwLock<VisualizationComponents>>,
export_manager: Arc<ExportManager>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardConfig {
pub refresh_interval: Duration,
pub data_retention: Duration,
pub max_data_points: usize,
pub default_time_range: TimeRange,
pub theme: DashboardTheme,
pub export_config: ExportConfig,
pub alert_integration: AlertIntegrationConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TimeRange {
LastHour,
Last6Hours,
Last24Hours,
LastWeek,
LastMonth,
Custom { start: DateTime<Utc>, end: DateTime<Utc> },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardTheme {
pub color_scheme: ColorScheme,
pub chart_style: ChartStyle,
pub typography: Typography,
pub layout: LayoutConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColorScheme {
pub primary: String,
pub secondary: String,
pub success: String,
pub warning: String,
pub error: String,
pub info: String,
pub background: String,
pub surface: String,
pub text_primary: String,
pub text_secondary: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChartStyle {
pub line_width: f32,
pub point_radius: f32,
pub bar_spacing: f32,
pub bar_corner_radius: f32,
pub grid_opacity: f32,
pub axis_color: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Typography {
pub font_family: String,
pub heading_size: f32,
pub body_size: f32,
pub caption_size: f32,
pub heading_weight: String,
pub body_weight: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayoutConfig {
pub grid_columns: usize,
pub grid_gap: f32,
pub padding: f32,
pub margin: f32,
pub component_min_height: f32,
pub sidebar_width: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportConfig {
pub supported_formats: Vec<ExportFormat>,
pub image_quality: ImageQuality,
pub data_export: DataExportConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExportFormat {
Json,
Csv,
Png,
Svg,
Pdf,
Html,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageQuality {
pub dpi: u32,
pub compression: f32,
pub width: Option<u32>,
pub height: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataExportConfig {
pub include_raw_data: bool,
pub include_metadata: bool,
pub max_rows: usize,
pub date_format: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertIntegrationConfig {
pub show_alerts: bool,
pub alert_display_duration: Duration,
pub sound_notifications: bool,
pub alert_styling: AlertStyling,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertStyling {
pub animation: AlertAnimation,
pub position: AlertPosition,
pub opacity: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AlertAnimation {
None,
Fade,
Slide,
Bounce,
Pulse,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AlertPosition {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
Center,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardState {
pub last_updated: DateTime<Utc>,
pub current_slo_state: Option<SloState>,
pub historical_data: HistoricalData,
pub dashboard_metrics: DashboardMetrics,
pub view_state: ViewState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoricalData {
pub slo_history: HashMap<SloType, VecDeque<TimestampedValue>>,
pub breach_events: VecDeque<BreachEvent>,
pub drift_history: VecDeque<DriftMeasurement>,
pub calibration_history: VecDeque<CalibrationBinData>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimestampedValue {
pub timestamp: DateTime<Utc>,
pub value: f64,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BreachEvent {
pub timestamp: DateTime<Utc>,
pub slo_type: SloType,
pub severity: String,
pub duration: Option<Duration>,
pub resolved: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriftMeasurement {
pub timestamp: DateTime<Utc>,
pub metric_name: String,
pub current_value: f64,
pub previous_value: f64,
pub delta: f64,
pub threshold_breached: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalibrationBinData {
pub timestamp: DateTime<Utc>,
pub language: String,
pub intent: String,
pub bins: Vec<CalibrationBin>,
pub overall_ece: f64,
pub sample_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalibrationBin {
pub bin_id: usize,
pub confidence_range: (f64, f64),
pub predicted_probability: f64,
pub actual_accuracy: f64,
pub sample_count: usize,
pub bin_ece: f64,
pub merged: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardMetrics {
pub average_render_time_ms: f64,
pub p99_render_time_ms: f64,
pub data_load_time_ms: f64,
pub updates_per_minute: f64,
pub memory_usage_mb: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewState {
pub time_range: TimeRange,
pub filters: ViewFilters,
pub selected_slos: Vec<SloType>,
pub drill_down: Option<DrillDownState>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewFilters {
pub languages: Vec<String>,
pub intents: Vec<String>,
pub severities: Vec<String>,
pub statuses: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DrillDownState {
pub slo_type: SloType,
pub time_range: TimeRange,
pub selected_slice: Option<String>,
}
#[derive(Debug, Clone)]
pub struct VisualizationComponents {
pub slo_overview: SloOverviewComponent,
pub reliability_chart: ReliabilityChartComponent,
pub calibration_table: CalibrationTableComponent,
pub drift_analysis: DriftAnalysisComponent,
pub alert_panel: AlertPanelComponent,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloOverviewComponent {
pub system_health: SystemHealthIndicator,
pub slo_cards: Vec<SloStatusCard>,
pub metrics_summary: MetricsSummary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemHealthIndicator {
pub health: SystemHealth,
pub health_score: f64, pub status_text: String,
pub last_updated: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloStatusCard {
pub slo_type: SloType,
pub current_value: f64,
pub target_value: f64,
pub status: String,
pub trend: TrendIndicator,
pub last_breach: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendIndicator {
pub direction: String, pub change_rate: f64,
pub confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsSummary {
pub total_slos: usize,
pub slos_meeting_target: usize,
pub slos_at_warning: usize,
pub slos_breached: usize,
pub average_availability: f64,
pub mttr_minutes: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityChartComponent {
pub compliance_data: HashMap<SloType, Vec<TimestampedValue>>,
pub breach_markers: Vec<BreachMarker>,
pub threshold_lines: ThresholdLines,
pub chart_config: ChartConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BreachMarker {
pub timestamp: DateTime<Utc>,
pub slo_type: SloType,
pub severity: String,
pub duration: Option<Duration>,
pub tooltip: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThresholdLines {
pub target_line: ThresholdLine,
pub warning_line: ThresholdLine,
pub critical_line: Option<ThresholdLine>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThresholdLine {
pub value: f64,
pub color: String,
pub style: String, pub label: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChartConfig {
pub chart_type: ChartType,
pub x_axis: AxisConfig,
pub y_axis: AxisConfig,
pub legend: LegendConfig,
pub tooltips: TooltipConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ChartType {
Line,
Area,
Bar,
Scatter,
Heatmap,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AxisConfig {
pub label: String,
pub min: Option<f64>,
pub max: Option<f64>,
pub format: String,
pub grid_lines: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LegendConfig {
pub show: bool,
pub position: String, pub orientation: String, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TooltipConfig {
pub show: bool,
pub format: String,
pub include_metadata: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalibrationTableComponent {
pub calibration_slices: Vec<CalibrationSliceTable>,
pub bin_statistics: BinStatistics,
pub mask_mismatch: MaskMismatchReport,
pub table_config: TableConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalibrationSliceTable {
pub slice_id: String,
pub language: String,
pub intent: String,
pub bins: Vec<CalibrationBin>,
pub overall_metrics: SliceMetrics,
pub last_updated: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SliceMetrics {
pub ece: f64,
pub ace: f64, pub brier_score: f64,
pub sample_count: usize,
pub merged_bins: usize,
pub clamp_activation_rate: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BinStatistics {
pub avg_samples_per_bin: f64,
pub bin_coverage_distribution: Vec<f64>,
pub merged_bin_rate: f64,
pub empty_bin_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaskMismatchReport {
pub fit_vs_eval_mismatches: Vec<MaskMismatch>,
pub mismatch_rate: f64,
pub affected_slices: Vec<String>,
pub severity: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaskMismatch {
pub slice_id: String,
pub fit_mask: String,
pub eval_mask: String,
pub mismatch_type: String,
pub impact_score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableConfig {
pub sortable_columns: Vec<String>,
pub filterable_columns: Vec<String>,
pub default_sort: String,
pub page_size: usize,
pub show_pagination: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriftAnalysisComponent {
pub weekly_deltas: WeeklyDriftReport,
pub trend_analysis: DriftTrendAnalysis,
pub threshold_visualization: ThresholdVisualization,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WeeklyDriftReport {
pub report_week: DateTime<Utc>,
pub metric_deltas: HashMap<String, DriftMetric>,
pub overall_status: String,
pub breached_thresholds: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriftMetric {
pub metric_name: String,
pub current_value: f64,
pub previous_value: f64,
pub delta: f64,
pub threshold: f64,
pub status: String, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriftTrendAnalysis {
pub trending_metrics: Vec<TrendingMetric>,
pub stability_score: f64,
pub prediction: DriftPrediction,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendingMetric {
pub metric_name: String,
pub trend_direction: String,
pub trend_strength: f64,
pub time_to_breach: Option<Duration>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriftPrediction {
pub predicted_breach_date: Option<DateTime<Utc>>,
pub confidence: f64,
pub recommended_actions: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThresholdVisualization {
pub threshold_lines: Vec<ThresholdLine>,
pub current_values: HashMap<String, f64>,
pub buffer_zones: Vec<BufferZone>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BufferZone {
pub name: String,
pub lower_bound: f64,
pub upper_bound: f64,
pub color: String,
pub opacity: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertPanelComponent {
pub active_alerts: Vec<AlertDisplayItem>,
pub recent_alerts: Vec<AlertDisplayItem>,
pub alert_stats: AlertStatistics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertDisplayItem {
pub id: String,
pub slo_type: SloType,
pub severity: String,
pub title: String,
pub description: String,
pub timestamp: DateTime<Utc>,
pub status: String, pub assignee: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertStatistics {
pub total_alerts_today: usize,
pub alerts_by_severity: HashMap<String, usize>,
pub average_resolution_time: Duration,
pub escalation_rate: f64,
}
#[derive(Debug, Clone)]
pub struct ExportManager {
config: ExportConfig,
}
impl SloDashboard {
pub fn new(slo_system: Arc<SloSystem>) -> Self {
let config = Self::default_config();
Self {
slo_system,
config,
dashboard_state: Arc::new(RwLock::new(DashboardState::default())),
visualizations: Arc::new(RwLock::new(VisualizationComponents::default())),
export_manager: Arc::new(ExportManager::new(ExportConfig::default())),
}
}
pub fn with_config(slo_system: Arc<SloSystem>, config: DashboardConfig) -> Self {
Self {
slo_system,
config: config.clone(),
dashboard_state: Arc::new(RwLock::new(DashboardState::default())),
visualizations: Arc::new(RwLock::new(VisualizationComponents::default())),
export_manager: Arc::new(ExportManager::new(config.export_config)),
}
}
pub async fn start(&self) -> Result<()> {
info!("Starting SLO dashboard");
let dashboard = self.clone();
tokio::spawn(async move {
dashboard.refresh_loop().await;
});
info!("SLO dashboard started successfully");
Ok(())
}
async fn refresh_loop(&self) {
let mut interval = tokio::time::interval(self.config.refresh_interval);
loop {
interval.tick().await;
if let Err(e) = self.refresh_dashboard_data().await {
error!("Error refreshing dashboard data: {}", e);
}
}
}
async fn refresh_dashboard_data(&self) -> Result<()> {
debug!("Refreshing dashboard data");
let slo_state = self.slo_system.get_slo_state().await;
let mut state = self.dashboard_state.write().await;
state.current_slo_state = Some(slo_state.clone());
state.last_updated = Utc::now();
self.update_historical_data(&slo_state).await?;
self.update_visualizations().await?;
debug!("Dashboard data refreshed successfully");
Ok(())
}
async fn update_historical_data(&self, slo_state: &SloState) -> Result<()> {
let mut state = self.dashboard_state.write().await;
for (slo_type, slo_status) in &slo_state.slo_status {
let history = state.historical_data.slo_history
.entry(slo_type.clone())
.or_insert_with(VecDeque::new);
history.push_back(TimestampedValue {
timestamp: slo_status.last_measured,
value: slo_status.current_value,
metadata: HashMap::new(),
});
while history.len() > self.config.max_data_points {
history.pop_front();
}
}
for (slo_type, slo_status) in &slo_state.slo_status {
if matches!(slo_status.status, crate::calibration::slo_system::SloHealthStatus::Breached) {
state.historical_data.breach_events.push_back(BreachEvent {
timestamp: slo_status.last_measured,
slo_type: slo_type.clone(),
severity: "critical".to_string(),
duration: None,
resolved: false,
});
}
}
Ok(())
}
async fn update_visualizations(&self) -> Result<()> {
let state = self.dashboard_state.read().await;
let mut visualizations = self.visualizations.write().await;
if let Some(slo_state) = &state.current_slo_state {
visualizations.slo_overview = self.create_slo_overview(slo_state).await?;
visualizations.reliability_chart = self.create_reliability_chart(&state.historical_data).await?;
visualizations.calibration_table = self.create_calibration_table().await?;
visualizations.drift_analysis = self.create_drift_analysis().await?;
visualizations.alert_panel = self.create_alert_panel(&slo_state.active_alerts).await?;
}
Ok(())
}
async fn create_slo_overview(&self, slo_state: &SloState) -> Result<SloOverviewComponent> {
let system_health = SystemHealthIndicator {
health: slo_state.overall_health.clone(),
health_score: self.calculate_health_score(slo_state),
status_text: format!("{:?}", slo_state.overall_health),
last_updated: slo_state.last_measurement,
};
let mut slo_cards = Vec::new();
for (slo_type, slo_status) in &slo_state.slo_status {
slo_cards.push(SloStatusCard {
slo_type: slo_type.clone(),
current_value: slo_status.current_value,
target_value: slo_status.target_value,
status: format!("{:?}", slo_status.status),
trend: TrendIndicator {
direction: format!("{:?}", slo_status.trend.direction),
change_rate: slo_status.trend.rate,
confidence: slo_status.trend.confidence,
},
last_breach: None, });
}
let metrics_summary = MetricsSummary {
total_slos: slo_state.slo_status.len(),
slos_meeting_target: slo_state.slo_status.values()
.filter(|s| matches!(s.status, crate::calibration::slo_system::SloHealthStatus::Meeting))
.count(),
slos_at_warning: slo_state.slo_status.values()
.filter(|s| matches!(s.status, crate::calibration::slo_system::SloHealthStatus::Warning))
.count(),
slos_breached: slo_state.slo_status.values()
.filter(|s| matches!(s.status, crate::calibration::slo_system::SloHealthStatus::Breached))
.count(),
average_availability: 99.5, mttr_minutes: 15.0, };
Ok(SloOverviewComponent {
system_health,
slo_cards,
metrics_summary,
})
}
fn calculate_health_score(&self, slo_state: &SloState) -> f64 {
let total_slos = slo_state.slo_status.len() as f64;
if total_slos == 0.0 {
return 100.0;
}
let meeting_count = slo_state.slo_status.values()
.filter(|s| matches!(s.status, crate::calibration::slo_system::SloHealthStatus::Meeting))
.count() as f64;
let warning_count = slo_state.slo_status.values()
.filter(|s| matches!(s.status, crate::calibration::slo_system::SloHealthStatus::Warning))
.count() as f64;
let score = (meeting_count * 100.0 + warning_count * 70.0) / total_slos;
score.round()
}
async fn create_reliability_chart(&self, historical_data: &HistoricalData) -> Result<ReliabilityChartComponent> {
let mut compliance_data = HashMap::new();
for (slo_type, history) in &historical_data.slo_history {
compliance_data.insert(slo_type.clone(), history.clone().into());
}
let breach_markers = historical_data.breach_events.iter()
.map(|event| BreachMarker {
timestamp: event.timestamp,
slo_type: event.slo_type.clone(),
severity: event.severity.clone(),
duration: event.duration,
tooltip: format!("SLO breach: {:?} - {}", event.slo_type, event.severity),
})
.collect();
let threshold_lines = ThresholdLines {
target_line: ThresholdLine {
value: 1.0,
color: "#28a745".to_string(),
style: "solid".to_string(),
label: "Target".to_string(),
},
warning_line: ThresholdLine {
value: 0.9,
color: "#ffc107".to_string(),
style: "dashed".to_string(),
label: "Warning".to_string(),
},
critical_line: Some(ThresholdLine {
value: 0.8,
color: "#dc3545".to_string(),
style: "dotted".to_string(),
label: "Critical".to_string(),
}),
};
let chart_config = ChartConfig {
chart_type: ChartType::Line,
x_axis: AxisConfig {
label: "Time".to_string(),
min: None,
max: None,
format: "%H:%M".to_string(),
grid_lines: true,
},
y_axis: AxisConfig {
label: "SLO Compliance".to_string(),
min: Some(0.0),
max: Some(1.2),
format: "%.2f".to_string(),
grid_lines: true,
},
legend: LegendConfig {
show: true,
position: "bottom".to_string(),
orientation: "horizontal".to_string(),
},
tooltips: TooltipConfig {
show: true,
format: "{series}: {value} at {timestamp}".to_string(),
include_metadata: true,
},
};
Ok(ReliabilityChartComponent {
compliance_data,
breach_markers,
threshold_lines,
chart_config,
})
}
async fn create_calibration_table(&self) -> Result<CalibrationTableComponent> {
Ok(CalibrationTableComponent {
calibration_slices: vec![],
bin_statistics: BinStatistics {
avg_samples_per_bin: 0.0,
bin_coverage_distribution: vec![],
merged_bin_rate: 0.0,
empty_bin_count: 0,
},
mask_mismatch: MaskMismatchReport {
fit_vs_eval_mismatches: vec![],
mismatch_rate: 0.0,
affected_slices: vec![],
severity: "none".to_string(),
},
table_config: TableConfig {
sortable_columns: vec!["bin_id".to_string(), "ece".to_string()],
filterable_columns: vec!["language".to_string(), "intent".to_string()],
default_sort: "bin_id".to_string(),
page_size: 25,
show_pagination: true,
},
})
}
async fn create_drift_analysis(&self) -> Result<DriftAnalysisComponent> {
Ok(DriftAnalysisComponent {
weekly_deltas: WeeklyDriftReport {
report_week: Utc::now(),
metric_deltas: HashMap::new(),
overall_status: "stable".to_string(),
breached_thresholds: vec![],
},
trend_analysis: DriftTrendAnalysis {
trending_metrics: vec![],
stability_score: 95.0,
prediction: DriftPrediction {
predicted_breach_date: None,
confidence: 0.8,
recommended_actions: vec!["Monitor trend".to_string()],
},
},
threshold_visualization: ThresholdVisualization {
threshold_lines: vec![],
current_values: HashMap::new(),
buffer_zones: vec![],
},
})
}
async fn create_alert_panel(&self, active_alerts: &[crate::calibration::slo_system::ActiveAlert]) -> Result<AlertPanelComponent> {
let alert_items: Vec<AlertDisplayItem> = active_alerts.iter()
.map(|alert| AlertDisplayItem {
id: alert.id.clone(),
slo_type: alert.slo_type.clone(),
severity: format!("{:?}", alert.severity),
title: format!("SLO Alert: {}", alert.slo_type.name()),
description: alert.message.clone(),
timestamp: alert.started_at,
status: if alert.acknowledged { "acknowledged" } else { "active" }.to_string(),
assignee: None,
})
.collect();
let alert_stats = AlertStatistics {
total_alerts_today: active_alerts.len(),
alerts_by_severity: HashMap::new(), average_resolution_time: Duration::minutes(30),
escalation_rate: 0.1,
};
Ok(AlertPanelComponent {
active_alerts: alert_items.clone(),
recent_alerts: alert_items,
alert_stats,
})
}
pub async fn export(&self, format: ExportFormat, filters: Option<ViewFilters>) -> Result<Vec<u8>> {
self.export_manager.export_dashboard_data(
&self.dashboard_state.read().await,
format,
filters,
).await
}
pub async fn get_dashboard_state(&self) -> DashboardState {
self.dashboard_state.read().await.clone()
}
pub async fn get_visualizations(&self) -> VisualizationComponents {
self.visualizations.read().await.clone()
}
fn default_config() -> DashboardConfig {
DashboardConfig {
refresh_interval: Duration::seconds(30),
data_retention: Duration::days(7),
max_data_points: 1000,
default_time_range: TimeRange::Last24Hours,
theme: DashboardTheme::default(),
export_config: ExportConfig::default(),
alert_integration: AlertIntegrationConfig {
show_alerts: true,
alert_display_duration: Duration::seconds(30),
sound_notifications: false,
alert_styling: AlertStyling {
animation: AlertAnimation::Fade,
position: AlertPosition::TopRight,
opacity: 0.9,
},
},
}
}
}
impl ExportManager {
pub fn new(config: ExportConfig) -> Self {
Self { config }
}
pub async fn export_dashboard_data(
&self,
dashboard_state: &DashboardState,
format: ExportFormat,
_filters: Option<ViewFilters>,
) -> Result<Vec<u8>> {
match format {
ExportFormat::Json => {
let json = serde_json::to_string_pretty(dashboard_state)?;
Ok(json.into_bytes())
}
ExportFormat::Csv => {
Ok(b"CSV export not yet implemented".to_vec())
}
_ => {
bail!("Export format not supported: {:?}", format)
}
}
}
}
impl Default for DashboardState {
fn default() -> Self {
Self {
last_updated: Utc::now(),
current_slo_state: None,
historical_data: HistoricalData {
slo_history: HashMap::new(),
breach_events: VecDeque::new(),
drift_history: VecDeque::new(),
calibration_history: VecDeque::new(),
},
dashboard_metrics: DashboardMetrics {
average_render_time_ms: 0.0,
p99_render_time_ms: 0.0,
data_load_time_ms: 0.0,
updates_per_minute: 0.0,
memory_usage_mb: 0.0,
},
view_state: ViewState {
time_range: TimeRange::Last24Hours,
filters: ViewFilters {
languages: vec![],
intents: vec![],
severities: vec![],
statuses: vec![],
},
selected_slos: vec![],
drill_down: None,
},
}
}
}
impl Default for VisualizationComponents {
fn default() -> Self {
Self {
slo_overview: SloOverviewComponent {
system_health: SystemHealthIndicator {
health: SystemHealth::Healthy,
health_score: 100.0,
status_text: "Healthy".to_string(),
last_updated: Utc::now(),
},
slo_cards: vec![],
metrics_summary: MetricsSummary {
total_slos: 0,
slos_meeting_target: 0,
slos_at_warning: 0,
slos_breached: 0,
average_availability: 100.0,
mttr_minutes: 0.0,
},
},
reliability_chart: ReliabilityChartComponent {
compliance_data: HashMap::new(),
breach_markers: vec![],
threshold_lines: ThresholdLines {
target_line: ThresholdLine {
value: 1.0,
color: "#28a745".to_string(),
style: "solid".to_string(),
label: "Target".to_string(),
},
warning_line: ThresholdLine {
value: 0.9,
color: "#ffc107".to_string(),
style: "dashed".to_string(),
label: "Warning".to_string(),
},
critical_line: None,
},
chart_config: ChartConfig {
chart_type: ChartType::Line,
x_axis: AxisConfig {
label: "Time".to_string(),
min: None,
max: None,
format: "%H:%M".to_string(),
grid_lines: true,
},
y_axis: AxisConfig {
label: "Value".to_string(),
min: None,
max: None,
format: "%.2f".to_string(),
grid_lines: true,
},
legend: LegendConfig {
show: true,
position: "bottom".to_string(),
orientation: "horizontal".to_string(),
},
tooltips: TooltipConfig {
show: true,
format: "{value}".to_string(),
include_metadata: false,
},
},
},
calibration_table: CalibrationTableComponent {
calibration_slices: vec![],
bin_statistics: BinStatistics {
avg_samples_per_bin: 0.0,
bin_coverage_distribution: vec![],
merged_bin_rate: 0.0,
empty_bin_count: 0,
},
mask_mismatch: MaskMismatchReport {
fit_vs_eval_mismatches: vec![],
mismatch_rate: 0.0,
affected_slices: vec![],
severity: "none".to_string(),
},
table_config: TableConfig {
sortable_columns: vec![],
filterable_columns: vec![],
default_sort: "id".to_string(),
page_size: 25,
show_pagination: true,
},
},
drift_analysis: DriftAnalysisComponent {
weekly_deltas: WeeklyDriftReport {
report_week: Utc::now(),
metric_deltas: HashMap::new(),
overall_status: "stable".to_string(),
breached_thresholds: vec![],
},
trend_analysis: DriftTrendAnalysis {
trending_metrics: vec![],
stability_score: 100.0,
prediction: DriftPrediction {
predicted_breach_date: None,
confidence: 1.0,
recommended_actions: vec![],
},
},
threshold_visualization: ThresholdVisualization {
threshold_lines: vec![],
current_values: HashMap::new(),
buffer_zones: vec![],
},
},
alert_panel: AlertPanelComponent {
active_alerts: vec![],
recent_alerts: vec![],
alert_stats: AlertStatistics {
total_alerts_today: 0,
alerts_by_severity: HashMap::new(),
average_resolution_time: Duration::seconds(0),
escalation_rate: 0.0,
},
},
}
}
}
impl Default for DashboardTheme {
fn default() -> Self {
Self {
color_scheme: ColorScheme {
primary: "#007bff".to_string(),
secondary: "#6c757d".to_string(),
success: "#28a745".to_string(),
warning: "#ffc107".to_string(),
error: "#dc3545".to_string(),
info: "#17a2b8".to_string(),
background: "#ffffff".to_string(),
surface: "#f8f9fa".to_string(),
text_primary: "#212529".to_string(),
text_secondary: "#6c757d".to_string(),
},
chart_style: ChartStyle {
line_width: 2.0,
point_radius: 4.0,
bar_spacing: 0.1,
bar_corner_radius: 2.0,
grid_opacity: 0.3,
axis_color: "#6c757d".to_string(),
},
typography: Typography {
font_family: "system-ui, -apple-system, sans-serif".to_string(),
heading_size: 24.0,
body_size: 14.0,
caption_size: 12.0,
heading_weight: "600".to_string(),
body_weight: "400".to_string(),
},
layout: LayoutConfig {
grid_columns: 12,
grid_gap: 16.0,
padding: 16.0,
margin: 8.0,
component_min_height: 200.0,
sidebar_width: 250.0,
},
}
}
}
impl Default for ExportConfig {
fn default() -> Self {
Self {
supported_formats: vec![
ExportFormat::Json,
ExportFormat::Csv,
ExportFormat::Png,
],
image_quality: ImageQuality {
dpi: 300,
compression: 0.8,
width: None,
height: None,
},
data_export: DataExportConfig {
include_raw_data: true,
include_metadata: true,
max_rows: 10000,
date_format: "%Y-%m-%d %H:%M:%S".to_string(),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_dashboard_creation() {
}
#[tokio::test]
async fn test_data_refresh() {
}
#[tokio::test]
async fn test_export_functionality() {
}
}