Stats: activity heatmap as a rolling window of the last 14 days

One row per calendar day (today at the bottom) instead of weekdays
aggregated over the selected range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-09 06:22:36 +01:00
1 parent 01c1484033
commit d14c609576
5 files changed
+46 -23

No files matched your search

+7 -5
View File
@@ -433,7 +433,7 @@ impl Store {
|r| agg_row(r, 0),
)?;
let grouped = |key_sql: &str, key_name: &str| -> Result<Vec<Value>> {
let grouped_in = |from: i64, key_sql: &str, key_name: &str| -> Result<Vec<Value>> {
let mut stmt = conn.prepare(&format!(
"SELECT {key_sql}, {AGG} FROM usage WHERE ts >= ?1 AND ts < ?2 AND ?3 = ?3
AND (?4 IS NULL OR user_id = ?4) GROUP BY 1 ORDER BY 1"
@@ -451,6 +451,7 @@ impl Store {
.collect::<rusqlite::Result<Vec<_>>>()?;
Ok(rows)
};
let grouped = |key_sql: &str, key_name: &str| grouped_in(from, key_sql, key_name);
let day_sql = "strftime('%Y-%m-%d', (ts + ?3) / 1000, 'unixepoch')";
let by_day = grouped(day_sql, "day")?;
@@ -461,10 +462,11 @@ impl Store {
"COALESCE((SELECT username FROM users WHERE users.id = usage.user_id), '(none)')",
"user",
)?;
// Heatmap: weekday (0 = Sunday) x hour of day.
let heatmap = grouped(
"CAST(strftime('%w', (ts + ?3) / 1000, 'unixepoch') AS INTEGER) * 24 + \
CAST(strftime('%H', (ts + ?3) / 1000, 'unixepoch') AS INTEGER)",
// Heatmap: rolling window of the last 14 days (local date + hour),
// independent of the selected range.
let heatmap = grouped_in(
to - 15 * 86_400_000,
"strftime('%Y-%m-%d %H', (ts + ?3) / 1000, 'unixepoch')",
"slot",
)?;
let by_day_model = {