diff --git a/README.md b/README.md index 899ef2b..e7bb631 100644 --- a/README.md +++ b/README.md @@ -204,20 +204,22 @@ Add `V=1` for verbose output: `make check V=1` cli-tools/ ├── src/ │ └── copilot_usage/ -│ ├── cli.py # Click commands + interactive loop + watchdog auto-refresh -│ ├── models.py # Pydantic data models -│ ├── parser.py # events.jsonl parsing -│ ├── pricing.py # Model cost multipliers -│ ├── logging_config.py # Loguru configuration -│ └── report.py # Rich terminal output +│ ├── cli.py # Click commands + interactive loop + watchdog auto-refresh +│ ├── models.py # Pydantic data models +│ ├── parser.py # events.jsonl parsing +│ ├── pricing.py # Model cost multipliers +│ ├── logging_config.py # Loguru configuration +│ ├── report.py # Rich terminal output +│ └── docs/ # Developer docs +│ ├── architecture.md +│ ├── changelog.md +│ ├── implementation.md +│ └── plan.md ├── tests/ -│ ├── copilot_usage/ # Unit tests -│ └── e2e/ # End-to-end tests with fixtures +│ ├── copilot_usage/ # Unit tests +│ └── e2e/ # End-to-end tests with fixtures ├── docs/ -│ ├── plan.md -│ ├── architecture.md -│ ├── changelog.md -│ └── implementation.md +│ └── changelog.md # Top-level changelog ├── Makefile └── pyproject.toml ``` diff --git a/src/copilot_usage/cli.py b/src/copilot_usage/cli.py index 19bdf97..329af2f 100644 --- a/src/copilot_usage/cli.py +++ b/src/copilot_usage/cli.py @@ -388,20 +388,11 @@ def cost( try: sessions = get_all_sessions(path) - # Filter by date range - since_aware = _ensure_aware(since) - until_aware = _ensure_aware(until) - filtered = sessions - if since_aware is not None or until_aware is not None: - filtered = [ - s - for s in sessions - if s.start_time is not None - and (since_aware is None or s.start_time >= since_aware) - and (until_aware is None or s.start_time <= until_aware) - ] - - render_cost_view(filtered) + render_cost_view( + sessions, + since=_ensure_aware(since), + until=_ensure_aware(until), + ) except Exception as exc: # noqa: BLE001 click.echo(f"Error: {exc}", err=True) sys.exit(1) diff --git a/src/copilot_usage/report.py b/src/copilot_usage/report.py index 7f01f18..f7bd94e 100644 --- a/src/copilot_usage/report.py +++ b/src/copilot_usage/report.py @@ -848,14 +848,18 @@ def render_full_summary( def render_cost_view( sessions: list[SessionSummary], *, + since: datetime | None = None, + until: datetime | None = None, target_console: Console | None = None, ) -> None: """Render per-session, per-model cost breakdown. + Filters sessions by date range when *since* and/or *until* are given. For active sessions, appends a "↳ Since last shutdown" row with N/A for premium and the active model calls / output tokens. """ console = target_console or Console() + sessions = _filter_sessions(sessions, since, until) if not sessions: console.print("[yellow]No sessions found.[/yellow]") diff --git a/tests/copilot_usage/test_report.py b/tests/copilot_usage/test_report.py index aeb3f7e..a26dad5 100644 --- a/tests/copilot_usage/test_report.py +++ b/tests/copilot_usage/test_report.py @@ -1134,11 +1134,15 @@ def test_no_historical_data(self) -> None: # --------------------------------------------------------------------------- -def _capture_cost_view(sessions: list[SessionSummary]) -> str: +def _capture_cost_view( + sessions: list[SessionSummary], + since: datetime | None = None, + until: datetime | None = None, +) -> str: """Capture Rich output from render_cost_view to a plain string.""" buf = StringIO() console = Console(file=buf, force_terminal=True, width=120) - render_cost_view(sessions, target_console=console) + render_cost_view(sessions, since=since, until=until, target_console=console) return buf.getvalue() @@ -1321,3 +1325,15 @@ def test_historical_model_table_present(self) -> None: output = _capture_full_summary([session]) assert "Per-Model Breakdown" in output assert "claude-sonnet-4" in output + + def test_since_until_filters_sessions(self) -> None: + """render_cost_view since/until params exclude sessions outside range.""" + early = _make_session( + start_time=datetime(2025, 1, 10, tzinfo=UTC), name="Early" + ) + late = _make_session(start_time=datetime(2025, 1, 20, tzinfo=UTC), name="Late") + since = datetime(2025, 1, 15, tzinfo=UTC) + until = datetime(2025, 1, 25, tzinfo=UTC) + output = _capture_cost_view([early, late], since=since, until=until) + assert "Late" in output + assert "Early" not in output