feat(stats): --since, --json, and --graph for tsk stats#4
Merged
Conversation
Restrict completion-derived metrics (Done, Completion, Streak, TopTags)
to tasks completed within the given window. Whole-store counts (Total,
Undone, Overdue, Today) are unaffected.
Accepts friendly suffixes (7d, 2w, 1m, 1y) and bare Go durations (72h,
1h30m). A local parseDurationLocal helper keeps this PR independent of
any sibling refactor PR; it can be promoted to internal/util later.
Output prints one extra line ('since: <dur> ago') near the top when set.
Emit a stable JSON document via encoding/json.MarshalIndent. Schema:
total, done, undone, overdue, today, completion, streak, since_seconds,
top_tags ({tag, count}), completion_history ({date, count}).
completion_history is always populated as a 30-bucket trailing window
(oldest-first) so the schema is stable regardless of any visualization
flag. The window is independent of --since on purpose: callers want
'completions in the last 30 days' even when their stats are scoped to
a different window.
JSON output is machine-only — no human prelude, no trailing prose.
Append a single-line sparkline below the human summary using the standard 9-rune alphabet (space + 8 increasing block heights). Each bucket is one day, oldest on the left, today on the right. Plain runes only \u2014 no ANSI escapes \u2014 so it works under NO_COLOR. The sparkline is always the trailing 30 days, intentionally independent of --since, so the visualization stays comparable across windows. The JSON contract already exposed completion_history in the previous commit; this commit just adds the rendering and the --graph flag. --graph and --json are independent. When both are set, --json wins (graph is suppressed) so callers get exactly one machine-readable doc.
Document the three new stats flags (--since, --graph, --json) under a new 'Stats and history' subsection in Usage. Spells out the JSON schema verbatim so scripts and dashboards have a written contract, calls out that --graph + --json is JSON-only, and notes that completion_history is always 30 buckets independent of --since.
Six new tests: - TestStatsSinceFiltersDoneAndStreak: 5 tasks with backdated completed timestamps (today/-1/-3/-10/-45 days) plus one open. Asserts whole-store total/undone unchanged, --since 7d narrows done to 3 and streak to 2. - TestStatsSinceRejectsBogus: 'banana' returns ExitCode 2. - TestStatsGraphRendersSparkline: counts the runes (must be exactly 30), validates each is in the sparkline alphabet, and asserts the right edge (today) is non-empty when there's a same-day completion. - TestStatsJSONStableSchema: parses the JSON, asserts every required key is present and correctly typed, since_seconds matches 30d, top_tags is sorted desc by count, completion_history has 30 oldest-first buckets. - TestStatsJSONWithoutSince: when --since is unset, since_seconds is 0. Adds a writeRawTasks helper that writes the raw .tsk.md so tests can control the Completed timestamps the CLI 'done' command would otherwise stamp at time.Now().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three additions to
tsk stats:--since <dur>restricts completion-derived metrics (Done, Completion, Streak, TopTags) to a window. Whole-store counts (Total, Undone, Overdue, Today) are unaffected.--graphappends a 30-day completion sparkline below the human summary.--jsonemits a stable JSON document for scripts and dashboards.--graphand--jsonare independent. With both set,--jsonwins (graph suppressed) so callers always get exactly one machine-readable doc.--jsonschema (stable contract){ "total": int, "done": int, "undone": int, "overdue": int, "today": int, "completion": float64, "streak": int, "since_seconds": int, "top_tags": [{"tag": string, "count": int}, ...], "completion_history": [{"date": "YYYY-MM-DD", "count": int}, ...] }completion_historyis always populated (30 oldest-first buckets) regardless of whether--graphwas passed, so the schema is stable.Sparkline notes
▁▂▃▄▅▆▇█plus space for zero. Ceiling-division mapping so any nonzero count gets at least the smallest visible block.NO_COLOR.--since, so the visualization stays comparable across filters.30d completions: ▁▂▁▃▄▂▁ ... █(oldest left, today right).--sincenotesAccepts friendly suffixes
7d,2w,1m,1yplus bare Go durations (72h,1h30m). Months = 30 days, years = 365 days for "completions in the last N" semantics.A local
parseDurationLocalhelper lives instats.goto keep this PR independent of any sibling refactor; it can be promoted tointernal/utillater.Scope
Only three files touched:
internal/commands/stats.go,internal/commands/commands_test.go,README.md. No new external deps.Tests
Five new cases covering the new surface:
TestStatsSinceFiltersDoneAndStreak— 5 backdated tasks; asserts whole-store counts unchanged,--since 7dnarrows Done to 3 and streak to 2.TestStatsSinceRejectsBogus— exit code 2 for invalid duration.TestStatsGraphRendersSparkline— exactly 30 runes, all in the alphabet, today bucket non-empty when there's a same-day completion.TestStatsJSONStableSchema— parses output, validates required keys + types,since_secondsmatches30d,top_tagssorted desc by count,completion_historyis 30 oldest-first buckets.TestStatsJSONWithoutSince—since_secondsis 0 when the flag is unset.Plus a
writeRawTaskshelper that writes.tsk.mddirectly so tests can stamp arbitraryCompletedtimestamps (the CLIdonecommand stampstime.Now(), which can't be backdated).go vet ./...,gofmt -l .,go test ./... -count=1— all clean.Diff size
~574 net additions (well-tested side; ~240 of those are test code; ~290 stats code; ~43 README). Slightly over the soft ~500-line target because the test surface is what was asked for: 4 substantive cases plus a helper, exercising every new flag end-to-end.