diff --git a/web/src/routes/admin.rs b/web/src/routes/admin.rs index c3297dd..ea46732 100644 --- a/web/src/routes/admin.rs +++ b/web/src/routes/admin.rs @@ -133,6 +133,38 @@ pub async fn bot_data(cookies: &CookieJar<'_>, pool: &State>) -> Jso .await .unwrap(); + let history = sqlx::query_as_unchecked!( + TimeFrame, + "SELECT + FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 86400) * 86400) AS `time_key`, + COUNT(1) AS `count` + FROM stat + WHERE + `utc_time` > DATE_SUB(NOW(), INTERVAL 31 DAY) AND + `type` = 'reminder_sent' + GROUP BY `time_key` + ORDER BY `time_key`" + ) + .fetch_all(pool.inner()) + .await + .unwrap(); + + let history_failed = sqlx::query_as_unchecked!( + TimeFrame, + "SELECT + FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 86400) * 86400) AS `time_key`, + COUNT(1) AS `count` + FROM stat + WHERE + `utc_time` > DATE_SUB(NOW(), INTERVAL 31 DAY) AND + `type` = 'reminder_failed' + GROUP BY `time_key` + ORDER BY `time_key`" + ) + .fetch_all(pool.inner()) + .await + .unwrap(); + let interval_count = sqlx::query!( "SELECT COUNT(1) AS count FROM reminders @@ -167,6 +199,10 @@ pub async fn bot_data(cookies: &CookieJar<'_>, pool: &State>) -> Jso "once": schedule_once_long, "interval": schedule_interval_long, }, + "historyLong": { + "sent": history, + "failed": history_failed, + }, "count": { "reminders": reminder_count.count, "intervals": interval_count.count, diff --git a/web/static/js/admin.js b/web/static/js/admin.js index 000d23d..e6ee1ec 100644 --- a/web/static/js/admin.js +++ b/web/static/js/admin.js @@ -6,6 +6,21 @@ document.addEventListener("DOMContentLoaded", () => { document.querySelector("#reminders").textContent = data.count.reminders; document.querySelector("#intervals").textContent = data.count.intervals; + let historySent = data.historyLong.sent.reduce( + (iv, frame) => iv + frame.count, + 0 + ); + let historyFailed = data.historyLong.failed.reduce( + (iv, frame) => iv + frame.count, + 0 + ); + + document.querySelector("#historySent").textContent = historySent; + document.querySelector("#historyFailed").textContent = historyFailed; + document.querySelector("#failRate").textContent = `${ + (historyFailed / (historySent + historyFailed)) * 100 + }%`; + new Chart(document.getElementById("schedule"), { type: "bar", data: { @@ -77,5 +92,40 @@ document.addEventListener("DOMContentLoaded", () => { }, }, }); + + new Chart(document.getElementById("historyLong"), { + type: "bar", + data: { + labels: [...data.historyLong.sent, ...data.historyLong.failed].map( + (row) => luxon.DateTime.fromISO(row.time_key) + ), + datasets: [ + { + label: "Success", + data: data.historyLong.sent.map((row) => row.count), + }, + { + label: "Fail", + data: data.historyLong.failed.map((row) => row.count), + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + x: { + stacked: true, + type: "time", + time: { + unit: "day", + }, + }, + y: { + stacked: true, + }, + }, + }, + }); }); }); diff --git a/web/templates/admin_dashboard.html.tera b/web/templates/admin_dashboard.html.tera index 84455b5..e09d3ab 100644 --- a/web/templates/admin_dashboard.html.tera +++ b/web/templates/admin_dashboard.html.tera @@ -60,6 +60,25 @@ +
+
+

Last 31 days (success)

+

?

+
+
+

Last 31 days (failed)

+

?

+
+
+

Last 31 days (failure rate)

+

?

+
+
+
+
+ +
+