kobo_db_tools/statistics/imp/
sessions.rs

1use crate::Statistics;
2use crate::{ReadingMetric, ReadingSessions};
3use std::cmp::Ordering;
4
5impl Statistics for ReadingSessions {
6    type Metric = ReadingMetric;
7
8    fn avg(&self) -> f64 {
9        let valid_sessions_seconds: Vec<f64> = self
10            .valid_sessions()
11            .filter_map(|s| s.seconds_read.map(|sec| sec as f64))
12            .collect();
13
14        if valid_sessions_seconds.is_empty() {
15            0.0
16        } else {
17            valid_sessions_seconds.iter().sum::<f64>() / valid_sessions_seconds.len() as f64
18        }
19    }
20    fn calculate_percentile(&self, metric: ReadingMetric, percentiles: &[f64]) -> Vec<f64> {
21        let mut values: Vec<f64> = self
22            .valid_sessions()
23            .map(|s| match metric {
24                ReadingMetric::SecondsRead => s.seconds_read.unwrap_or(0) as f64,
25                ReadingMetric::PagesTurned => s.pages_turned.unwrap_or(0) as f64,
26                ReadingMetric::ButtonPressCount => s.button_press_count.unwrap_or(0) as f64,
27                ReadingMetric::Progress => (s.end_progress.unwrap_or(0) - s.start_progress) as f64,
28            })
29            .collect();
30
31        if values.is_empty() {
32            return vec![0.0];
33        }
34
35        values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less));
36        percentiles
37            .iter()
38            .map(|&p| {
39                let idx = ((p.clamp(0.0, 1.0)) * ((values.len() - 1) as f64)).round() as usize;
40                values.get(idx).copied().unwrap_or(0.0)
41            })
42            .collect()
43    }
44}