crate_activity/
summary.rs

1crate::ix!();
2
3#[derive(Builder,Debug)]
4#[builder(setter(into))]
5pub struct CrateActivitySummary {
6    date_interval_1d:              NaiveDate,
7    date_interval_3d:              NaiveDate,
8    date_interval_full_start:      NaiveDate,
9    date_interval_full_end:        NaiveDate,
10    total_downloads:               i64,
11    avg_daily_downloads:           f64,
12    avg_daily_downloads_per_crate: f64,
13    median_daily_downloads:        i64,
14    crates_analyzed:               usize,
15    top_crates_1d:                 Vec<(String, i64)>,
16    top_crates_3d:                 Vec<(String, i64)>,
17}
18
19impl fmt::Display for CrateActivitySummary {
20
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22
23        writeln!(f, "Crate Activity Summary:")?;
24        writeln!(f, "  Full Data Range:             {} to {}", self.date_interval_full_start, self.date_interval_full_end)?;
25        writeln!(f, "  Date Interval (Last 1 Day):  {}", self.date_interval_1d)?;
26        writeln!(f, "  Date Interval (Last 3 Days): {}", self.date_interval_3d)?;
27
28        writeln!(f, "  Total Downloads:                   {}", self.total_downloads)?;
29        writeln!(f, "  Average Daily Downloads:           {:.2}", self.avg_daily_downloads)?;
30        writeln!(f, "  Average Daily Downloads per Crate: {:.2}", self.avg_daily_downloads_per_crate)?;
31        writeln!(f, "  Median Daily Downloads:            {}", self.median_daily_downloads)?;
32        writeln!(f, "  Crates Analyzed:                   {}", self.crates_analyzed)?;
33
34        let mut total_1d_downloads = 0;
35        writeln!(f, "\nTop Crates (Last 1 Day):")?;
36        for (crate_name, downloads) in &self.top_crates_1d {
37            total_1d_downloads += downloads;
38            writeln!(f, "  {:<30} {:>10} downloads", crate_name, downloads)?;
39        }
40        writeln!(f, "  {:<30} total downloads (Last 1 Day)", total_1d_downloads)?;
41
42        let mut total_3d_downloads = 0;
43        writeln!(f, "\nTop Crates (Last 3 Days):")?;
44        for (crate_name, downloads) in &self.top_crates_3d {
45            total_3d_downloads += downloads;
46            writeln!(f, "  {:<30} {:>10} downloads", crate_name, downloads)?;
47        }
48        writeln!(f, "  {:<30} total downloads (Last 3 Days)", total_3d_downloads)?;
49
50        Ok(())
51    }
52}
53
54impl CrateActivitySummary {
55
56    pub fn new(
57        summaries: &[CrateUsageSummary],
58        interval_downloads_1d: HashMap<String, i64>,
59        interval_downloads_3d: HashMap<String, i64>,
60        one_day_ago: NaiveDate,
61        three_days_ago: NaiveDate,
62    ) -> Self {
63
64        // Compute the full date range
65        let (full_start, full_end) = summaries
66            .iter()
67            .flat_map(|s| s.version_downloads())
68            .map(|d| d.date())
69            .minmax()
70            .into_option()
71            .unwrap_or((&one_day_ago, &one_day_ago));
72
73        // Overall stats
74        let total_downloads: i64 = summaries.iter().map(|s| s.total_downloads()).sum();
75        let avg_daily_downloads: f64 = summaries.iter().map(|s| s.average_daily_downloads()).sum::<f64>();
76        let avg_daily_downloads_per_crate = avg_daily_downloads / summaries.len() as f64;
77
78        // Median daily downloads
79        let mut daily_downloads: Vec<i64> = summaries.iter().map(|s| *s.total_downloads()).collect();
80        daily_downloads.sort();
81        let median_daily_downloads = if daily_downloads.is_empty() {
82            0
83        } else if daily_downloads.len() % 2 == 0 {
84            let mid = daily_downloads.len() / 2;
85            (daily_downloads[mid - 1] + daily_downloads[mid]) / 2
86        } else {
87            daily_downloads[daily_downloads.len() / 2]
88        };
89
90        // Top crates for the last 1 and 3 days
91        let mut top_crates_1d: Vec<_> = interval_downloads_1d.into_iter().collect();
92        let mut top_crates_3d: Vec<_> = interval_downloads_3d.into_iter().collect();
93
94        top_crates_1d.sort_by_key(|&(_, downloads)| std::cmp::Reverse(downloads));
95        top_crates_3d.sort_by_key(|&(_, downloads)| std::cmp::Reverse(downloads));
96
97        CrateActivitySummary {
98            date_interval_1d:         one_day_ago,
99            date_interval_3d:         three_days_ago,
100            date_interval_full_end:   *full_end,
101            date_interval_full_start: *full_start,
102            total_downloads,
103            avg_daily_downloads,
104            avg_daily_downloads_per_crate,
105            median_daily_downloads,
106            crates_analyzed: summaries.len(),
107            top_crates_1d,
108            top_crates_3d,
109        }
110    }
111}