guzzles data
allow specifying period
bendn 2024-03-02
parent fa26357 · commit 85f9687
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs88
-rw-r--r--x.plot2
3 files changed, 83 insertions, 8 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 0b97bae..d1df327 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,3 +16,4 @@ poise = { git = "https://github.com/serenity-rs/poise" }
anyhow = "1.0.80"
tokio = { version = "1.36.0", features = ["rt-multi-thread"] }
emoji = { git = "https://github.com/apricot-conservation-project/emoji", version = "0.1.0" }
+chrono = "0.4.34"
diff --git a/src/main.rs b/src/main.rs
index c92aa9b..4f4342d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,13 +1,89 @@
use anyhow::Result;
+use chrono::{DateTime, Datelike, TimeZone, Utc};
use emoji::named::*;
use poise::serenity_prelude::*;
use serenity::futures::StreamExt;
use std::fs::read_to_string;
use std::io::Write;
+use std::ops::{Deref, Sub};
type Context<'a> = poise::Context<'a, (), anyhow::Error>;
+
+#[derive(poise::ChoiceParameter, Default, Copy, Clone, Debug)]
+enum Period {
+ #[name = "all time"]
+ #[default]
+ AllTime,
+ #[name = "year"]
+ LastYear,
+ #[name = "month"]
+ LastMonth,
+ #[name = "week"]
+ LastWeek,
+}
+
+struct Days(usize);
+
+impl Deref for Days {
+ type Target = usize;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl Sub<usize> for Days {
+ type Output = Days;
+
+ fn sub(self, rhs: usize) -> Self::Output {
+ Self(self.0 - rhs)
+ }
+}
+
+impl Days {
+ fn t(self) -> DateTime<Utc> {
+ Utc.timestamp_millis_opt(((*self as i64 * (60 * 60 * 24) as i64) + 1420070400) * 1000)
+ .single()
+ .unwrap()
+ }
+
+ fn now() -> Self {
+ Self::from(Utc::now().timestamp() as usize)
+ }
+}
+
+impl From<usize> for Days {
+ fn from(value: usize) -> Self {
+ Self((value - 1420070400) / (60 * 60 * 24))
+ }
+}
+
+impl Period {
+ fn from(self) -> Days {
+ match self {
+ Self::AllTime => Days(0),
+ Self::LastYear => Days::now() - 365,
+ Self::LastMonth => Days::now() - 30,
+ Self::LastWeek => Days::now() - 7,
+ }
+ }
+
+ fn tic(self, t: Days) -> bool {
+ match self {
+ Self::LastYear | Self::AllTime => t.t().day() == 1,
+ Self::LastMonth => t.t().day() % 7 == 0,
+ Self::LastWeek => true,
+ }
+ }
+}
+
#[poise::command(slash_command)]
-pub async fn users(c: Context<'_>) -> Result<()> {
+/// graph of users over time
+pub async fn users(
+ c: Context<'_>,
+ #[description = "time period to graph (defaults to all time)"] period: Option<Period>,
+) -> Result<()> {
c.defer().await?;
+ let p = period.unwrap_or_default();
let g = {
let Some(g) = c.guild() else {
_ = c.reply(format!("{CANCEL} need guild"));
@@ -21,23 +97,21 @@ pub async fn users(c: Context<'_>) -> Result<()> {
let mut max = 0;
while let Some(x) = s.next().await {
let x = x?.joined_at.unwrap();
- let d = (x.timestamp() as usize - 1420070400) / (60 * 60 * 24);
+ let d = *Days::from(x.timestamp() as usize);
min = min.min(d);
max = max.max(d);
data[d] += 1;
}
+ let min = min.max(*p.from());
let mut f = std::fs::File::create("1.dat").unwrap();
let mut sum = 0;
for (i, &d) in data[min..max].iter().enumerate() {
sum += d;
- let t =
- Timestamp::from_unix_timestamp(((i + min) as i64 * (60 * 60 * 24) as i64) + 1420070400)
- .unwrap();
writeln!(
&mut f,
r"{},{sum}",
- if t.format("%d").to_string() == "01" {
- t.format("%m/%d/%y").to_string()
+ if p.tic(Days(i + min)) {
+ Days(i + min).t().format("%m/%d/%y").to_string()
} else {
"".to_string()
}
diff --git a/x.plot b/x.plot
index bd548e0..68551d8 100644
--- a/x.plot
+++ b/x.plot
@@ -23,7 +23,7 @@ set ytics 50
set mytics 2
set style data histogram
set style histogram cluster gap 1
-set ylabel "user count"
+set ylabel "change in user count in period"
set xlabel "days"
set auto x
set yrange [0:*]