feat: Implement single sync job management and progress tracking

This commit is contained in:
2025-10-11 18:36:19 -07:00
parent 3819e4f5e2
commit 723ca04aa8
51 changed files with 1625 additions and 596 deletions

View File

@@ -1,9 +1,13 @@
import logging
import asyncio # Import asyncio for sleep
from typing import Optional
from garminconnect import Garmin
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from tenacity import (
retry,
retry_if_exception_type,
stop_after_attempt,
wait_exponential,
)
from ..schemas import GarminCredentials
@@ -13,34 +17,49 @@ logger = logging.getLogger(__name__)
# Define a retry strategy for Garmin login
GARMIN_LOGIN_RETRY_STRATEGY = retry(
stop=stop_after_attempt(10), # Increased attempts
wait=wait_exponential(multiplier=1, min=10, max=60), # Increased min and max wait times
retry=retry_if_exception_type(Exception), # Retry on any exception for now
reraise=True
stop=stop_after_attempt(10), # Increased attempts
wait=wait_exponential(
multiplier=1, min=10, max=60
), # Increased min and max wait times
retry=retry_if_exception_type(Exception), # Retry on any exception for now
reraise=True,
)
class GarminAuthService:
def __init__(self):
pass
@GARMIN_LOGIN_RETRY_STRATEGY # Apply retry strategy here
@GARMIN_LOGIN_RETRY_STRATEGY # Apply retry strategy here
async def _perform_login(self, username: str, password: str) -> Garmin:
"""Helper to perform the actual garminconnect login with retry."""
client = Garmin(username, password)
client.login()
return client
async def initial_login(self, username: str, password: str) -> Optional[GarminCredentials]:
async def initial_login(
self, username: str, password: str
) -> Optional[GarminCredentials]:
"""Performs initial login to Garmin Connect and returns GarminCredentials."""
try:
client = await self._perform_login(username, password) # Use the retried login helper
garmin_client = await self._perform_login(
username, password
) # Use the retried login helper
if not garmin_client:
return None
logger.info(f"Successful Garmin login for {username}")
return GarminCredentials(
# Extract tokens and cookies
garmin_credentials = GarminCredentials(
garmin_username=username,
garmin_password_plaintext=password, # Storing plaintext as per user requirement
garmin_password_plaintext=password, # Storing plaintext for re-auth, consider encryption
display_name=garmin_client.display_name,
full_name=garmin_client.full_name,
unit_system=garmin_client.unit_system,
token_dict=garmin_client.garth.dump(), # Use garth.dump() to get the token dictionary
)
return garmin_credentials
except Exception as e:
logger.error(f"Garmin initial login failed for {username}: {e}")
return None
return None