From ee948aefd3f4c4726dccca9a1c1ee3c4b6ce5aa6 Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 19 Apr 2026 17:10:11 +0200 Subject: [PATCH] fix: add retry logic for garmin 429 rate limiting --- clusters/homelab/apps/garmin/configmap.yaml | 225 ++++++++++---------- 1 file changed, 116 insertions(+), 109 deletions(-) diff --git a/clusters/homelab/apps/garmin/configmap.yaml b/clusters/homelab/apps/garmin/configmap.yaml index 7d965b7..b178af2 100644 --- a/clusters/homelab/apps/garmin/configmap.yaml +++ b/clusters/homelab/apps/garmin/configmap.yaml @@ -1,118 +1,125 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: garmin-sync-script - namespace: garmin -data: - sync.py: | - import os - import json - from datetime import date, timedelta - import psycopg2 - from garminconnect import Garmin +import os +import time +from datetime import date, timedelta +import psycopg2 +from garminconnect import Garmin - email = os.environ["GARMIN_EMAIL"] - password = os.environ["GARMIN_PASSWORD"] - db_url = os.environ["DATABASE_URL"] +email = os.environ["GARMIN_EMAIL"] +password = os.environ["GARMIN_PASSWORD"] +db_url = os.environ["DATABASE_URL"] - client = Garmin(email, password) - client.login() +# Retry-Login bei 429 +client = Garmin(email, password) +for attempt in range(5): + try: + client.login() + print(f"Login erfolgreich (Versuch {attempt + 1})") + break + except Exception as e: + if "429" in str(e): + wait = 60 * (attempt + 1) + print(f"Rate limited (429), warte {wait}s...") + time.sleep(wait) + else: + raise +else: + raise Exception("Login nach 5 Versuchen fehlgeschlagen") - today = date.today() - start = today - timedelta(days=365 * 3) +today = date.today() +start = today - timedelta(days=365 * 3) - activities = client.get_activities_by_date( - start.isoformat(), today.isoformat() +activities = client.get_activities_by_date( + start.isoformat(), today.isoformat() +) + +conn = psycopg2.connect(db_url) +cur = conn.cursor() + +cur.execute(""" + CREATE TABLE IF NOT EXISTS activities ( + id BIGINT PRIMARY KEY, + name TEXT, + type TEXT, + start_time TIMESTAMP, + duration_seconds FLOAT, + distance_meters FLOAT, + avg_hr INTEGER, + max_hr INTEGER, + avg_pace FLOAT, + vo2max FLOAT, + calories FLOAT, + elevation_gain FLOAT, + steps INTEGER, + cadence FLOAT, + hr_zone_1 FLOAT, + hr_zone_2 FLOAT, + hr_zone_3 FLOAT, + hr_zone_4 FLOAT, + hr_zone_5 FLOAT ) +""") - conn = psycopg2.connect(db_url) - cur = conn.cursor() +cur.execute(""" + ALTER TABLE activities + ADD COLUMN IF NOT EXISTS max_hr INTEGER, + ADD COLUMN IF NOT EXISTS vo2max FLOAT, + ADD COLUMN IF NOT EXISTS calories FLOAT, + ADD COLUMN IF NOT EXISTS elevation_gain FLOAT, + ADD COLUMN IF NOT EXISTS steps INTEGER, + ADD COLUMN IF NOT EXISTS cadence FLOAT, + ADD COLUMN IF NOT EXISTS hr_zone_1 FLOAT, + ADD COLUMN IF NOT EXISTS hr_zone_2 FLOAT, + ADD COLUMN IF NOT EXISTS hr_zone_3 FLOAT, + ADD COLUMN IF NOT EXISTS hr_zone_4 FLOAT, + ADD COLUMN IF NOT EXISTS hr_zone_5 FLOAT +""") +for a in activities: cur.execute(""" - CREATE TABLE IF NOT EXISTS activities ( - id BIGINT PRIMARY KEY, - name TEXT, - type TEXT, - start_time TIMESTAMP, - duration_seconds FLOAT, - distance_meters FLOAT, - avg_hr INTEGER, - max_hr INTEGER, - avg_pace FLOAT, - vo2max FLOAT, - calories FLOAT, - elevation_gain FLOAT, - steps INTEGER, - cadence FLOAT, - hr_zone_1 FLOAT, - hr_zone_2 FLOAT, - hr_zone_3 FLOAT, - hr_zone_4 FLOAT, - hr_zone_5 FLOAT - ) - """) + INSERT INTO activities + (id, name, type, start_time, duration_seconds, distance_meters, + avg_hr, max_hr, avg_pace, vo2max, calories, elevation_gain, + steps, cadence, hr_zone_1, hr_zone_2, hr_zone_3, hr_zone_4, hr_zone_5) + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) + ON CONFLICT (id) DO UPDATE SET + duration_seconds = EXCLUDED.duration_seconds, + distance_meters = EXCLUDED.distance_meters, + avg_hr = EXCLUDED.avg_hr, + max_hr = EXCLUDED.max_hr, + avg_pace = EXCLUDED.avg_pace, + vo2max = EXCLUDED.vo2max, + calories = EXCLUDED.calories, + elevation_gain = EXCLUDED.elevation_gain, + steps = EXCLUDED.steps, + cadence = EXCLUDED.cadence, + hr_zone_1 = EXCLUDED.hr_zone_1, + hr_zone_2 = EXCLUDED.hr_zone_2, + hr_zone_3 = EXCLUDED.hr_zone_3, + hr_zone_4 = EXCLUDED.hr_zone_4, + hr_zone_5 = EXCLUDED.hr_zone_5 + """, ( + a.get("activityId"), + a.get("activityName"), + a.get("activityType", {}).get("typeKey"), + a.get("startTimeLocal"), + a.get("duration"), + a.get("distance"), + a.get("averageHR"), + a.get("maxHR"), + a.get("averageSpeed"), + a.get("vO2MaxValue"), + a.get("calories"), + a.get("elevationGain"), + a.get("steps"), + a.get("averageRunningCadenceInStepsPerMinute"), + a.get("hrTimeInZone_1"), + a.get("hrTimeInZone_2"), + a.get("hrTimeInZone_3"), + a.get("hrTimeInZone_4"), + a.get("hrTimeInZone_5"), + )) - cur.execute(""" - ALTER TABLE activities - ADD COLUMN IF NOT EXISTS max_hr INTEGER, - ADD COLUMN IF NOT EXISTS vo2max FLOAT, - ADD COLUMN IF NOT EXISTS calories FLOAT, - ADD COLUMN IF NOT EXISTS elevation_gain FLOAT, - ADD COLUMN IF NOT EXISTS steps INTEGER, - ADD COLUMN IF NOT EXISTS cadence FLOAT, - ADD COLUMN IF NOT EXISTS hr_zone_1 FLOAT, - ADD COLUMN IF NOT EXISTS hr_zone_2 FLOAT, - ADD COLUMN IF NOT EXISTS hr_zone_3 FLOAT, - ADD COLUMN IF NOT EXISTS hr_zone_4 FLOAT, - ADD COLUMN IF NOT EXISTS hr_zone_5 FLOAT - """) - - for a in activities: - cur.execute(""" - INSERT INTO activities - (id, name, type, start_time, duration_seconds, distance_meters, - avg_hr, max_hr, avg_pace, vo2max, calories, elevation_gain, - steps, cadence, hr_zone_1, hr_zone_2, hr_zone_3, hr_zone_4, hr_zone_5) - VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) - ON CONFLICT (id) DO UPDATE SET - duration_seconds = EXCLUDED.duration_seconds, - distance_meters = EXCLUDED.distance_meters, - avg_hr = EXCLUDED.avg_hr, - max_hr = EXCLUDED.max_hr, - avg_pace = EXCLUDED.avg_pace, - vo2max = EXCLUDED.vo2max, - calories = EXCLUDED.calories, - elevation_gain = EXCLUDED.elevation_gain, - steps = EXCLUDED.steps, - cadence = EXCLUDED.cadence, - hr_zone_1 = EXCLUDED.hr_zone_1, - hr_zone_2 = EXCLUDED.hr_zone_2, - hr_zone_3 = EXCLUDED.hr_zone_3, - hr_zone_4 = EXCLUDED.hr_zone_4, - hr_zone_5 = EXCLUDED.hr_zone_5 - """, ( - a.get("activityId"), - a.get("activityName"), - a.get("activityType", {}).get("typeKey"), - a.get("startTimeLocal"), - a.get("duration"), - a.get("distance"), - a.get("averageHR"), - a.get("maxHR"), - a.get("averageSpeed"), - a.get("vO2MaxValue"), - a.get("calories"), - a.get("elevationGain"), - a.get("steps"), - a.get("averageRunningCadenceInStepsPerMinute"), - a.get("hrTimeInZone_1"), - a.get("hrTimeInZone_2"), - a.get("hrTimeInZone_3"), - a.get("hrTimeInZone_4"), - a.get("hrTimeInZone_5"), - )) - - conn.commit() - cur.close() - conn.close() - print(f"Synced {len(activities)} activities") +conn.commit() +cur.close() +conn.close() +print(f"Synced {len(activities)} activities")