Skip to main content

correlated/
correlated.rs

1use kobo_db_tools::Parser;
2use rusqlite::{Connection, OpenFlags};
3use std::env;
4use std::error::Error;
5
6fn main() -> Result<(), Box<dyn Error>> {
7    let db_path = env::args().nth(1).or_else(|| env::var("KOBO_DB_PATH").ok());
8    let db_path = db_path.ok_or_else(|| {
9        "Usage: cargo run --example correlated -- /path/to/KoboReader.sqlite (or set KOBO_DB_PATH)"
10            .to_string()
11    })?;
12
13    let uri = format!("file:{}?immutable=1", db_path);
14    let conn = Connection::open_with_flags(
15        uri,
16        OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_URI,
17    )?;
18    let analysis = Parser::parse_correlated(&conn)?;
19
20    println!("correlated sessions: {}", analysis.sessions.len());
21    if let Some(session) = analysis.sessions.first() {
22        println!(
23            "sample session: {} -> {:?}",
24            session.session.time_start.to_rfc3339(),
25            session.session.time_end.map(|end| end.to_rfc3339())
26        );
27        println!("  dictionary events: {}", session.dictionary.len());
28        println!(
29            "  brightness events: {}",
30            session.brightness.len() + session.natural_light.len()
31        );
32        println!("  app events: {}", session.app_events.len());
33    }
34
35    println!(
36        "orphans: dict {} brightness {} natural {} app {}",
37        analysis.orphans.dictionary.len(),
38        analysis.orphans.brightness.len(),
39        analysis.orphans.natural_light.len(),
40        analysis.orphans.app_events.len()
41    );
42
43    println!("charge cycles: {}", analysis.cycles.len());
44    for (index, cycle) in analysis.cycles.iter().take(3).enumerate() {
45        let end = cycle
46            .end
47            .map(|ts| ts.to_rfc3339())
48            .unwrap_or_else(|| "open".to_string());
49        println!(
50            "cycle {}: {} -> {} | sessions {} | app starts {}",
51            index + 1,
52            cycle.start.to_rfc3339(),
53            end,
54            cycle.sessions.len(),
55            cycle.metrics.app_starts
56        );
57    }
58
59    if !analysis.app_start_counts_by_day.is_empty() {
60        let mut days: Vec<_> = analysis.app_start_counts_by_day.iter().collect();
61        days.sort_by_key(|(day, _)| **day);
62        println!("app starts by day (first 5):");
63        for (day, count) in days.into_iter().take(5) {
64            println!("  {}: {}", day, count);
65        }
66    }
67
68    Ok(())
69}