use rusqlite::{Connection, params}; use std::time::Instant; use time::{Duration, OffsetDateTime}; // Thank you, cat, for optimizing my query const TOP_TEN_ALL_TIME: &str = "SELECT reqs.cnt, agents.agent FROM agents JOIN ( SELECT count(id) as cnt, agent_id FROM requests GROUP BY agent_id ) reqs ON reqs.agent_id=agents.id ORDER BY reqs.cnt DESC LIMIT 10;"; fn main() { let db_path = std::env::var("CORGI_STATS_DB").ok(); let db = if let Some(path) = db_path { if let Ok(db) = Connection::open(path) { db } else { error_and_die(500, "failed to open database"); } } else { error_and_die(500, "database key not set"); }; let mut body = String::new(); let now = OffsetDateTime::now_utc(); let fifteen_ago = now - Duration::minutes(15); let start = Instant::now(); let query = "SELECT count(requests.id) AS request_count, agents.agent FROM requests \ INNER JOIN agents ON requests.agent_id = agents.id \ WHERE requests.timestamp > ?1 \ GROUP BY requests.agent_id;"; let mut prepared = db.prepare(query).unwrap(); let mut agents: Vec<(usize, String)> = prepared .query_map(params![fifteen_ago], |row| Ok((row.get(0)?, row.get(1)?))) .unwrap() .map(|r| r.unwrap()) .collect(); agents.sort_by(|a, b| a.0.cmp(&b.0).reverse()); let mut prepared = db.prepare(TOP_TEN_ALL_TIME).unwrap(); let highest_five: Vec<(usize, String)> = prepared .query_map(params![], |row| Ok((row.get(0)?, row.get(1)?))) .unwrap() .map(|r| r.unwrap()) .collect(); let sum_highest_five = highest_five.iter().fold(0, |acc, (count, _)| acc + count); body.push_str("

In the last fifteen minutes:

");
	body.push_str("total | req/m | agent\n");
	for (count, agent) in &agents {
		body.push_str(&format!(
			"{count:<5} | {:<5.1} | {agent}\n",
			*count as f32 / 15.0
		));
	}
	body.push_str("

"); body.push_str("

Highest ten all time requesters:

");
	body.push_str("  total  | %of10 | agent\n");
	for (count, agent) in highest_five {
		body.push_str(&format!(
			" {count:<7} | {:<5.1} | {agent}\n",
			(count as f32 / sum_highest_five as f32) * 100.0
		));
	}
	body.push_str("

"); body.push_str(&format!("

took {}ms

", start.elapsed().as_millis())); println!("Status: 200\nContent-Type: text/html\n"); println!("{body}"); } fn error_and_die>(status: u16, msg: S) -> ! { println!("Status: {status}"); println!("Content-Type: text/html\n"); println!(""); println!("\t{status}"); println!("\t"); println!("\t\t

{status}

"); println!("\t\t
"); println!("\t\t

{}

", msg.into()); println!("\t\n"); std::process::exit(0); }