1use std::collections::HashMap;
2
3use chrono::{Datelike, NaiveDate};
4use num_format::ToFormattedString;
5
6use crate::models::saved_scrobbles::SavedScrobble;
7use crate::utils;
8
9pub struct Stats {
10 average_tracks_per_day: f64,
11 average_tracks_per_week: f64,
12 average_tracks_per_month: f64,
13 average_tracks_per_year: f64,
14
15 best_month: (String, i32),
16}
17
18impl Stats {
19 pub fn new(scrobbles: &[SavedScrobble]) -> Self {
20 Self {
21 average_tracks_per_day: calculate_daily_average(scrobbles),
22 average_tracks_per_week: calculate_weekly_average(scrobbles),
23 average_tracks_per_month: calculate_monthly_average(scrobbles),
24 average_tracks_per_year: calculate_yearly_average(scrobbles),
25
26 best_month: calculate_best_month(scrobbles),
27 }
28 }
29
30 pub fn print(&self) {
31 println!("STATS:\n");
32
33 println!("Average Tracks Per Day: {}", self.average_tracks_per_day);
34 println!("Average Tracks Per Week: {}", self.average_tracks_per_week);
35 println!(
36 "Average Tracks Per Month: {}",
37 self.average_tracks_per_month
38 );
39 println!("Average Tracks Per Year: {}", self.average_tracks_per_year);
40
41 println!(
42 "Best Month: {} ({} scrobbles)",
43 self.best_month.0,
44 self.best_month.1.to_formatted_string(&utils::get_locale())
45 );
46 }
47}
48
49fn calculate_daily_average(scrobbles: &[SavedScrobble]) -> f64 {
50 let mut groups: HashMap<NaiveDate, i32> = HashMap::new();
51
52 scrobbles.iter().for_each(|scrobble| {
53 let group = groups.entry(scrobble.date()).or_insert(0);
54 *group += 1
55 });
56
57 groups.iter().map(|g| g.1).sum::<i32>() as f64 / utils::get_total_days(&scrobbles) as f64
58}
59
60fn calculate_weekly_average(scrobbles: &[SavedScrobble]) -> f64 {
61 let mut groups: HashMap<u32, i32> = HashMap::new();
62
63 scrobbles.iter().for_each(|scrobble| {
64 let group = groups.entry(scrobble.date().iso_week().week()).or_insert(0);
65 *group += 1;
66 });
67
68 groups.iter().map(|g| g.1).sum::<i32>() as f64 / utils::get_total_weeks(&scrobbles) as f64
69}
70
71fn calculate_monthly_average(scrobbles: &[SavedScrobble]) -> f64 {
72 let mut groups: HashMap<u32, i32> = HashMap::new();
73
74 scrobbles.iter().for_each(|scrobble| {
75 let group = groups.entry(scrobble.date().month()).or_insert(0);
76 *group += 1;
77 });
78
79 groups.iter().map(|g| g.1).sum::<i32>() as f64 / utils::get_total_months(&scrobbles) as f64
80}
81
82fn calculate_yearly_average(scrobbles: &[SavedScrobble]) -> f64 {
83 let mut groups: HashMap<i32, i32> = HashMap::new();
84
85 scrobbles.iter().for_each(|scrobble| {
86 let group = groups.entry(scrobble.date().year()).or_insert(0);
87 *group += 1
88 });
89
90 groups.iter().map(|g| g.1).sum::<i32>() as f64 / utils::get_total_years(&scrobbles) as f64
91}
92
93fn calculate_best_month(scrobbles: &[SavedScrobble]) -> (String, i32) {
94 let mut groups: HashMap<String, i32> = HashMap::new();
95
96 scrobbles.iter().for_each(|scrobble| {
97 let group = groups.entry(scrobble.month_year()).or_insert(0);
98 *group += 1
99 });
100
101 let mut group_vec = groups.iter().collect::<Vec<(&String, &i32)>>();
102 group_vec.sort_by(|a, b| a.1.cmp(b.1));
103 group_vec.reverse();
104
105 let best_month = group_vec.get(0).expect("Error retrieving best month");
106
107 (best_month.0.to_string(), *best_month.1)
108}