Skip to content

Commit 9553b9f

Browse files
committed
Coderabbit fixes datestamps etc
1 parent 9d28d87 commit 9553b9f

File tree

2 files changed

+45
-26
lines changed

2 files changed

+45
-26
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ Select a category:
2525
Make your selection:
2626
```
2727

28-
## API Coverage Statistics
28+
## API Coverage Statistics (as of 2025-08-31)
2929

30-
- **Total API Methods**: 101 unique endpoints
30+
- **Total API Methods**: 101 unique endpoints (snapshot)
3131
- **Categories**: 11 organized sections
3232
- **User & Profile**: 4 methods (basic user info, settings)
3333
- **Daily Health & Activity**: 8 methods (today's health data)

garminconnect/__init__.py

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ def _validate_positive_integer(value: int, param_name: str = "value") -> int:
8181
return value
8282

8383

84+
def _fmt_ts(dt: datetime) -> str:
85+
# Use ms precision to match server expectations
86+
return dt.replace(tzinfo=None).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
87+
88+
8489
class Garmin:
8590
"""Class for fetching data from Garmin Connect."""
8691

@@ -615,8 +620,8 @@ def add_weigh_in(
615620
# Apply timezone offset to get UTC/GMT time
616621
dtGMT = dt.astimezone(timezone.utc)
617622
payload = {
618-
"dateTimestamp": dt.isoformat()[:19] + ".00",
619-
"gmtTimestamp": dtGMT.isoformat()[:19] + ".00",
623+
"dateTimestamp": f"{_fmt_ts(dt)}.000",
624+
"gmtTimestamp": f"{_fmt_ts(dtGMT)}.000",
620625
"unitKey": unitKey,
621626
"sourceType": "MANUAL",
622627
"value": weight,
@@ -650,15 +655,15 @@ def add_weigh_in_with_timestamps(
650655
weight = _validate_positive_number(weight, "weight")
651656
# Build the payload
652657
payload = {
653-
"dateTimestamp": dt.isoformat()[:19] + ".00", # Local time
654-
"gmtTimestamp": dtGMT.isoformat()[:19] + ".00", # GMT/UTC time
658+
"dateTimestamp": f"{_fmt_ts(dt)}.000", # Local time
659+
"gmtTimestamp": f"{_fmt_ts(dtGMT)}.000", # GMT/UTC time
655660
"unitKey": unitKey,
656661
"sourceType": "MANUAL",
657662
"value": weight,
658663
}
659664

660665
# Debug log for payload
661-
logger.debug(f"Adding weigh-in with explicit timestamps: {payload}")
666+
logger.debug("Adding weigh-in with explicit timestamps: %s", payload)
662667

663668
# Make the POST request
664669
return self.garth.post("connectapi", url, json=payload).json()
@@ -686,6 +691,7 @@ def get_daily_weigh_ins(self, cdate: str) -> dict[str, Any]:
686691

687692
def delete_weigh_in(self, weight_pk: str, cdate: str) -> Any:
688693
"""Delete specific weigh-in."""
694+
cdate = _validate_date_format(cdate, "cdate")
689695
url = f"{self.garmin_connect_weight_url}/weight/{cdate}/byversion/{weight_pk}"
690696
logger.debug("Deleting weigh-in")
691697

@@ -769,8 +775,8 @@ def set_blood_pressure(
769775
# Apply timezone offset to get UTC/GMT time
770776
dtGMT = dt.astimezone(timezone.utc)
771777
payload = {
772-
"measurementTimestampLocal": dt.isoformat()[:19] + ".00",
773-
"measurementTimestampGMT": dtGMT.isoformat()[:19] + ".00",
778+
"measurementTimestampLocal": f"{_fmt_ts(dt)}.000",
779+
"measurementTimestampGMT": f"{_fmt_ts(dtGMT)}.000",
774780
"systolic": systolic,
775781
"diastolic": diastolic,
776782
"pulse": pulse,
@@ -837,7 +843,6 @@ def get_lactate_threshold(
837843
:param date (Optional) - start_date: The first date in the range to query, format 'YYYY-MM-DD'. Required if `latest` is False. Ignored if `latest` is True
838844
:param date (Optional) - end_date: The last date in the range to query, format 'YYYY-MM-DD'. Defaults to current data. Ignored if `latest` is True
839845
:param str (Optional) - aggregation: How to aggregate the data. Must be one of `daily`, `weekly`, `monthly`, `yearly`.
840-
841846
"""
842847

843848
if latest:
@@ -898,6 +903,16 @@ def get_lactate_threshold(
898903
if end_date is None:
899904
end_date = date.today().isoformat()
900905

906+
# Normalize and validate
907+
if isinstance(start_date, date):
908+
start_date = start_date.isoformat()
909+
else:
910+
start_date = _validate_date_format(start_date, "start_date")
911+
if isinstance(end_date, date):
912+
end_date = end_date.isoformat()
913+
else:
914+
end_date = _validate_date_format(end_date, "end_date")
915+
901916
_valid_aggregations = {"daily", "weekly", "monthly", "yearly"}
902917
if aggregation not in _valid_aggregations:
903918
raise ValueError(f"aggregation must be one of {_valid_aggregations}")
@@ -944,14 +959,14 @@ def add_hydration_data(
944959
cdate = str(raw_date)
945960

946961
raw_ts = datetime.now()
947-
timestamp = datetime.strftime(raw_ts, "%Y-%m-%dT%H:%M:%S.%f")
962+
timestamp = _fmt_ts(raw_ts)
948963

949964
elif cdate is not None and timestamp is None:
950965
# If cdate is not null, validate and use timestamp associated with midnight
951966
cdate = _validate_date_format(cdate, "cdate")
952967
try:
953968
raw_ts = datetime.strptime(cdate, "%Y-%m-%d")
954-
timestamp = datetime.strftime(raw_ts, "%Y-%m-%dT%H:%M:%S.%f")
969+
timestamp = _fmt_ts(raw_ts)
955970
except ValueError as e:
956971
raise ValueError(f"invalid cdate: {e}") from e
957972

@@ -964,7 +979,7 @@ def add_hydration_data(
964979
cdate = str(raw_ts.date())
965980
except ValueError as e:
966981
raise ValueError(
967-
f"Invalid timestamp format (expected YYYY-MM-DDTHH:MM:SS.ffffff): {e}"
982+
f"Invalid timestamp format (expected YYYY-MM-DDTHH:MM:SS.mmm): {e}"
968983
) from e
969984
else:
970985
# Both provided - validate consistency
@@ -1425,7 +1440,7 @@ def get_activities(
14251440
if activitytype:
14261441
params["activityType"] = str(activitytype)
14271442

1428-
logger.debug(f"Requesting activities from {start} with limit {limit}")
1443+
logger.debug("Requesting activities from %d with limit %d", start, limit)
14291444

14301445
activities = self.connectapi(url, params=params)
14311446

@@ -1440,7 +1455,7 @@ def get_activities_fordate(self, fordate: str) -> dict[str, Any]:
14401455

14411456
fordate = _validate_date_format(fordate, "fordate")
14421457
url = f"{self.garmin_connect_activity_fordate}/{fordate}"
1443-
logger.debug(f"Requesting activities for date {fordate}")
1458+
logger.debug("Requesting activities for date %s", fordate)
14441459

14451460
return self.connectapi(url)
14461461

@@ -1468,7 +1483,7 @@ def set_activity_type(
14681483
"parentTypeId": parent_type_id,
14691484
},
14701485
}
1471-
logger.debug(f"Changing activity type: {str(payload)}")
1486+
logger.debug("Changing activity type: %s", payload)
14721487
return self.garth.put("connectapi", url, json=payload, api=True)
14731488

14741489
def create_manual_activity_from_json(self, payload: dict[str, Any]) -> Any:
@@ -1634,10 +1649,10 @@ def get_activities_by_date(
16341649
if sortorder:
16351650
params["sortOrder"] = str(sortorder)
16361651

1637-
logger.debug(f"Requesting activities by date from {startdate} to {enddate}")
1652+
logger.debug("Requesting activities by date from %s to %s", startdate, enddate)
16381653
while True:
16391654
params["start"] = str(start)
1640-
logger.debug(f"Requesting activities {start} to {start+limit}")
1655+
logger.debug("Requesting activities %d to %d", start, start + limit)
16411656
act = self.connectapi(url, params=params)
16421657
if act:
16431658
activities.extend(act)
@@ -1675,7 +1690,9 @@ def get_progress_summary_between_dates(
16751690
"metric": str(metric),
16761691
}
16771692

1678-
logger.debug(f"Requesting fitnessstats by date from {startdate} to {enddate}")
1693+
logger.debug(
1694+
"Requesting fitnessstats by date from %s to %s", startdate, enddate
1695+
)
16791696
return self.connectapi(url, params=params)
16801697

16811698
def get_activity_types(self) -> dict[str, Any]:
@@ -1708,10 +1725,12 @@ def get_goals(
17081725
"sortOrder": "asc",
17091726
}
17101727

1711-
logger.debug(f"Requesting {status} goals")
1728+
logger.debug("Requesting %s goals", status)
17121729
while True:
17131730
params["start"] = str(start)
1714-
logger.debug(f"Requesting {status} goals {start} to {start + limit - 1}")
1731+
logger.debug(
1732+
"Requesting %s goals %d to %d", status, start, start + limit - 1
1733+
)
17151734
goals_json = self.connectapi(url, params=params)
17161735
if goals_json:
17171736
goals.extend(goals_json)
@@ -1891,7 +1910,7 @@ def get_gear_activities(
18911910
:return: List of activities where the specified gear was used
18921911
"""
18931912
gearUUID = str(gearUUID)
1894-
1913+
limit = _validate_positive_integer(limit, "limit")
18951914
url = f"{self.garmin_connect_activities_baseurl}{gearUUID}/gear?start=0&limit={limit}"
18961915
logger.debug("Requesting activities for gearUUID %s", gearUUID)
18971916

@@ -1921,7 +1940,7 @@ def request_reload(self, cdate: str) -> dict[str, Any]:
19211940

19221941
cdate = _validate_date_format(cdate, "cdate")
19231942
url = f"{self.garmin_request_reload_url}/{cdate}"
1924-
logger.debug(f"Requesting reload of data for {cdate}.")
1943+
logger.debug("Requesting reload of data for %s.", cdate)
19251944

19261945
return self.garth.post("connectapi", url, api=True).json()
19271946

@@ -1931,7 +1950,7 @@ def get_workouts(self, start: int = 0, limit: int = 100) -> dict[str, Any]:
19311950
url = f"{self.garmin_workouts}/workouts"
19321951
start = _validate_non_negative_integer(start, "start")
19331952
limit = _validate_positive_integer(limit, "limit")
1934-
logger.debug(f"Requesting workouts from {start} with limit {limit}")
1953+
logger.debug("Requesting workouts from %d with limit %d", start, limit)
19351954
params = {"start": start, "limit": limit}
19361955
return self.connectapi(url, params=params)
19371956

@@ -1977,7 +1996,7 @@ def get_menstrual_data_for_date(self, fordate: str) -> dict[str, Any]:
19771996

19781997
fordate = _validate_date_format(fordate, "fordate")
19791998
url = f"{self.garmin_connect_menstrual_dayview_url}/{fordate}"
1980-
logger.debug(f"Requesting menstrual data for date {fordate}")
1999+
logger.debug("Requesting menstrual data for date %s", fordate)
19812000

19822001
return self.connectapi(url)
19832002

@@ -1990,7 +2009,7 @@ def get_menstrual_calendar_data(
19902009
enddate = _validate_date_format(enddate, "enddate")
19912010
url = f"{self.garmin_connect_menstrual_calendar_url}/{startdate}/{enddate}"
19922011
logger.debug(
1993-
f"Requesting menstrual data for dates {startdate} through {enddate}"
2012+
"Requesting menstrual data for dates %s through %s", startdate, enddate
19942013
)
19952014

19962015
return self.connectapi(url)

0 commit comments

Comments
 (0)