use super::SeriesData;
use super::charming_extensions::MultiSeries;
use crate::TimeDeltaExt;
use crate::filters::group_by_trackable_item;
use crate::model::TimeLog;
use chrono::Duration;
use std::collections::BTreeMap;
struct TrackedTime {
time_spent: Duration,
estimate: Duration,
}
pub(super) fn calculate_estimate_data<'a, T, Series>(
grouped_time_log: BTreeMap<impl Into<Option<&'a T>> + Clone, Vec<&'a TimeLog>>,
) -> (SeriesData, Vec<String>)
where
T: std::fmt::Display + 'a,
Series: MultiSeries,
{
let capacity = grouped_time_log.len();
let mut axis_labels = Vec::with_capacity(capacity);
let mut estimate_sums = Vec::with_capacity(capacity);
let mut actual_sums = Vec::with_capacity(capacity);
for (outer_key, time_logs) in grouped_time_log {
axis_labels.push(Series::option_to_string(outer_key));
let tracked_time_by_item = group_by_trackable_item(time_logs)
.map(|(trackable_item, _)| TrackedTime {
estimate: trackable_item.common.time_estimate,
time_spent: trackable_item.common.total_time_spent,
})
.collect::<Vec<_>>();
let (estimate_sum, actual_sum) = tracked_time_by_item.iter().fold(
(Duration::zero(), Duration::zero()),
|(est, act), tracked| (est + tracked.estimate, act + tracked.time_spent),
);
estimate_sums.push(estimate_sum.total_hours());
actual_sums.push(actual_sum.total_hours());
}
let series = vec![
("Estimates".into(), estimate_sums),
("Actual Time".into(), actual_sums),
];
(series, axis_labels)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::charts::tests::*;
use crate::filters::group_by_milestone;
use crate::model::Milestone;
use charming::series::Bar;
#[test]
fn test_calculate_estimate_data() {
const AXIS_LABELS: &[&str] = &["None", "M1", "M2"];
const NUMBER_OF_SERIES: usize = 2;
const NUMBER_OF_DATA_POINTS: usize = 3;
const ESTIMATES_DATA: [f32; NUMBER_OF_DATA_POINTS] = [2.0, 7.0, 2.0];
const ACTUAL_DATA: [f32; NUMBER_OF_DATA_POINTS] = [5.0, 7.75, 1.5];
let time_logs = get_time_logs();
let by_milestone = group_by_milestone(&time_logs).collect();
let (series, axis_labels) = calculate_estimate_data::<Milestone, Bar>(by_milestone);
assert_eq!(axis_labels, AXIS_LABELS);
assert_eq!(series.len(), NUMBER_OF_SERIES);
let estimate_data = series.first().unwrap();
assert_eq!(estimate_data.0, "Estimates");
assert_eq!(estimate_data.1, ESTIMATES_DATA);
let actual_data = series.last().unwrap();
assert_eq!(actual_data.0, "Actual Time");
assert_eq!(actual_data.1, ACTUAL_DATA);
}
}