crate_activity/
align_and_normalize.rs

1crate::ix!();
2
3pub fn align_and_normalize_data(
4    version_downloads: &[VersionDownload],
5    full_date_range:   &[NaiveDate],
6
7) -> Vec<i64> {
8
9    let downloads_map: HashMap<NaiveDate, i64> = version_downloads
10        .iter()
11        .map(|d| (*d.date(), *d.downloads())) // Dereference both the date and the downloads
12        .collect();
13
14    full_date_range
15        .iter()
16        .map(|date| *downloads_map.get(date).unwrap_or(&0)) // Fill missing dates with 0
17        .collect()
18}
19
20pub fn debug_alignment(
21    crate_a: &str,
22    crate_b: &str,
23    aligned_a: &[i64],
24    aligned_b: &[i64],
25) {
26    println!("Aligned data for {} and {}:", crate_a, crate_b);
27    println!("  Aligned A: {:?}", aligned_a);
28    println!("  Aligned B: {:?}", aligned_b);
29}
30
31#[cfg(test)]
32mod alignment_and_normalization_tests {
33    use super::*;
34    use chrono::NaiveDate;
35
36    #[test]
37    fn test_empty_input() {
38        let version_downloads = [];
39        let full_date_range = [];
40        let result = align_and_normalize_data(&version_downloads, &full_date_range);
41        assert_eq!(result, Vec::<i64>::new(), "Empty input should produce empty output.");
42    }
43
44    #[test]
45    fn test_full_date_range_matches_data() {
46        let version_downloads = [
47            VersionDownloadBuilder::default()
48                .version(1_i64)
49                .downloads(100_i64)
50                .date(NaiveDate::from_ymd_opt(2024, 12, 1).unwrap())
51                .build()
52                .unwrap(),
53            VersionDownloadBuilder::default()
54                .version(1_i64)
55                .downloads(200_i64)
56                .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
57                .build()
58                .unwrap(),
59        ];
60        let full_date_range = vec![
61            NaiveDate::from_ymd_opt(2024, 12, 1).unwrap(),
62            NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
63        ];
64        let result = align_and_normalize_data(&version_downloads, &full_date_range);
65        assert_eq!(result, vec![100, 200], "All dates should align correctly.");
66    }
67
68    #[test]
69    fn test_full_date_range_extends_beyond_data() {
70        let version_downloads = [
71            VersionDownloadBuilder::default()
72                .version(1_i64)
73                .downloads(200_i64)
74                .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
75                .build()
76                .unwrap(),
77        ];
78        let full_date_range = vec![
79            NaiveDate::from_ymd_opt(2024, 12, 1).unwrap(),
80            NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
81            NaiveDate::from_ymd_opt(2024, 12, 3).unwrap(),
82        ];
83        let result = align_and_normalize_data(&version_downloads, &full_date_range);
84        assert_eq!(
85            result,
86            vec![0, 200, 0],
87            "Missing dates should be filled with 0."
88        );
89    }
90
91    #[test]
92    fn test_full_date_range_subset_of_data() {
93        let version_downloads = [
94            VersionDownloadBuilder::default()
95                .version(1_i64)
96                .downloads(100_i64)
97                .date(NaiveDate::from_ymd_opt(2024, 12, 1).unwrap())
98                .build()
99                .unwrap(),
100            VersionDownloadBuilder::default()
101                .version(1_i64)
102                .downloads(200_i64)
103                .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
104                .build()
105                .unwrap(),
106            VersionDownloadBuilder::default()
107                .version(1_i64)
108                .downloads(300_i64)
109                .date(NaiveDate::from_ymd_opt(2024, 12, 3).unwrap())
110                .build()
111                .unwrap(),
112        ];
113        let full_date_range = vec![
114            NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
115            NaiveDate::from_ymd_opt(2024, 12, 3).unwrap(),
116        ];
117        let result = align_and_normalize_data(&version_downloads, &full_date_range);
118        assert_eq!(
119            result,
120            vec![200, 300],
121            "Only values within the full_date_range should be included."
122        );
123    }
124
125    #[test]
126    fn test_duplicate_dates_in_input() {
127        let version_downloads = [
128            VersionDownloadBuilder::default()
129                .version(1_i64)
130                .downloads(100_i64)
131                .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
132                .build()
133                .unwrap(),
134            VersionDownloadBuilder::default()
135                .version(1_i64)
136                .downloads(200_i64)
137                .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
138                .build()
139                .unwrap(), // Duplicate date
140        ];
141        let full_date_range = vec![
142            NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
143        ];
144        let result = align_and_normalize_data(&version_downloads, &full_date_range);
145        assert_eq!(
146            result,
147            vec![200],
148            "The most recent value for a duplicate date should be used."
149        );
150    }
151}