Files
FitTrack2/FitnessSync/backend/src/api/sync.py

79 lines
3.2 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from ..models.api_token import APIToken
from ..services.sync_app import SyncApp
from ..services.garmin.client import GarminClient
from ..services.postgresql_manager import PostgreSQLManager
from sqlalchemy.orm import Session
from ..utils.config import config
import logging
import json
import garth
from garth.auth_tokens import OAuth1Token, OAuth2Token
router = APIRouter()
logger = logging.getLogger(__name__)
class SyncActivityRequest(BaseModel):
days_back: int = 30
class SyncResponse(BaseModel):
status: str
message: str
job_id: Optional[str] = None
def get_db():
db_manager = PostgreSQLManager(config.DATABASE_URL)
with db_manager.get_db_session() as session:
yield session
def _load_and_verify_garth_session(db: Session):
"""Helper to load token from DB and verify session with Garmin."""
logger.info("Loading and verifying Garmin session...")
token_record = db.query(APIToken).filter_by(token_type='garmin').first()
if not (token_record and token_record.garth_oauth1_token and token_record.garth_oauth2_token):
raise HTTPException(status_code=401, detail="Garmin token not found.")
try:
oauth1_dict = json.loads(token_record.garth_oauth1_token)
oauth2_dict = json.loads(token_record.garth_oauth2_token)
domain = oauth1_dict.get('domain')
if domain:
garth.configure(domain=domain)
garth.client.oauth1_token = OAuth1Token(**oauth1_dict)
garth.client.oauth2_token = OAuth2Token(**oauth2_dict)
garth.UserProfile.get()
logger.info("Garth session verified.")
except Exception as e:
logger.error(f"Garth session verification failed: {e}", exc_info=True)
raise HTTPException(status_code=401, detail=f"Failed to authenticate with Garmin: {e}")
@router.post("/sync/activities", response_model=SyncResponse)
def sync_activities(request: SyncActivityRequest, db: Session = Depends(get_db)):
_load_and_verify_garth_session(db)
garmin_client = GarminClient() # The client is now just a thin wrapper
sync_app = SyncApp(db_session=db, garmin_client=garmin_client)
result = sync_app.sync_activities(days_back=request.days_back)
return SyncResponse(
status=result.get("status", "completed_with_errors" if result.get("failed", 0) > 0 else "completed"),
message=f"Activity sync completed: {result.get('processed', 0)} processed, {result.get('failed', 0)} failed",
job_id=f"activity-sync-{datetime.now().strftime('%Y%m%d%H%M%S')}"
)
@router.post("/sync/metrics", response_model=SyncResponse)
def sync_metrics(db: Session = Depends(get_db)):
_load_and_verify_garth_session(db)
garmin_client = GarminClient()
sync_app = SyncApp(db_session=db, garmin_client=garmin_client)
result = sync_app.sync_health_metrics()
return SyncResponse(
status=result.get("status", "completed_with_errors" if result.get("failed", 0) > 0 else "completed"),
message=f"Health metrics sync completed: {result.get('processed', 0)} processed, {result.get('failed', 0)} failed",
job_id=f"metrics-sync-{datetime.now().strftime('%Y%m%d%H%M%S')}"
)