Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def __init__(self):
# API call settings
self.default_limit = 100
self.start = 0
self.start_badge = 1 # Badge related calls start counting at 1
self.start_badge = 1 # Badge related calls in demo default to 1 (backend page index)

# Activity settings
self.activitytype = "" # Possible values: cycling, running, swimming, multi_sport, fitness_equipment, hiking, walking, other
Expand Down Expand Up @@ -2221,7 +2221,9 @@ def clean_step_ids(workout_segments):

except FileNotFoundError:
print(f"❌ File not found: {config.workoutfile}")
print("ℹ️ Please ensure the workout JSON file exists in the test_data directory")
print(
"ℹ️ Please ensure the workout JSON file exists in the test_data directory"
)
except json.JSONDecodeError as e:
print(f"❌ Invalid JSON format in {config.workoutfile}: {e}")
print("ℹ️ Please check the JSON file format")
Expand Down
12 changes: 8 additions & 4 deletions garminconnect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1418,7 +1418,8 @@ def get_badge_challenges(self, start: int, limit: int) -> dict[str, Any]:
start = _validate_non_negative_integer(start, "start")
limit = _validate_positive_integer(limit, "limit")
url = self.garmin_connect_badge_challenges_url
params = {"start": str(start), "limit": str(limit)}
backend_start = str(1 if start == 0 else start)
params = {"desc": "true", "start": backend_start, "limit": str(limit)}
Comment on lines +1418 to +1419
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Incorrect 0-based to 1-based conversion for badge pagination.

backend_start = str(1 if start == 0 else start) only fixes page 0. For page 1+, this sends the wrong backend page and returns shifted/duplicate results.

🔧 Proposed fix
+    `@staticmethod`
+    def _badge_challenge_params(start: int, limit: int) -> dict[str, str]:
+        """Build backend params for badge-challenge endpoints.
+
+        Public API uses 0-based start; backend requires 1-based.
+        """
+        return {"desc": "true", "start": str(start + 1), "limit": str(limit)}
+
     def get_badge_challenges(self, start: int, limit: int) -> dict[str, Any]:
@@
-        backend_start = str(1 if start == 0 else start)
-        params = {"desc": "true", "start": backend_start, "limit": str(limit)}
+        params = self._badge_challenge_params(start, limit)
@@
     def get_available_badge_challenges(self, start: int, limit: int) -> dict[str, Any]:
@@
-        backend_start = str(1 if start == 0 else start)
-        params = {"desc": "true", "start": backend_start, "limit": str(limit)}
+        params = self._badge_challenge_params(start, limit)
@@
     def get_non_completed_badge_challenges(
         self, start: int, limit: int
     ) -> dict[str, Any]:
@@
-        backend_start = str(1 if start == 0 else start)
-        params = {"desc": "true", "start": backend_start, "limit": str(limit)}
+        params = self._badge_challenge_params(start, limit)
@@
     def get_inprogress_virtual_challenges(
         self, start: int, limit: int
     ) -> dict[str, Any]:
@@
-        backend_start = str(1 if start == 0 else start)
-        params = {"desc": "true", "start": backend_start, "limit": str(limit)}
+        params = self._badge_challenge_params(start, limit)

Also applies to: 1429-1430, 1442-1443, 1455-1456

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@garminconnect/__init__.py` around lines 1418 - 1419, The pagination logic
incorrectly maps 0-based pages to backend pages by only special-casing start==0;
replace the conditional assignment (backend_start = str(1 if start == 0 else
start)) with a proper 1-based conversion such as backend_start = str(int(start)
+ 1) so every 0-based start maps to backend page start+1, and apply the same
change to the other occurrences that use backend_start/params (the similar
assignments at the other locations referenced in the review).

logger.debug("Requesting badge challenges for user")

return self.connectapi(url, params=params)
Expand All @@ -1428,7 +1429,8 @@ def get_available_badge_challenges(self, start: int, limit: int) -> dict[str, An
start = _validate_non_negative_integer(start, "start")
limit = _validate_positive_integer(limit, "limit")
url = self.garmin_connect_available_badge_challenges_url
params = {"start": str(start), "limit": str(limit)}
backend_start = str(1 if start == 0 else start)
params = {"desc": "true", "start": backend_start, "limit": str(limit)}
logger.debug("Requesting available badge challenges")

return self.connectapi(url, params=params)
Expand All @@ -1440,7 +1442,8 @@ def get_non_completed_badge_challenges(
start = _validate_non_negative_integer(start, "start")
limit = _validate_positive_integer(limit, "limit")
url = self.garmin_connect_non_completed_badge_challenges_url
params = {"start": str(start), "limit": str(limit)}
backend_start = str(1 if start == 0 else start)
params = {"desc": "true", "start": backend_start, "limit": str(limit)}
logger.debug("Requesting badge challenges for user")

return self.connectapi(url, params=params)
Expand All @@ -1452,7 +1455,8 @@ def get_inprogress_virtual_challenges(
start = _validate_non_negative_integer(start, "start")
limit = _validate_positive_integer(limit, "limit")
url = self.garmin_connect_inprogress_virtual_challenges_url
params = {"start": str(start), "limit": str(limit)}
backend_start = str(1 if start == 0 else start)
params = {"desc": "true", "start": backend_start, "limit": str(limit)}
logger.debug("Requesting in-progress virtual challenges for user")

return self.connectapi(url, params=params)
Expand Down