Add weekly quota tracking with rolling 7-day window#183
Add weekly quota tracking with rolling 7-day window#183jonathanpberger wants to merge 1 commit intoMaciek-roboblog:mainfrom
Conversation
This adds support for tracking weekly token usage against Claude's rolling 7-day quota limits. The feature includes: - WeeklyUsage model for quota tracking (tokens, cost, burn rate) - Weekly token/cost limits per plan (Pro, Max5, Max20, Custom) - Rolling 7-day aggregation separate from calendar week view - New --view weekly CLI option for weekly statistics - Weekly quota panel with progress bar and projections - Per-model token breakdown for the rolling week The weekly quota uses a rolling window (matching Claude's actual quota system) rather than fixed calendar weeks. This helps users understand their quota consumption and avoid hitting limits. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR adds comprehensive weekly view support to Claude Monitor. A new Changes
Sequence DiagramsequenceDiagram
participant CLI as CLI (main.py)
participant Settings as Settings
participant Aggregator as Data Aggregator
participant Plans as Plan Limits
participant UIDisplay as UI Display (table_views)
participant Console as Console Output
CLI->>Settings: Parse view_mode="weekly"
Settings-->>CLI: Validate "weekly" mode
CLI->>Aggregator: Call aggregate_rolling_week(entries, token_limit, cost_limit)
Aggregator->>Aggregator: Compute rolling 7-day window from UTC now
Aggregator->>Aggregator: Aggregate tokens/costs per model
Aggregator->>Aggregator: Calculate elapsed days & burn rate
Aggregator-->>CLI: Return WeeklyUsage object
CLI->>Plans: Fetch get_weekly_token_limit() & get_weekly_cost_limit()
Plans-->>CLI: Return weekly limits for plan
CLI->>UIDisplay: Call display_aggregated_view(weekly_usage=WeeklyUsage, view_mode="weekly")
UIDisplay->>UIDisplay: create_weekly_quota_panel(weekly_usage)
UIDisplay->>UIDisplay: create_weekly_table(weekly_data)
UIDisplay->>Console: Print quota panel with progress & metrics
UIDisplay->>Console: Print weekly table with per-model breakdown
UIDisplay-->>CLI: Display complete
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/claude_monitor/data/aggregator.py`:
- Around line 260-267: The weekly filter currently includes entries with
timestamp == week_start, which keeps items that should have expired; in the
aggregation logic that computes weekly_entries (using variables now, week_start,
week_end and iterating entries with e.timestamp), change the boundary check to
exclude the exact seven-days-prior moment (use a strict greater-than for the
start: e.timestamp > week_start and keep the end condition as <= week_end) so
entries exactly at week_start are not counted.
In `@src/claude_monitor/ui/table_views.py`:
- Around line 348-361: The "quota exceeded" branch is unreachable because the
code checks `pct >= 80` before `pct >= 100` (and `usage_percentage` is capped at
100), so change the conditional order or logic: check the `pct >= 100` case
first (or use `pct > 100` if you remove the cap) before the `pct >= 80` branch
so the "🚨 Weekly quota exceeded" Text in table_views.py is reachable; update
the conditionals involving the `pct` variable accordingly.
🧹 Nitpick comments (1)
src/claude_monitor/cli/main.py (1)
404-425: Avoid double-loading usage entries in weekly view.
Weekly mode loads entries inaggregator.aggregate()and again viaload_usage_entries. Consider loading once and reusing for bothaggregate_weeklyandaggregate_rolling_weekto reduce I/O and keep timestamp normalization consistent.♻️ Possible refactor
- aggregated_data = aggregator.aggregate() + if view_mode == "weekly": + from claude_monitor.data.reader import load_usage_entries + + entries, _ = load_usage_entries(data_path=str(data_path)) + if entries: + for entry in entries: + if entry.timestamp.tzinfo is None: + entry.timestamp = aggregator.timezone_handler.ensure_timezone( + entry.timestamp + ) + aggregated_data = aggregator.aggregate_weekly(entries) + else: + aggregated_data = [] + else: + aggregated_data = aggregator.aggregate()
| now = datetime.now(timezone.utc) | ||
| week_start = now - timedelta(days=7) | ||
| week_end = now | ||
|
|
||
| # Filter entries to last 7 days | ||
| weekly_entries = [ | ||
| e for e in entries if week_start <= e.timestamp <= week_end | ||
| ] |
There was a problem hiding this comment.
Rolling‑window boundary includes entries that should have expired.
The PR objective says usage from exactly seven days prior should expire, but the filter includes timestamp == week_start. This will overcount at the cutoff.
🔧 Proposed fix
- weekly_entries = [
- e for e in entries if week_start <= e.timestamp <= week_end
- ]
+ weekly_entries = [
+ e for e in entries if week_start < e.timestamp <= week_end
+ ]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| now = datetime.now(timezone.utc) | |
| week_start = now - timedelta(days=7) | |
| week_end = now | |
| # Filter entries to last 7 days | |
| weekly_entries = [ | |
| e for e in entries if week_start <= e.timestamp <= week_end | |
| ] | |
| now = datetime.now(timezone.utc) | |
| week_start = now - timedelta(days=7) | |
| week_end = now | |
| # Filter entries to last 7 days | |
| weekly_entries = [ | |
| e for e in entries if week_start < e.timestamp <= week_end | |
| ] |
🤖 Prompt for AI Agents
In `@src/claude_monitor/data/aggregator.py` around lines 260 - 267, The weekly
filter currently includes entries with timestamp == week_start, which keeps
items that should have expired; in the aggregation logic that computes
weekly_entries (using variables now, week_start, week_end and iterating entries
with e.timestamp), change the boundary check to exclude the exact
seven-days-prior moment (use a strict greater-than for the start: e.timestamp >
week_start and keep the end condition as <= week_end) so entries exactly at
week_start are not counted.
| if pct >= 80: | ||
| lines.append( | ||
| Text( | ||
| "⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.", | ||
| style="yellow", | ||
| ) | ||
| ) | ||
| elif pct >= 100: | ||
| lines.append( | ||
| Text( | ||
| "🚨 Weekly quota exceeded - expect rate limiting", | ||
| style="red bold", | ||
| ) | ||
| ) |
There was a problem hiding this comment.
“Quota exceeded” warning never triggers.
usage_percentage is capped at 100 and the pct >= 80 branch runs first, so the exceeded message is unreachable even when over the limit.
🔧 Proposed fix
- if pct >= 80:
- lines.append(
- Text(
- "⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.",
- style="yellow",
- )
- )
- elif pct >= 100:
- lines.append(
- Text(
- "🚨 Weekly quota exceeded - expect rate limiting",
- style="red bold",
- )
- )
+ if weekly_usage.token_limit > 0 and weekly_usage.tokens_used >= weekly_usage.token_limit:
+ lines.append(
+ Text(
+ "🚨 Weekly quota exceeded - expect rate limiting",
+ style="red bold",
+ )
+ )
+ elif pct >= 80:
+ lines.append(
+ Text(
+ "⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.",
+ style="yellow",
+ )
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if pct >= 80: | |
| lines.append( | |
| Text( | |
| "⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.", | |
| style="yellow", | |
| ) | |
| ) | |
| elif pct >= 100: | |
| lines.append( | |
| Text( | |
| "🚨 Weekly quota exceeded - expect rate limiting", | |
| style="red bold", | |
| ) | |
| ) | |
| if weekly_usage.token_limit > 0 and weekly_usage.tokens_used >= weekly_usage.token_limit: | |
| lines.append( | |
| Text( | |
| "🚨 Weekly quota exceeded - expect rate limiting", | |
| style="red bold", | |
| ) | |
| ) | |
| elif pct >= 80: | |
| lines.append( | |
| Text( | |
| "⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.", | |
| style="yellow", | |
| ) | |
| ) |
🤖 Prompt for AI Agents
In `@src/claude_monitor/ui/table_views.py` around lines 348 - 361, The "quota
exceeded" branch is unreachable because the code checks `pct >= 80` before `pct
>= 100` (and `usage_percentage` is capped at 100), so change the conditional
order or logic: check the `pct >= 100` case first (or use `pct > 100` if you
remove the cap) before the `pct >= 80` branch so the "🚨 Weekly quota exceeded"
Text in table_views.py is reachable; update the conditionals involving the `pct`
variable accordingly.
Summary
This PR adds support for tracking weekly token usage against Claude's rolling 7-day quota limits. This helps users understand their quota consumption and avoid hitting limits.
Features
--view weeklyCLI option - Shows weekly statistics with quota panelUsage
Why Rolling 7-Day?
Claude's weekly quota uses a rolling window where usage from exactly 7 days ago "expires," making room for new capacity. This is different from calendar weeks - there's no single "reset day."
This implementation tracks the rolling 7-day window to accurately represent your quota consumption.
Test plan
claude-monitor --view weekly --plan max20and verify quota panel displays🤖 Generated with Claude Code
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.