Skip to content

Add an option to list daily summary#132

Merged
matin merged 2 commits intomatin:mainfrom
vactomas:feature/daily-summary
Jan 8, 2026
Merged

Add an option to list daily summary#132
matin merged 2 commits intomatin:mainfrom
vactomas:feature/daily-summary

Conversation

@vactomas
Copy link
Copy Markdown
Contributor

@vactomas vactomas commented Jun 20, 2025

Hi, matin,
I have added an option to pull daily summary for a user as I believe it is nice to have simple overview available directly.

I wasn't sure if it belongs in data or stats, but given the format of the request, I have chosen the data directory as the best possible place.

Please, let me know if there is anything else that needs to be done.

Summary by CodeRabbit

  • New Features
    • Added DailySummary: a public model providing comprehensive daily health metrics and methods to fetch summaries by date or range; now easily importable from the package root.
  • Tests
    • Added tests to validate fetching a single daily summary and listing summaries over a date range.
  • Documentation
    • Updated README with usage examples and sample DailySummary output.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 20, 2025

Warning

Rate limit exceeded

@matin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 15 minutes and 27 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 1e62a28 and d8187c1.

📒 Files selected for processing (1)
  • src/garth/data/daily_summary.py

Walkthrough

Adds a new Pydantic dataclass DailySummary with a classmethod to fetch daily metrics from the external connect API, exposes it via package exports, adds tests for fetching/listing daily summaries, and documents usage in README.

Changes

