Files
FitTrack_GarminSync/backend/src/services/central_db_service.py

167 lines
7.1 KiB
Python

import logging
from pathlib import Path
from typing import Optional, List
import httpx
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from ..schemas import GarminCredentials, Token, User, WorkoutPlan
from ..config import settings
logger = logging.getLogger(__name__)
# Define a retry strategy for CentralDB calls
CENTRAL_DB_RETRY_STRATEGY = retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_exception_type(httpx.RequestError), # Retry on network errors
reraise=True
)
class CentralDBService:
def __init__(self, base_url: str):
self.base_url = base_url
@CENTRAL_DB_RETRY_STRATEGY
async def get_user_by_email(self, email: str) -> Optional[User]:
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"{self.base_url}/users")
response.raise_for_status()
users = response.json()
for user_data in users:
if user_data["email"] == email:
return User(**user_data)
return None
except Exception as e:
logger.error(f"Error fetching user from CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def get_user(self, user_id: int) -> Optional[User]:
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"{self.base_url}/users/{user_id}")
response.raise_for_status()
return User(**response.json())
except Exception as e:
logger.error(f"Error fetching user from CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def create_user(self, user_create: dict) -> Optional[User]:
try:
async with httpx.AsyncClient() as client:
response = await client.post(f"{self.base_url}/users", json=user_create)
response.raise_for_status()
return User(**response.json())
except Exception as e:
logger.error(f"Error creating user in CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def get_token(self, user_id: int) -> Optional[Token]:
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"{self.base_url}/tokens/{user_id}")
response.raise_for_status()
return Token(**response.json())
except Exception as e:
logger.error(f"Error fetching token from CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def create_token(self, token_create: dict) -> Optional[Token]:
try:
async with httpx.AsyncClient() as client:
response = await client.post(f"{self.base_url}/tokens/", json=token_create)
response.raise_for_status()
return Token(**response.json())
except Exception as e:
logger.error(f"Error creating token in CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def update_token(self, user_id: int, token_update: dict) -> Optional[Token]:
try:
async with httpx.AsyncClient() as client:
response = await client.put(f"{self.base_url}/tokens/{user_id}", json=token_update)
response.raise_for_status()
return Token(**response.json())
except Exception as e:
logger.error(f"Error updating token in CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def get_workout_by_id(self, workout_id: int) -> Optional[WorkoutPlan]:
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"{self.base_url}/workout_plans/{workout_id}")
response.raise_for_status()
return WorkoutPlan(**response.json())
except Exception as e:
logger.error(f"Error fetching workout from CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def upload_activity_file(self, activity_id: str, file_path: Path) -> bool:
"""Uploads activity file content to CentralDB."""
try:
async with httpx.AsyncClient() as client:
with open(file_path, "rb") as f:
files = {"file": (file_path.name, f, "application/fit")} # Changed content type
user_id = 1 # Assuming single user for now
response = await client.post(
f"{self.base_url}/activities/{user_id}", # user_id as path parameter
files=files,
)
response.raise_for_status()
logger.info(f"Successfully uploaded activity {activity_id} to CentralDB.")
return True
except Exception as e:
logger.error(f"Error uploading activity {activity_id} to CentralDB: {e}", exc_info=True)
return False
@CENTRAL_DB_RETRY_STRATEGY
async def save_health_metric(self, health_metric_data: dict) -> Optional[dict]:
try:
async with httpx.AsyncClient() as client:
response = await client.post(f"{self.base_url}/health_metrics", json=health_metric_data)
response.raise_for_status()
return response.json()
except Exception as e:
logger.error(f"Error saving health metric to CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def get_garmin_credentials(self, user_id: int) -> Optional[GarminCredentials]:
try:
async with httpx.AsyncClient() as client:
response = await client.get(f"{self.base_url}/garmin_credentials/{user_id}")
response.raise_for_status()
return GarminCredentials(**response.json())
except Exception as e:
logger.error(f"Error fetching Garmin credentials from CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def create_garmin_credentials(self, user_id: int, credentials_data: dict) -> Optional[GarminCredentials]:
try:
async with httpx.AsyncClient() as client:
response = await client.post(f"{self.base_url}/garmin_credentials/{user_id}", json=credentials_data)
response.raise_for_status()
return GarminCredentials(**response.json())
except Exception as e:
logger.error(f"Error creating Garmin credentials in CentralDB: {e}")
return None
@CENTRAL_DB_RETRY_STRATEGY
async def update_garmin_credentials(self, user_id: int, credentials_data: dict) -> Optional[GarminCredentials]:
try:
async with httpx.AsyncClient() as client:
response = await client.put(f"{self.base_url}/garmin_credentials/{user_id}", json=credentials_data)
response.raise_for_status()
return GarminCredentials(**response.json())
except Exception as e:
logger.error(f"Error updating Garmin credentials in CentralDB: {e}")
return None