gitlab_time_report/charts/
estimates.rs1use super::SeriesData;
4use super::charming_extensions::MultiSeries;
5use crate::TimeDeltaExt;
6use crate::filters::group_by_trackable_item;
7use crate::model::TimeLog;
8use chrono::Duration;
9use std::collections::BTreeMap;
10
11struct TrackedTime {
13 time_spent: Duration,
15 estimate: Duration,
17}
18
19pub(super) fn calculate_estimate_data<'a, T, Series>(
22 grouped_time_log: BTreeMap<impl Into<Option<&'a T>> + Clone, Vec<&'a TimeLog>>,
23) -> (SeriesData, Vec<String>)
24where
25 T: std::fmt::Display + 'a,
26 Series: MultiSeries,
27{
28 let capacity = grouped_time_log.len();
29 let mut axis_labels = Vec::with_capacity(capacity);
30 let mut estimate_sums = Vec::with_capacity(capacity);
31 let mut actual_sums = Vec::with_capacity(capacity);
32
33 for (outer_key, time_logs) in grouped_time_log {
34 axis_labels.push(Series::option_to_string(outer_key));
35
36 let tracked_time_by_item = group_by_trackable_item(time_logs)
38 .map(|(trackable_item, _)| TrackedTime {
39 estimate: trackable_item.common.time_estimate,
40 time_spent: trackable_item.common.total_time_spent,
41 })
42 .collect::<Vec<_>>();
43
44 let (estimate_sum, actual_sum) = tracked_time_by_item.iter().fold(
46 (Duration::zero(), Duration::zero()),
47 |(est, act), tracked| (est + tracked.estimate, act + tracked.time_spent),
48 );
49
50 estimate_sums.push(estimate_sum.total_hours());
51 actual_sums.push(actual_sum.total_hours());
52 }
53
54 let series = vec![
55 ("Estimates".into(), estimate_sums),
56 ("Actual Time".into(), actual_sums),
57 ];
58
59 (series, axis_labels)
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use crate::charts::tests::*;
66 use crate::filters::group_by_milestone;
67 use crate::model::Milestone;
68 use charming::series::Bar;
69
70 #[test]
71 fn test_calculate_estimate_data() {
72 const AXIS_LABELS: &[&str] = &["None", "M1", "M2"];
73 const NUMBER_OF_SERIES: usize = 2;
74 const NUMBER_OF_DATA_POINTS: usize = 3;
75 const ESTIMATES_DATA: [f32; NUMBER_OF_DATA_POINTS] = [2.0, 7.0, 2.0];
76 const ACTUAL_DATA: [f32; NUMBER_OF_DATA_POINTS] = [5.0, 7.75, 1.5];
77
78 let time_logs = get_time_logs();
79 let by_milestone = group_by_milestone(&time_logs).collect();
80 let (series, axis_labels) = calculate_estimate_data::<Milestone, Bar>(by_milestone);
81
82 assert_eq!(axis_labels, AXIS_LABELS);
83 assert_eq!(series.len(), NUMBER_OF_SERIES);
84
85 let estimate_data = series.first().unwrap();
86 assert_eq!(estimate_data.0, "Estimates");
87 assert_eq!(estimate_data.1, ESTIMATES_DATA);
88
89 let actual_data = series.last().unwrap();
90 assert_eq!(actual_data.0, "Actual Time");
91 assert_eq!(actual_data.1, ACTUAL_DATA);
92 }
93}