Cohort / File(s) Change Summary
New data model
src/garth/data/daily_summary.py
Added DailySummary dataclass (many optional health metric fields) and classmethod `get(day: date
Data package exports
src/garth/data/__init__.py
Imported DailySummary from .daily_summary and added it to __all__.
Top-level package exports
src/garth/__init__.py
Imported DailySummary from .data and added it to package __all__.
Tests
tests/data/test_daily_summary.py
Added test_daily_summary_get and test_daily_summary_list using VCR'd HTTP interactions and an authed_client fixture; assertions on calendar_date, user_profile_id, and list length.
Docs
README.md
Added "Daily Summary" documentation and usage examples showing DailySummary.get(date) and DailySummary.list(...) with sample fields.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DailySummary
    participant httpClient as http.Client
    participant API

    User->>DailySummary: DailySummary.get(day, client?)
    alt no client provided
        DailySummary->>httpClient: instantiate default client
    end
    DailySummary->>httpClient: connectapi("/wellness/dailySummary?calendarDate=day")
    httpClient->>API: GET /wellness/dailySummary?calendarDate=day
    API-->>httpClient: JSON response (camelCase keys)
    httpClient-->>DailySummary: response dict
    DailySummary->>DailySummary: camel_to_snake_dict -> validate -> instantiate
    DailySummary-->>User: DailySummary instance or None
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title refers to a real aspect of the changes (adding daily summary functionality), but significantly understates the scope by emphasizing only the 'list' option when the PR actually introduces comprehensive daily summary support including both single-date retrieval and list functionality, a new data model, tests, and documentation. Consider clarifying whether the title should reflect 'Add daily summary data model' or 'Add daily summary functionality' to better represent that this introduces both get and list capabilities, not just the list option.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1bdee8 and 6833870.

📒 Files selected for processing (3)
  • src/garth/__init__.py (2 hunks)
  • src/garth/data/__init__.py (2 hunks)
  • src/garth/data/daily_summary.py (1 hunks)
🧰 Additional context used
🪛 Pylint (3.3.7)
src/garth/data/daily_summary.py

[refactor] 12-12: Too many instance attributes (27/7)

(R0902)

🔇 Additional comments (4)
src/garth/data/__init__.py (1)

6-6: LGTM! Proper API exposure.

The changes correctly add DailySummary to the module's public API following established patterns for other data classes.

Also applies to: 19-19

src/garth/__init__.py (1)

1-7: LGTM! Clean package-level integration.

The changes properly integrate DailySummary into the main package API with improved import formatting and correct alphabetical ordering in __all__.

Also applies to: 34-34

src/garth/data/daily_summary.py (2)

1-8: LGTM! Well-organized imports.

Import statements follow Python conventions with proper separation of standard library, third-party, and local imports.


11-40: Data class design is appropriate despite many fields.

While static analysis flags 27 instance attributes as excessive, this is justified for a comprehensive daily health summary data class. All fields represent distinct health metrics that logically belong together, and the optional nature accommodates varying data availability from fitness devices.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/data/test_daily_summary.py (1)

9-27: Consider adding edge case tests and error handling validation.

While the current tests cover the happy path well, consider adding tests for:

  • Invalid date formats
  • Network error scenarios
  • Empty result sets
  • Boundary conditions (e.g., days=0 in list method)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6833870 and bb93746.

📒 Files selected for processing (1)
  • tests/data/test_daily_summary.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`tests/**`: - test functions shouldn't have a return type hint - it's ok to use `assert` instead of `pytest.assume()`

tests/**: - test functions shouldn't have a return type hint

  • it's ok to use assert instead of pytest.assume()
  • tests/data/test_daily_summary.py
🔇 Additional comments (3)
tests/data/test_daily_summary.py (3)

1-6: Imports look good and follow conventions.

The imports are well-organized and appropriate for the test functionality.


19-27: Test implementation looks solid with good coverage.

The test effectively validates the list functionality with appropriate assertions for both count and date verification. The max_workers=1 parameter is a good practice for deterministic testing.


10-16: I’ll inspect the tests for DailySummary.list usage and then check the base Data class where list is likely defined:

#!/usr/bin/env bash
set -e

# Show tests for DailySummary
echo "--- tests/data/test_daily_summary.py ---"
sed -n '1,200p' tests/data/test_daily_summary.py

# Locate the file defining the base Data class
data_file=$(rg -l "class Data" -n src/garth/data)
echo "Data class implemented in: $data_file"

# Show the Data class definition context
echo -e "\n--- Data class definition ---"
rg -n -A5 "class Data" "$data_file"

# Show list() and get() signatures in Data
echo -e "\n--- Data.list() signature ---"
rg -n -C2 "def list" "$data_file"

echo -e "\n--- Data.get() signature ---"
rg -n -C2 "def get" "$data_file"

@vactomas vactomas force-pushed the feature/daily-summary branch from bb93746 to d0aab1f Compare June 20, 2025 16:29
@matin
Copy link
Copy Markdown
Owner

matin commented Jun 22, 2025

@vactomas this is a good feature to add. ci is failing. could you check the tests?

@vactomas
Copy link
Copy Markdown
Contributor Author

vactomas commented Jun 22, 2025

I see the problem, I have not generated VCR cassettes. I have a question, do I just run the tests against my account or is there some other way?

I have locally temporarily set vcr record_mode to once, but it fails with 401 not authorized error on the tests I ran. I presume this is due to the sanitized tokens in conftest.py.

@vactomas vactomas force-pushed the feature/daily-summary branch from d0aab1f to 3d14c9c Compare June 22, 2025 09:54
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/garth/data/daily_summary.py (1)

45-56: Address the unresolved issues from previous review.

The issues identified in the previous review regarding error handling and input validation remain unaddressed. As mentioned in the past review comments, this method needs:

  1. Input validation for string date format
  2. Proper error handling for API failures
  3. Descriptive error messages instead of bare assertions
🧹 Nitpick comments (1)
README.md (1)

910-910: Fix missing article for better readability.

-Retrieve user's daily summary for given date.
+Retrieve user's daily summary for a given date.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0aab1f and 3d14c9c.

📒 Files selected for processing (5)
  • README.md (1 hunks)
  • src/garth/__init__.py (2 hunks)
  • src/garth/data/__init__.py (2 hunks)
  • src/garth/data/daily_summary.py (1 hunks)
  • tests/data/test_daily_summary.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/garth/init.py
  • src/garth/data/init.py
  • tests/data/test_daily_summary.py
🧰 Additional context used
🪛 LanguageTool
README.md

[uncategorized] ~910-~910: Possible missing article found.
Context: ...mary Retrieve user's daily summary for given date. ```python garth.DailySummary.get...

(AI_HYDRA_LEO_MISSING_A)

🪛 Pylint (3.3.7)
src/garth/data/daily_summary.py

[refactor] 12-12: Too many instance attributes (31/7)

(R0902)

🔇 Additional comments (2)
src/garth/data/daily_summary.py (1)

11-43: Well-structured data class for comprehensive health metrics.

The DailySummary dataclass effectively models the extensive daily health data from Garmin Connect. The use of optional fields with None defaults is appropriate since not all metrics may be available for every day.

README.md (1)

908-1027: Excellent documentation following established patterns.

The new "Daily Summary" section provides clear usage examples and comprehensive sample output that will help users understand the available metrics. The documentation structure is consistent with other sections in the README.

@matin
Copy link
Copy Markdown
Owner

matin commented Jun 22, 2025

you have to log in, save your tokens and set the GARTH_HOME env bar.

to generate the cassettes, this'll let you generate the tests against your account. all sensitive info should be masked, but i recommend double checking to cassettes before committing them

@vactomas
Copy link
Copy Markdown
Contributor Author

I see, thanks. I have some trouble getting it to work, but I'll again later. Hopefully, I can get it to cooperate and get the cassettes ready

@vactomas vactomas force-pushed the feature/daily-summary branch 2 times, most recently from 65d9bd4 to 4cb66bf Compare June 26, 2025 19:18
@vactomas
Copy link
Copy Markdown
Contributor Author

I have managed to get these cassettes to work locally. Hopefully, it will work even in CI

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/garth/data/daily_summary.py (1)

45-56: Duplicate: Error handling and input validation improvements needed.

The error handling and input validation concerns raised in the previous review are still applicable to this implementation. The method would benefit from:

  1. Input validation for string date format
  2. Proper error handling around the API call
  3. Replacing the bare assertion with a descriptive error
🧹 Nitpick comments (1)
README.md (1)

910-910: Fix grammatical issue: missing article.

The phrase "for given date" should include an article.

-Retrieve user's daily summary for given date.
+Retrieve user's daily summary for a given date.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d14c9c and 4cb66bf.

⛔ Files ignored due to path filters (2)
  • tests/data/cassettes/test_daily_summary_get.yaml is excluded by !tests/**/cassettes/**
  • tests/data/cassettes/test_daily_summary_list.yaml is excluded by !tests/**/cassettes/**
📒 Files selected for processing (5)
  • README.md (1 hunks)
  • src/garth/__init__.py (2 hunks)
  • src/garth/data/__init__.py (2 hunks)
  • src/garth/data/daily_summary.py (1 hunks)
  • tests/data/test_daily_summary.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/garth/init.py
  • tests/data/test_daily_summary.py
  • src/garth/data/init.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/garth/data/daily_summary.py (3)
src/garth/utils.py (1)
  • camel_to_snake_dict (17-33)
src/garth/data/_base.py (1)
  • Data (15-47)
src/garth/http.py (2)
  • Client (19-244)
  • connectapi (186-192)
🪛 LanguageTool
README.md

[uncategorized] ~910-~910: Possible missing article found.
Context: ...mary Retrieve user's daily summary for given date. ```python garth.DailySummary.get...

(AI_HYDRA_LEO_MISSING_A)

🪛 Pylint (3.3.7)
src/garth/data/daily_summary.py

[refactor] 12-12: Too many instance attributes (31/7)

(R0902)

🔇 Additional comments (3)
README.md (1)

908-1027: Excellent documentation for the new Daily Summary feature.

The documentation follows the established pattern and provides comprehensive examples with clear sample output. The code examples demonstrate both single date retrieval and date range functionality effectively.

src/garth/data/daily_summary.py (2)

11-43: Well-structured data class for comprehensive health metrics.

The DailySummary class appropriately captures a wide range of daily health metrics with proper type annotations. While Pylint flags the high number of attributes (31), this is reasonable and expected for a comprehensive daily health summary that consolidates various health metrics into a single data structure.


1-8: Clean and appropriate imports.

The imports are well-organized and include all necessary dependencies for the implementation.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (7612229) to head (d8187c1).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #132   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           49        51    +2     
  Lines         2047      2113   +66     
=========================================
+ Hits          2047      2113   +66     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vactomas
Copy link
Copy Markdown
Contributor Author

vactomas commented Jul 5, 2025

I looked at the test coverage issue. There is a problem with the part:

if not daily_summary:
  return None

That said, if we try to pull a summary from time when the account wasn't used or has no data, it will still return non-empty return, but every single key is marked as None essentially.

I believe the best way to solve this is to drop the if condition completely. What are your thoughts @matin? Also, is there anything else that needs to be solved?

@vactomas vactomas force-pushed the feature/daily-summary branch from 4cb66bf to bfec094 Compare September 5, 2025 13:02
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/garth/data/daily_summary.py (1)

57-58: Replace assert with a typed error for clearer failures.

Asserts can be stripped; raise a descriptive exception instead.

Apply this diff:

-        assert isinstance(daily_summary, dict)
+        if not isinstance(daily_summary, dict):
+            raise TypeError(f"Expected dict from API, got {type(daily_summary)!r}")
🧹 Nitpick comments (5)
tests/data/test_daily_summary.py (2)

17-25: Assert ordering explicitly and rename plural for clarity.

Make the order guarantee obvious and improve naming.

Apply this diff:

-from datetime import date
+from datetime import date, timedelta
@@
-    daily_summary = DailySummary.list(
+    daily_summaries = DailySummary.list(
         end, days, client=authed_client, max_workers=1
     )
-    assert len(daily_summary) == days
-    assert daily_summary[0].calendar_date == end
+    assert len(daily_summaries) == days
+    assert [d.calendar_date for d in daily_summaries] == [end, end - timedelta(days=1)]

17-25: Optional: cover the “no content” branch without VCR.

Add a lightweight unit test that monkeypatches Client.connectapi to return None so the get() None-path is exercised.

Example:

def test_daily_summary_get_none(monkeypatch, client: Client):
    monkeypatch.setattr(Client, "connectapi", lambda self, path, method="GET", **kw: None)
    assert DailySummary.get("2025-06-01", client=client) is None
src/garth/data/daily_summary.py (3)

49-51: Build query string robustly and normalize date formatting.

Avoid manual f-string concatenation; ensure consistent ISO formatting and proper encoding.

Apply this diff:

+from urllib.parse import urlencode
@@
-        path = f"/usersummary-service/usersummary/daily/?calendarDate={day}"
+        day_str = day.isoformat() if isinstance(day, date) else str(day)
+        qs = urlencode({"calendarDate": day_str})
+        path = f"/usersummary-service/usersummary/daily/?{qs}"

53-55: Narrow the falsy check to None only.

The API often returns a dict with keys whose values are None (truthy). Keep the 204/None case, don’t drop valid-but-empty days.

Apply this diff:

-        # pragma: no cover
-        if not daily_summary:
-            return None
+        # pragma: no cover
+        if daily_summary is None:
+            # 204 No Content
+            return None

If the endpoint can return {} for no-activity days, confirm desired behavior (return None vs. construct with missing fields).


45-49: Minor: add a short docstring to get() for API/return semantics.

Helps downstream users understand None vs. populated objects.

Example:

    @classmethod
    def get(...):
        """Fetch a single day. Returns DailySummary or None (e.g., 204 No Content)."""
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4cb66bf and bfec094.

⛔ Files ignored due to path filters (2)
  • tests/data/cassettes/test_daily_summary_get.yaml is excluded by !tests/**/cassettes/**
  • tests/data/cassettes/test_daily_summary_list.yaml is excluded by !tests/**/cassettes/**
📒 Files selected for processing (5)
  • README.md (1 hunks)
  • src/garth/__init__.py (2 hunks)
  • src/garth/data/__init__.py (2 hunks)
  • src/garth/data/daily_summary.py (1 hunks)
  • tests/data/test_daily_summary.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/garth/init.py
  • src/garth/data/init.py
🧰 Additional context used
📓 Path-based instructions (1)
tests/**

⚙️ CodeRabbit configuration file

tests/**: - test functions shouldn't have a return type hint

  • it's ok to use assert instead of pytest.assume()

Files:

  • tests/data/test_daily_summary.py
🧬 Code graph analysis (2)
tests/data/test_daily_summary.py (3)
src/garth/data/daily_summary.py (2)
  • DailySummary (12-59)
  • get (46-59)
src/garth/http.py (1)
  • Client (19-244)
tests/conftest.py (3)
  • vcr (79-82)
  • authed_client (65-75)
  • client (21-22)
src/garth/data/daily_summary.py (3)
src/garth/utils.py (1)
  • camel_to_snake_dict (17-33)
src/garth/data/_base.py (1)
  • Data (15-47)
src/garth/http.py (2)
  • Client (19-244)
  • connectapi (186-192)
🔇 Additional comments (2)
tests/data/test_daily_summary.py (1)

9-14: Solid positive-path coverage for .get().

Assertions are clear and stable with VCR. LGTM.

src/garth/data/daily_summary.py (1)

11-43: Schema looks good and maps cleanly to API fields.

Field types and optionality align with expected payloads. LGTM.

@matin matin force-pushed the feature/daily-summary branch from bfec094 to e2091ec Compare January 8, 2026 18:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/garth/data/daily_summary.py:
- Line 58: Replace the fragile assertion with explicit error handling: check
that the parameter/variable daily_summary is an instance of dict and, if not,
raise a clear exception (e.g., TypeError or ValueError) with a descriptive
message mentioning daily_summary and expected type; update the surrounding
function or method (where daily_summary is validated) to use this explicit check
and consider logging the error before raising if appropriate.
🧹 Nitpick comments (1)
src/garth/data/daily_summary.py (1)

45-59: Consider adding explicit error handling for consistency and robustness.

The get method currently follows the pattern of other data models in the codebase (e.g., WeightData, SleepData), which don't include try-except blocks. However, src/garth/data/body_battery/events.py demonstrates a more robust approach: it wraps client.connectapi() in try-except with logging, gracefully handling potential network/API errors and data parsing failures.

For DailySummary.get(), explicit error handling would address:

  • Network/API errors from client.connectapi()
  • Schema validation errors when unpacking **daily_summary into the Pydantic dataclass

Adopting the error-handling pattern from BodyBattery.events would align this method with best practices already demonstrated in the codebase.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfec094 and e2091ec.

⛔ Files ignored due to path filters (2)
  • tests/data/cassettes/test_daily_summary_get.yaml is excluded by !tests/**/cassettes/**
  • tests/data/cassettes/test_daily_summary_list.yaml is excluded by !tests/**/cassettes/**
📒 Files selected for processing (5)
  • README.md
  • src/garth/__init__.py
  • src/garth/data/__init__.py
  • src/garth/data/daily_summary.py
  • tests/data/test_daily_summary.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/garth/init.py
  • src/garth/data/init.py
  • tests/data/test_daily_summary.py
  • README.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use make format to auto-format Python source files using ruff
Use make lint to lint Python source files (ruff format check, ruff check, mypy)

Files:

  • src/garth/data/daily_summary.py
🧬 Code graph analysis (1)
src/garth/data/daily_summary.py (4)
src/garth/utils.py (1)
  • camel_to_snake_dict (17-33)
src/garth/data/_base.py (1)
  • Data (15-47)
tests/conftest.py (1)
  • client (21-22)
src/garth/http.py (2)
  • Client (19-245)
  • connectapi (186-192)
🔇 Additional comments (3)
src/garth/data/daily_summary.py (3)

1-8: LGTM: Imports are well-structured.

All necessary imports are present and appropriately organized.


11-43: LGTM: Well-structured dataclass definition.

The DailySummary model is properly designed with appropriate required and optional fields, and all fields are correctly typed.


53-55: Clarify the handling of missing data in API responses and consider explicit validation.

The check if not daily_summary: only catches None (HTTP 204) and empty dict/list responses, not dicts with missing required fields. Since DailySummary requires user_profile_id: int and calendar_date: date, if the API returns a dict without these fields, the check will pass but dataclass instantiation will fail with a Pydantic ValidationError.

Consider one of these approaches:

  1. If the API always returns a complete dict structure (even for inactive days): Remove the check as you suggested, since the dataclass validation will catch incomplete responses.

  2. If the API can return None or incomplete responses: Keep the check but add explicit field validation—for example, verify that required fields exist after conversion:

    if not daily_summary or not isinstance(daily_summary, dict):
        return None
    daily_summary = camel_to_snake_dict(daily_summary)
    if not all(k in daily_summary for k in ("user_profile_id", "calendar_date")):
        return None
  3. Document the actual API behavior: Add a comment explaining when the API returns None vs. a dict, and what structure inactive days have.

Note: The pragma: no cover annotation indicates this code path is untested, so verify against actual API responses.

Comment thread src/garth/data/daily_summary.py Outdated
return None

daily_summary = camel_to_snake_dict(daily_summary)
assert isinstance(daily_summary, dict)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Replace assertion with proper error handling.

Using assert in production code is an anti-pattern because assertions can be disabled with Python's -O optimization flag.

♻️ Proposed fix
     daily_summary = camel_to_snake_dict(daily_summary)
-    assert isinstance(daily_summary, dict)
+    if not isinstance(daily_summary, dict):
+        raise TypeError(f"Expected dict from API, got {type(daily_summary).__name__}")
     return cls(**daily_summary)
🤖 Prompt for AI Agents
In @src/garth/data/daily_summary.py at line 58, Replace the fragile assertion
with explicit error handling: check that the parameter/variable daily_summary is
an instance of dict and, if not, raise a clear exception (e.g., TypeError or
ValueError) with a descriptive message mentioning daily_summary and expected
type; update the surrounding function or method (where daily_summary is
validated) to use this explicit check and consider logging the error before
raising if appropriate.

@matin matin force-pushed the feature/daily-summary branch from e2091ec to 7b2acd3 Compare January 8, 2026 18:48
@matin matin force-pushed the feature/daily-summary branch from 7b2acd3 to 1e62a28 Compare January 8, 2026 18:51
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2091ec and 7b2acd3.

⛔ Files ignored due to path filters (2)
  • tests/data/cassettes/test_daily_summary_get.yaml is excluded by !tests/**/cassettes/**
  • tests/data/cassettes/test_daily_summary_list.yaml is excluded by !tests/**/cassettes/**
📒 Files selected for processing (5)
  • README.md
  • src/garth/__init__.py
  • src/garth/data/__init__.py
  • src/garth/data/daily_summary.py
  • tests/data/test_daily_summary.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • README.md
  • tests/data/test_daily_summary.py
  • src/garth/init.py
  • src/garth/data/init.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use make format to auto-format Python source files using ruff
Use make lint to lint Python source files (ruff format check, ruff check, mypy)

Files:

  • src/garth/data/daily_summary.py
🧬 Code graph analysis (1)
src/garth/data/daily_summary.py (3)
src/garth/utils.py (1)
  • camel_to_snake_dict (17-33)
src/garth/data/_base.py (1)
  • Data (15-47)
src/garth/http.py (2)
  • Client (19-245)
  • connectapi (186-192)
🪛 GitHub Actions: CI
src/garth/data/daily_summary.py

[error] 57-57: mypy: Argument 1 to 'camel_to_snake_dict' has incompatible type 'dict[str, Any] | list[dict[str, Any]]'; expected 'dict[str, Any]'.

🔇 Additional comments (2)
src/garth/data/daily_summary.py (2)

1-8: LGTM: Clean imports.

All imports are properly organized and used within the file.


11-43: LGTM: Well-structured data model.

The DailySummary dataclass is appropriately designed with required identity fields (user_profile_id, calendar_date) and comprehensive optional health metrics. Field types are correct.

Comment on lines +45 to +59
@classmethod
def get(
cls, day: date | str, *, client: http.Client | None = None
) -> Self | None:
client = client or http.client
path = f"/usersummary-service/usersummary/daily/?calendarDate={day}"
daily_summary = client.connectapi(path)

# pragma: no cover
if not daily_summary:
return None

daily_summary = camel_to_snake_dict(daily_summary)
assert isinstance(daily_summary, dict)
return cls(**daily_summary)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix type handling to resolve mypy error.

The mypy pipeline failure on line 57 is valid: client.connectapi() returns dict[str, Any] | list[dict[str, Any]] | None, but camel_to_snake_dict expects only dict[str, Any]. The isinstance check on line 58 comes too late—it needs to occur before calling camel_to_snake_dict to satisfy mypy and prevent passing a list.

Additionally, consider replacing the falsy check with an explicit is None check for clarity.

🔧 Proposed fix for type handling
     @classmethod
     def get(
         cls, day: date | str, *, client: http.Client | None = None
     ) -> Self | None:
         client = client or http.client
         path = f"/usersummary-service/usersummary/daily/?calendarDate={day}"
         daily_summary = client.connectapi(path)
 
-        # pragma: no cover
-        if not daily_summary:
+        if daily_summary is None:
             return None
+        
+        if not isinstance(daily_summary, dict):
+            raise TypeError(f"Expected dict from API, got {type(daily_summary).__name__}")
 
         daily_summary = camel_to_snake_dict(daily_summary)
-        assert isinstance(daily_summary, dict)
         return cls(**daily_summary)

This fix:

  • Explicitly checks for None (clearer intent)
  • Validates the type before calling camel_to_snake_dict (resolves mypy error)
  • Raises a descriptive error if the API unexpectedly returns a list
  • Removes the # pragma: no cover since the None path should now be testable
🧰 Tools
🪛 GitHub Actions: CI

[error] 57-57: mypy: Argument 1 to 'camel_to_snake_dict' has incompatible type 'dict[str, Any] | list[dict[str, Any]]'; expected 'dict[str, Any]'.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/data/test_daily_summary.py (1)

1-25: LGTM! Tests provide adequate happy-path coverage.

The tests follow coding guidelines and established patterns:

  • Use VCR for HTTP replay
  • No return type hints on test functions (as required)
  • Correct import ordering
  • max_workers=1 for deterministic execution

The current tests validate basic functionality for both single-date and multi-date retrieval scenarios.

Optional: Consider adding edge case tests

For more comprehensive coverage, you could add tests for:

  • Dates with no activity data (to verify None handling)
  • API error responses
  • Invalid date formats

However, these are nice-to-have improvements and not required for the initial implementation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b2acd3 and 1e62a28.

⛔ Files ignored due to path filters (2)
  • tests/data/cassettes/test_daily_summary_get.yaml is excluded by !tests/**/cassettes/**
  • tests/data/cassettes/test_daily_summary_list.yaml is excluded by !tests/**/cassettes/**
📒 Files selected for processing (5)
  • README.md
  • src/garth/__init__.py
  • src/garth/data/__init__.py
  • src/garth/data/daily_summary.py
  • tests/data/test_daily_summary.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/garth/data/daily_summary.py
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use make format to auto-format Python source files using ruff
Use make lint to lint Python source files (ruff format check, ruff check, mypy)

Files:

  • src/garth/__init__.py
  • src/garth/data/__init__.py
  • tests/data/test_daily_summary.py
**/__init__.py

