garmin sync: fix same-day second activity skipped by date-only dedup
This commit is contained in:
@@ -133,18 +133,24 @@ def sync_activities(garmin, user_id: int, since: Optional[datetime],
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Slow-path dedup: activity imported via bulk export (no garmin_activity_id).
|
# Slow-path dedup: activity imported via bulk export (no garmin_activity_id).
|
||||||
# Check by start_time; stamp the ID so future syncs skip it in the fast path.
|
# Match on the actual start instant (not just the date — two activities on
|
||||||
act_start_str = act.get("startTimeLocal") or act.get("startTimeGMT") or ""
|
# the same day are distinct), comparing GMT-to-GMT since FIT start_times are
|
||||||
|
# stored in UTC. A small window absorbs sub-second/rounding differences.
|
||||||
|
act_start_str = act.get("startTimeGMT") or ""
|
||||||
if act_start_str:
|
if act_start_str:
|
||||||
try:
|
try:
|
||||||
from datetime import datetime as _dt
|
from datetime import datetime as _dt, timezone as _tz
|
||||||
act_start = _dt.fromisoformat(act_start_str.replace("Z", "+00:00"))
|
act_start = _dt.fromisoformat(act_start_str.replace("Z", "+00:00"))
|
||||||
|
if act_start.tzinfo is None:
|
||||||
|
act_start = act_start.replace(tzinfo=_tz.utc) # startTimeGMT is UTC
|
||||||
|
window = timedelta(minutes=5)
|
||||||
time_match = db.execute(
|
time_match = db.execute(
|
||||||
select(Activity).where(
|
select(Activity).where(
|
||||||
Activity.user_id == user_id,
|
Activity.user_id == user_id,
|
||||||
func.date(Activity.start_time) == act_start.date(),
|
Activity.start_time >= act_start - window,
|
||||||
|
Activity.start_time <= act_start + window,
|
||||||
)
|
)
|
||||||
).scalar_one_or_none()
|
).scalars().first()
|
||||||
if time_match:
|
if time_match:
|
||||||
if not time_match.garmin_activity_id:
|
if not time_match.garmin_activity_id:
|
||||||
time_match.garmin_activity_id = garmin_id
|
time_match.garmin_activity_id = garmin_id
|
||||||
|
|||||||
@@ -127,18 +127,24 @@ def sync_activities(garmin, user_id: int, since: Optional[datetime],
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Slow-path dedup: activity imported via bulk export (no garmin_activity_id).
|
# Slow-path dedup: activity imported via bulk export (no garmin_activity_id).
|
||||||
# Check by start_time; stamp the ID so future syncs skip it in the fast path.
|
# Match on the actual start instant (not just the date — two activities on
|
||||||
act_start_str = act.get("startTimeLocal") or act.get("startTimeGMT") or ""
|
# the same day are distinct), comparing GMT-to-GMT since FIT start_times are
|
||||||
|
# stored in UTC. A small window absorbs sub-second/rounding differences.
|
||||||
|
act_start_str = act.get("startTimeGMT") or ""
|
||||||
if act_start_str:
|
if act_start_str:
|
||||||
try:
|
try:
|
||||||
from datetime import datetime as _dt
|
from datetime import datetime as _dt, timezone as _tz
|
||||||
act_start = _dt.fromisoformat(act_start_str.replace("Z", "+00:00"))
|
act_start = _dt.fromisoformat(act_start_str.replace("Z", "+00:00"))
|
||||||
|
if act_start.tzinfo is None:
|
||||||
|
act_start = act_start.replace(tzinfo=_tz.utc) # startTimeGMT is UTC
|
||||||
|
window = timedelta(minutes=5)
|
||||||
time_match = db.execute(
|
time_match = db.execute(
|
||||||
select(Activity).where(
|
select(Activity).where(
|
||||||
Activity.user_id == user_id,
|
Activity.user_id == user_id,
|
||||||
func.date(Activity.start_time) == act_start.date(),
|
Activity.start_time >= act_start - window,
|
||||||
|
Activity.start_time <= act_start + window,
|
||||||
)
|
)
|
||||||
).scalar_one_or_none()
|
).scalars().first()
|
||||||
if time_match:
|
if time_match:
|
||||||
if not time_match.garmin_activity_id:
|
if not time_match.garmin_activity_id:
|
||||||
time_match.garmin_activity_id = garmin_id
|
time_match.garmin_activity_id = garmin_id
|
||||||
|
|||||||
Reference in New Issue
Block a user