kobo_db_tools/statistics/imp/
sessions.rs1use 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}