📄 CodeRabbit inference engine (CLAUDE.md)

Provide the main client instance as garth.client for direct API access

Files:

  • src/garth/__init__.py
  • src/garth/data/__init__.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest with VCR cassettes for HTTP recording/playback in tests
Maintain comprehensive test coverage across all modules with separate test directories mirroring source structure

Files:

  • tests/data/test_daily_summary.py
tests/**

⚙️ CodeRabbit configuration file

tests/**: - test functions shouldn't have a return type hint

  • it's ok to use assert instead of pytest.assume()

Files:

  • tests/data/test_daily_summary.py
🧠 Learnings (1)
📚 Learning: 2026-01-08T00:25:14.763Z
Learnt from: CR
Repo: matin/garth PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-08T00:25:14.763Z
Learning: Applies to data/**/*.py : Raw data retrieval from Garmin Connect API should be organized in the `data/` module with submodules for specific data types (body_battery/, hrv.py, sleep.py, weight.py)

Applied to files:

  • src/garth/__init__.py
  • src/garth/data/__init__.py
🧬 Code graph analysis (3)
src/garth/__init__.py (1)
src/garth/data/daily_summary.py (1)
  • DailySummary (12-61)
src/garth/data/__init__.py (1)
src/garth/data/daily_summary.py (1)
  • DailySummary (12-61)
tests/data/test_daily_summary.py (3)
src/garth/data/daily_summary.py (2)
  • DailySummary (12-61)
  • get (46-61)
src/garth/http.py (1)
  • Client (19-245)
tests/conftest.py (2)
  • authed_client (65-75)
  • client (21-22)
🔇 Additional comments (3)
README.md (1)

908-1027: LGTM! Clear and comprehensive documentation.

The Daily Summary documentation follows the established pattern and provides clear examples for both single-date and date-range retrieval. The sample data fields are well-illustrated and match the API surface.

src/garth/__init__.py (1)

4-4: LGTM! Correct public API exposure.

The DailySummary import and export follow the established pattern and maintain alphabetical ordering consistently with other data classes in the public API.

Also applies to: 37-37

src/garth/data/__init__.py (1)

6-6: LGTM! Proper module exposure.

The DailySummary export and import follow the established pattern and maintain alphabetical ordering with other data classes in the module.

Also applies to: 22-22

@matin matin merged commit 16c214e into matin:main Jan 8, 2026
23 checks passed
@matin matin mentioned this pull request Jan 8, 2026
19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants