Skip to content

Commit 8ea4374

Browse files
committed
Codebase fixes
1 parent 9553b9f commit 8ea4374

File tree

3 files changed

+40
-14
lines changed

3 files changed

+40
-14
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ repos:
2828
rev: v0.6.4
2929
hooks:
3030
- id: ruff
31-
args: [--fix]
31+
args: [--fix, --unsafe-fixes]
3232
- id: ruff-format
3333

3434
- repo: local

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ python3 -m venv .venv --copies
101101
source .venv/bin/activate # On Windows: .venv\Scripts\activate
102102

103103
# 2. Install PDM (Python Dependency Manager)
104-
pip install pdm "black[jupyter]" codespell
104+
pip install pdm
105105

106106
# 3. Install all development dependencies
107107
pdm install --group :all
@@ -272,9 +272,13 @@ Explore the API interactively with our [reference notebook](https://github.com/c
272272

273273
```python
274274
from garminconnect import Garmin
275+
import os
275276

276277
# Initialize and login
277-
client = Garmin('your_email', 'your_password')
278+
client = Garmin(
279+
os.getenv("GARMIN_EMAIL", "<YOUR_EMAIL>"),
280+
os.getenv("GARMIN_PASSWORD", "<YOUR_PASSWORD>")
281+
)
278282
client.login()
279283

280284
# Get today's stats

garminconnect/__init__.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,15 @@ def connectapi(self, path: str, **kwargs: Any) -> Any:
291291
else:
292292
raise GarminConnectConnectionError(f"HTTP error: {e}") from e
293293
except Exception as e:
294+
logger.exception("Connection error during connectapi path=%s", path)
294295
raise GarminConnectConnectionError(f"Connection error: {e}") from e
295296

296297
def download(self, path: str, **kwargs: Any) -> Any:
297298
"""Wrapper for garth download with error handling."""
298299
try:
299300
return self.garth.download(path, **kwargs)
300301
except Exception as e:
302+
logger.exception("Download error path=%s", path)
301303
status = getattr(getattr(e, "response", None), "status_code", None)
302304
logger.error(
303305
"Download failed for path '%s': %s (status=%s)", path, e, status
@@ -483,8 +485,8 @@ def get_daily_steps(self, start: str, end: str) -> list[dict[str, Any]]:
483485
end = _validate_date_format(end, "end")
484486

485487
# Validate date range
486-
start_date = datetime.strptime(start, "%Y-%m-%d").date()
487-
end_date = datetime.strptime(end, "%Y-%m-%d").date()
488+
start_date = datetime.strptime(start, DATE_FORMAT_STR).date()
489+
end_date = datetime.strptime(end, DATE_FORMAT_STR).date()
488490

489491
if start_date > end_date:
490492
raise ValueError("start date cannot be after end date")
@@ -783,7 +785,13 @@ def set_blood_pressure(
783785
"sourceType": "MANUAL",
784786
"notes": notes,
785787
}
786-
788+
for name, val, lo, hi in (
789+
("systolic", systolic, 70, 260),
790+
("diastolic", diastolic, 40, 150),
791+
("pulse", pulse, 20, 250),
792+
):
793+
if not isinstance(val, int) or not (lo <= val <= hi):
794+
raise ValueError(f"{name} must be an int in [{lo}, {hi}]")
787795
logger.debug("Adding blood pressure")
788796

789797
return self.garth.post("connectapi", url, json=payload).json()
@@ -852,10 +860,11 @@ def get_lactate_threshold(
852860
power_url = f"{self.garmin_connect_biometric_url}/powerToWeight/latest/{date.today()}?sport=Running"
853861

854862
power = self.connectapi(power_url)
855-
try:
863+
if isinstance(power, list) and power:
856864
power_dict = power[0]
857-
except IndexError:
858-
# If no power available
865+
elif isinstance(power, dict):
866+
power_dict = power
867+
else:
859868
power_dict = {}
860869

861870
speed_and_heart_rate = self.connectapi(speed_and_heart_rate_url)
@@ -965,10 +974,13 @@ def add_hydration_data(
965974
# If cdate is not null, validate and use timestamp associated with midnight
966975
cdate = _validate_date_format(cdate, "cdate")
967976
try:
968-
raw_ts = datetime.strptime(cdate, "%Y-%m-%d")
969-
timestamp = _fmt_ts(raw_ts)
970-
except ValueError as e:
971-
raise ValueError(f"invalid cdate: {e}") from e
977+
raw_ts = datetime.fromisoformat(cdate)
978+
except ValueError:
979+
# if cdate is just a date, parse with format and set time to 00:00:00
980+
raw_ts = datetime.strptime(cdate, DATE_FORMAT_STR)
981+
timestamp = raw_ts.replace(
982+
hour=0, minute=0, second=0, microsecond=0
983+
).isoformat(timespec="microseconds")
972984

973985
elif cdate is None and timestamp is not None:
974986
# If timestamp is not null, validate and set cdate equal to date part of timestamp
@@ -1123,6 +1135,8 @@ def is_badge_in_progress(badge: dict) -> bool:
11231135
def get_adhoc_challenges(self, start: int, limit: int) -> dict[str, Any]:
11241136
"""Return adhoc challenges for current user."""
11251137

1138+
start = _validate_non_negative_integer(start, "start")
1139+
limit = _validate_positive_integer(limit, "limit")
11261140
url = self.garmin_connect_adhoc_challenges_url
11271141
params = {"start": str(start), "limit": str(limit)}
11281142
logger.debug("Requesting adhoc challenges for user")
@@ -1132,6 +1146,8 @@ def get_adhoc_challenges(self, start: int, limit: int) -> dict[str, Any]:
11321146
def get_badge_challenges(self, start: int, limit: int) -> dict[str, Any]:
11331147
"""Return badge challenges for current user."""
11341148

1149+
start = _validate_non_negative_integer(start, "start")
1150+
limit = _validate_positive_integer(limit, "limit")
11351151
url = self.garmin_connect_badge_challenges_url
11361152
params = {"start": str(start), "limit": str(limit)}
11371153
logger.debug("Requesting badge challenges for user")
@@ -1141,6 +1157,8 @@ def get_badge_challenges(self, start: int, limit: int) -> dict[str, Any]:
11411157
def get_available_badge_challenges(self, start: int, limit: int) -> dict[str, Any]:
11421158
"""Return available badge challenges."""
11431159

1160+
start = _validate_non_negative_integer(start, "start")
1161+
limit = _validate_positive_integer(limit, "limit")
11441162
url = self.garmin_connect_available_badge_challenges_url
11451163
params = {"start": str(start), "limit": str(limit)}
11461164
logger.debug("Requesting available badge challenges")
@@ -1152,6 +1170,8 @@ def get_non_completed_badge_challenges(
11521170
) -> dict[str, Any]:
11531171
"""Return badge non-completed challenges for current user."""
11541172

1173+
start = _validate_non_negative_integer(start, "start")
1174+
limit = _validate_positive_integer(limit, "limit")
11551175
url = self.garmin_connect_non_completed_badge_challenges_url
11561176
params = {"start": str(start), "limit": str(limit)}
11571177
logger.debug("Requesting badge challenges for user")
@@ -1163,6 +1183,8 @@ def get_inprogress_virtual_challenges(
11631183
) -> dict[str, Any]:
11641184
"""Return in-progress virtual challenges for current user."""
11651185

1186+
start = _validate_non_negative_integer(start, "start")
1187+
limit = _validate_positive_integer(limit, "limit")
11661188
url = self.garmin_connect_inprogress_virtual_challenges_url
11671189
params = {"start": str(start), "limit": str(limit)}
11681190
logger.debug("Requesting in-progress virtual challenges for user")
@@ -1488,7 +1510,7 @@ def set_activity_type(
14881510

14891511
def create_manual_activity_from_json(self, payload: dict[str, Any]) -> Any:
14901512
url = f"{self.garmin_connect_activity}"
1491-
logger.debug(f"Uploading manual activity: {str(payload)}")
1513+
logger.debug("Uploading manual activity: %s", str(payload))
14921514
return self.garth.post("connectapi", url, json=payload, api=True)
14931515

14941516
def create_manual_activity(

0 commit comments

Comments
 (0)