1use chrono::{DateTime, Duration as ChronoDuration, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::TemporalId;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DurationEstimate {
11 pub id: TemporalId,
13
14 pub label: String,
16
17 pub optimistic_secs: i64,
19
20 pub expected_secs: i64,
22
23 pub pessimistic_secs: i64,
25
26 pub confidence: f64,
28
29 pub source: DurationSource,
31
32 pub created_at: DateTime<Utc>,
34
35 pub tags: Vec<String>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub enum DurationSource {
42 UserEstimate,
44 Historical {
46 sample_count: u32,
48 },
49 Predicted {
51 model: String,
53 confidence: f64,
55 },
56 Default,
58}
59
60impl DurationEstimate {
61 pub fn new(label: impl Into<String>, optimistic: i64, expected: i64, pessimistic: i64) -> Self {
63 Self {
64 id: TemporalId::new(),
65 label: label.into(),
66 optimistic_secs: optimistic,
67 expected_secs: expected,
68 pessimistic_secs: pessimistic,
69 confidence: 0.8,
70 source: DurationSource::UserEstimate,
71 created_at: Utc::now(),
72 tags: Vec::new(),
73 }
74 }
75
76 pub fn pert_estimate(&self) -> ChronoDuration {
78 let pert = (self.optimistic_secs + 4 * self.expected_secs + self.pessimistic_secs) / 6;
79 ChronoDuration::seconds(pert)
80 }
81
82 pub fn std_deviation(&self) -> ChronoDuration {
84 let sd = (self.pessimistic_secs - self.optimistic_secs) / 6;
85 ChronoDuration::seconds(sd)
86 }
87
88 pub fn optimistic(&self) -> ChronoDuration {
90 ChronoDuration::seconds(self.optimistic_secs)
91 }
92
93 pub fn expected(&self) -> ChronoDuration {
95 ChronoDuration::seconds(self.expected_secs)
96 }
97
98 pub fn pessimistic(&self) -> ChronoDuration {
100 ChronoDuration::seconds(self.pessimistic_secs)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_pert_estimate() {
110 let d = DurationEstimate::new("test", 60, 120, 300);
111 assert_eq!(d.pert_estimate().num_seconds(), 140);
113 }
114
115 #[test]
116 fn test_std_deviation() {
117 let d = DurationEstimate::new("test", 60, 120, 300);
118 assert_eq!(d.std_deviation().num_seconds(), 40);
120 }
121}