1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ValueReport {
9 pub period: String,
10 pub total_executions: u64,
11 pub successful_executions: u64,
12 pub failed_executions: u64,
13 pub auto_fixes: u64,
14 pub scheduled_runs: u64,
15 pub manual_runs: u64,
16 pub total_api_cost: f64,
17 pub estimated_time_saved_hours: f64,
18 pub hourly_rate_assumption: f64,
19 pub estimated_value_saved: f64,
20 pub roi_ratio: f64,
21 pub workflow_success_rate: f64,
22 pub generated_at: DateTime<Utc>,
23}
24
25impl ValueReport {
26 pub fn generate(
28 period: &str,
29 executions: u64,
30 successful: u64,
31 auto_fixes: u64,
32 scheduled: u64,
33 api_cost: f64,
34 ) -> Self {
35 let failed = executions.saturating_sub(successful);
36 let manual = executions.saturating_sub(scheduled);
37
38 let time_saved = (auto_fixes as f64 * 0.25)
43 + (scheduled as f64 * 0.083)
44 + (manual as f64 * 0.167);
45
46 let hourly_rate = 50.0;
47 let value_saved = time_saved * hourly_rate;
48 let roi = if api_cost > 0.0 {
49 value_saved / api_cost
50 } else {
51 0.0
52 };
53 let success_rate = if executions > 0 {
54 successful as f64 / executions as f64 * 100.0
55 } else {
56 0.0
57 };
58
59 Self {
60 period: period.to_string(),
61 total_executions: executions,
62 successful_executions: successful,
63 failed_executions: failed,
64 auto_fixes,
65 scheduled_runs: scheduled,
66 manual_runs: manual,
67 total_api_cost: api_cost,
68 estimated_time_saved_hours: time_saved,
69 hourly_rate_assumption: hourly_rate,
70 estimated_value_saved: value_saved,
71 roi_ratio: roi,
72 workflow_success_rate: success_rate,
73 generated_at: Utc::now(),
74 }
75 }
76
77 pub fn format(&self) -> String {
79 format!(
80 r#"📊 MUR Commander — {} Report
81
82🕐 Time Saved: {:.1} hours
83 ├── Auto-fixes: {} → {:.1}h saved
84 ├── Scheduled: {} → {:.1}h saved
85 └── Manual: {} → {:.1}h saved
86
87💰 API Cost: ${:.2}
88📈 ROI: {:.1}x (each $1 → ${:.0} value @ ${}/hr)
89🏥 Success Rate: {:.0}%
90
91📋 Executions: {} total ({} ✅ / {} ❌)"#,
92 self.period,
93 self.estimated_time_saved_hours,
94 self.auto_fixes,
95 self.auto_fixes as f64 * 0.25,
96 self.scheduled_runs,
97 self.scheduled_runs as f64 * 0.083,
98 self.manual_runs,
99 self.manual_runs as f64 * 0.167,
100 self.total_api_cost,
101 self.roi_ratio,
102 self.roi_ratio * 1.0,
103 self.hourly_rate_assumption,
104 self.workflow_success_rate,
105 self.total_executions,
106 self.successful_executions,
107 self.failed_executions,
108 )
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn test_generate_report() {
118 let report = ValueReport::generate("February 2026", 50, 45, 8, 30, 8.72);
119 assert_eq!(report.total_executions, 50);
120 assert_eq!(report.successful_executions, 45);
121 assert_eq!(report.auto_fixes, 8);
122 assert!(report.estimated_time_saved_hours > 0.0);
123 assert!(report.roi_ratio > 0.0);
124 assert!((report.workflow_success_rate - 90.0).abs() < 0.1);
125 }
126
127 #[test]
128 fn test_format_report() {
129 let report = ValueReport::generate("Feb 2026", 50, 45, 8, 30, 8.72);
130 let text = report.format();
131 assert!(text.contains("Time Saved"));
132 assert!(text.contains("API Cost"));
133 assert!(text.contains("ROI"));
134 }
135
136 #[test]
137 fn test_zero_executions() {
138 let report = ValueReport::generate("Empty Month", 0, 0, 0, 0, 0.0);
139 assert_eq!(report.workflow_success_rate, 0.0);
140 assert_eq!(report.roi_ratio, 0.0);
141 }
142}