From 4410950c761970d640af00f245fdba859f8795b0 Mon Sep 17 00:00:00 2001 From: gennyble Date: Fri, 4 Apr 2025 01:45:07 -0500 Subject: rip out module system --- corgi_stats/src/main.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 corgi_stats/src/main.rs (limited to 'corgi_stats/src/main.rs') diff --git a/corgi_stats/src/main.rs b/corgi_stats/src/main.rs new file mode 100644 index 0000000..1097685 --- /dev/null +++ b/corgi_stats/src/main.rs @@ -0,0 +1,95 @@ +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); +} -- cgit 1.4.1-3-g733a5