Add FitnessActivity for training plan from coach#173
Conversation
Adds FitnessActivity class to retrieve activities with adaptive coaching metadata from the fitness stats service. This includes: - workout_type (e.g., ADAPTIVE_COACHING) - adaptive_coaching_workout_status (e.g., COMPLETED_VIA_ACTIVITY) - aerobic_training_effect - workout_group_enumerator Closes #162 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WalkthroughThis change introduces a new Changes
Sequence DiagramsequenceDiagram
actor App as Application Code
participant FA as FitnessActivity.list()
participant Client as HTTP Client
participant API as Fitness Stats Service
participant Parser as Data Normalizer
App->>FA: list(end, days, client)
FA->>FA: Construct date range
FA->>Client: GET /fitnessstats-service/activity/all
Client->>API: HTTP Request with metrics
API-->>Client: JSON Response (camelCase)
Client-->>FA: Response data
FA->>Parser: camel_to_snake_dict()
Parser-->>FA: Normalized dict (snake_case)
FA->>FA: Instantiate FitnessActivity objects
FA->>FA: Sort by start_local
FA-->>App: list[FitnessActivity]
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #173 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 64 66 +2
Lines 2970 3020 +50
=========================================
+ Hits 2970 3020 +50
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/garth/data/fitness_stats.py (1)
74-76: Consider replacingassertwith explicit exception for runtime validation.Using
assertfor validating API responses is fragile because assertions can be disabled globally withpython -O. If the API unexpectedly returns a non-list (e.g., error dict), this check would be silently skipped in optimized mode.♻️ Suggested fix
data = client.connectapi(path, params=params) - assert isinstance(data, list), ( - f"Expected list from {path}, got {type(data).__name__}" - ) + if not isinstance(data, list): + raise TypeError( + f"Expected list from {path}, got {type(data).__name__}" + )
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/data/cassettes/test_fitness_activity_list.yamlis excluded by!tests/**/cassettes/**
📒 Files selected for processing (5)
docs/api/data.mdsrc/garth/__init__.pysrc/garth/data/__init__.pysrc/garth/data/fitness_stats.pytests/data/test_fitness_stats.py
🧰 Additional context used
📓 Path-based instructions (2)
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use pytest with VCR cassettes for HTTP recording/playback in tests
Mirror test directory structure to match source code structure
Files:
tests/data/test_fitness_stats.py
tests/**
⚙️ CodeRabbit configuration file
tests/**: - test functions shouldn't have a return type hint
- it's ok to use
assertinstead ofpytest.assume()
Files:
tests/data/test_fitness_stats.py
🧠 Learnings (1)
📚 Learning: 2026-01-09T22:41:07.978Z
Learnt from: CR
Repo: matin/garth PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T22:41:07.978Z
Learning: Use `data/` module for raw data retrieval from Garmin Connect API and `stats/` module for processed statistics and aggregated data
Applied to files:
src/garth/data/__init__.pysrc/garth/__init__.py
🧬 Code graph analysis (4)
src/garth/data/__init__.py (1)
src/garth/data/fitness_stats.py (1)
FitnessActivity(14-83)
tests/data/test_fitness_stats.py (1)
src/garth/data/fitness_stats.py (1)
FitnessActivity(14-83)
src/garth/__init__.py (1)
src/garth/data/fitness_stats.py (1)
FitnessActivity(14-83)
src/garth/data/fitness_stats.py (3)
src/garth/utils.py (2)
camel_to_snake_dict(17-33)format_end_date(66-71)tests/conftest.py (1)
client(21-22)src/garth/http.py (2)
Client(19-245)connectapi(186-192)
🔇 Additional comments (8)
src/garth/data/fitness_stats.py (3)
1-11: LGTM - Clean imports and utility usage.The imports are well-organized and the use of existing utilities (
camel_to_snake_dict,format_end_date) promotes code reuse. Based on learnings, placing this indata/module is correct for raw data retrieval from Garmin Connect API.
13-35: LGTM - Well-structured dataclass with clear documentation.The dataclass fields align with the API response and the docstring provides a helpful usage example. Optional fields appropriately default to
None.
78-81: No fix required—the API response contains only fields declared in the class.The actual API response from the fitness stats service includes only the fields declared in
FitnessActivity. Aftercamel_to_snake_dictconversion, all response fields map exactly to class attributes (activityId → activity_id, startLocal → start_local, etc.), with no extra fields present. The test cassette confirms this pattern with real API data, socls(**item)instantiation works as expected without raisingTypeError.src/garth/__init__.py (1)
8-8: LGTM - Proper public API exposure.
FitnessActivityis correctly imported and exported, maintaining alphabetical ordering consistent with the existing pattern.Also applies to: 50-50
tests/data/test_fitness_stats.py (2)
1-7: LGTM - Imports and test setup follow project conventions.The test file correctly uses pytest with VCR cassettes for HTTP recording/playback, as specified in the coding guidelines.
9-29: Good test coverage for the new functionality.The test validates key behaviors: non-empty results, sorted ordering, required field presence, and coaching-specific fields. The
@pytest.mark.vcrdecorator andauthed_clientfixture usage follows project conventions.One minor observation: line 29 only validates the first coaching activity's status. Consider validating all coaching activities if the VCR cassette contains multiple:
for activity in coaching: assert activity.adaptive_coaching_workout_status is not Nonesrc/garth/data/__init__.py (1)
10-10: LGTM - Proper subpackage export.
FitnessActivityis correctly added to__all__and imported from.fitness_stats, maintaining alphabetical order consistent with other exports.Also applies to: 30-30
docs/api/data.md (1)
422-484: Well-documented new feature.The documentation follows the existing pattern in the file, providing:
- Clear description of the feature's purpose
- Usage example with
list()method- Sample output showing field values
- Filtering example for coaching activities
- Reference for coaching status enum values
Summary
FitnessActivityclass to retrieve activities with adaptive coaching metadata/fitnessstats-service/activity/allendpointCloses #162
Test plan
list()method🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.