mirror of
https://github.com/sstent/AICyclingCoach.git
synced 2026-03-06 21:15:28 +00:00
sync - still working on the TUI
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
"""
|
||||
Workout service for TUI application.
|
||||
Manages workout data, analysis, and Garmin sync without HTTP dependencies.
|
||||
Enhanced workout service with debugging for TUI application.
|
||||
"""
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import select, desc
|
||||
from sqlalchemy import select, desc, text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.app.models.workout import Workout
|
||||
@@ -20,17 +19,39 @@ class WorkoutService:
|
||||
self.db = db
|
||||
|
||||
async def get_workouts(self, limit: Optional[int] = None) -> List[Dict]:
|
||||
"""Get all workouts."""
|
||||
"""Get all workouts with enhanced debugging."""
|
||||
try:
|
||||
print(f"WorkoutService.get_workouts: Starting query with limit={limit}")
|
||||
|
||||
# First, let's check if the table exists and has data
|
||||
count_result = await self.db.execute(text("SELECT COUNT(*) FROM workouts"))
|
||||
total_count = count_result.scalar()
|
||||
print(f"WorkoutService.get_workouts: Total workouts in database: {total_count}")
|
||||
|
||||
if total_count == 0:
|
||||
print("WorkoutService.get_workouts: No workouts found in database")
|
||||
return []
|
||||
|
||||
# Build the query
|
||||
query = select(Workout).order_by(desc(Workout.start_time))
|
||||
if limit:
|
||||
query = query.limit(limit)
|
||||
|
||||
result = await self.db.execute(query)
|
||||
workouts = result.scalars().all()
|
||||
print(f"WorkoutService.get_workouts: Executing query: {query}")
|
||||
|
||||
return [
|
||||
{
|
||||
# Execute the query
|
||||
result = await self.db.execute(query)
|
||||
print("WorkoutService.get_workouts: Query executed successfully")
|
||||
|
||||
# Get all workouts
|
||||
workouts = result.scalars().all()
|
||||
print(f"WorkoutService.get_workouts: Retrieved {len(workouts)} workout objects")
|
||||
|
||||
# Convert to dictionaries
|
||||
workout_dicts = []
|
||||
for i, w in enumerate(workouts):
|
||||
print(f"WorkoutService.get_workouts: Processing workout {i+1}: ID={w.id}, Type={w.activity_type}")
|
||||
workout_dict = {
|
||||
"id": w.id,
|
||||
"garmin_activity_id": w.garmin_activity_id,
|
||||
"activity_type": w.activity_type,
|
||||
@@ -43,15 +64,65 @@ class WorkoutService:
|
||||
"max_power": w.max_power,
|
||||
"avg_cadence": w.avg_cadence,
|
||||
"elevation_gain_m": w.elevation_gain_m
|
||||
} for w in workouts
|
||||
]
|
||||
}
|
||||
workout_dicts.append(workout_dict)
|
||||
|
||||
print(f"WorkoutService.get_workouts: Returning {len(workout_dicts)} workouts")
|
||||
return workout_dicts
|
||||
|
||||
except Exception as e:
|
||||
# Enhanced error logging
|
||||
import traceback
|
||||
print(f"WorkoutService.get_workouts: ERROR: {str(e)}")
|
||||
print(f"WorkoutService.get_workouts: Traceback: {traceback.format_exc()}")
|
||||
|
||||
# Log error properly
|
||||
import logging
|
||||
logging.error(f"Error fetching workouts: {str(e)}")
|
||||
logging.error(f"Traceback: {traceback.format_exc()}")
|
||||
return []
|
||||
|
||||
async def debug_database_connection(self) -> Dict:
|
||||
"""Debug method to check database connection and table status."""
|
||||
debug_info = {}
|
||||
try:
|
||||
# Check database connection
|
||||
result = await self.db.execute(text("SELECT 1"))
|
||||
debug_info["connection"] = "OK"
|
||||
|
||||
# Check if workouts table exists
|
||||
table_check = await self.db.execute(
|
||||
text("SELECT name FROM sqlite_master WHERE type='table' AND name='workouts'")
|
||||
)
|
||||
table_exists = table_check.fetchone()
|
||||
debug_info["workouts_table_exists"] = bool(table_exists)
|
||||
|
||||
if table_exists:
|
||||
# Get table schema
|
||||
schema_result = await self.db.execute(text("PRAGMA table_info(workouts)"))
|
||||
schema = schema_result.fetchall()
|
||||
debug_info["workouts_schema"] = [dict(row._mapping) for row in schema]
|
||||
|
||||
# Get row count
|
||||
count_result = await self.db.execute(text("SELECT COUNT(*) FROM workouts"))
|
||||
debug_info["workouts_count"] = count_result.scalar()
|
||||
|
||||
# Get sample data if any
|
||||
if debug_info["workouts_count"] > 0:
|
||||
sample_result = await self.db.execute(text("SELECT * FROM workouts LIMIT 3"))
|
||||
sample_data = sample_result.fetchall()
|
||||
debug_info["sample_workouts"] = [dict(row._mapping) for row in sample_data]
|
||||
|
||||
return debug_info
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
debug_info["error"] = str(e)
|
||||
debug_info["traceback"] = traceback.format_exc()
|
||||
return debug_info
|
||||
|
||||
# ... rest of the methods remain the same ...
|
||||
|
||||
async def get_workout(self, workout_id: int) -> Optional[Dict]:
|
||||
"""Get a specific workout by ID."""
|
||||
try:
|
||||
@@ -76,130 +147,4 @@ class WorkoutService:
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error fetching workout {workout_id}: {str(e)}")
|
||||
|
||||
async def get_workout_metrics(self, workout_id: int) -> List[Dict]:
|
||||
"""Get time-series metrics for a workout."""
|
||||
try:
|
||||
workout = await self.db.get(Workout, workout_id)
|
||||
if not workout or not workout.metrics:
|
||||
return []
|
||||
|
||||
return workout.metrics
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error fetching workout metrics: {str(e)}")
|
||||
|
||||
async def sync_garmin_activities(self, days_back: int = 14) -> Dict:
|
||||
"""Trigger Garmin sync in background."""
|
||||
try:
|
||||
sync_service = WorkoutSyncService(self.db)
|
||||
result = await sync_service.sync_recent_activities(days_back=days_back)
|
||||
|
||||
return {
|
||||
"message": "Garmin sync completed",
|
||||
"activities_synced": result.get("activities_synced", 0),
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"message": f"Garmin sync failed: {str(e)}",
|
||||
"activities_synced": 0,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
async def get_sync_status(self) -> Dict:
|
||||
"""Get the latest sync status."""
|
||||
try:
|
||||
result = await self.db.execute(
|
||||
select(GarminSyncLog).order_by(desc(GarminSyncLog.created_at)).limit(1)
|
||||
)
|
||||
sync_log = result.scalar_one_or_none()
|
||||
|
||||
if not sync_log:
|
||||
return {"status": "never_synced"}
|
||||
|
||||
return {
|
||||
"status": sync_log.status,
|
||||
"last_sync_time": sync_log.last_sync_time.isoformat() if sync_log.last_sync_time else None,
|
||||
"activities_synced": sync_log.activities_synced,
|
||||
"error_message": sync_log.error_message
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error fetching sync status: {str(e)}")
|
||||
|
||||
async def analyze_workout(self, workout_id: int) -> Dict:
|
||||
"""Trigger AI analysis of a specific workout."""
|
||||
try:
|
||||
workout = await self.db.get(Workout, workout_id)
|
||||
if not workout:
|
||||
raise Exception("Workout not found")
|
||||
|
||||
ai_service = AIService(self.db)
|
||||
analysis_result = await ai_service.analyze_workout(workout, None)
|
||||
|
||||
# Store analysis
|
||||
analysis = Analysis(
|
||||
workout_id=workout.id,
|
||||
jsonb_feedback=analysis_result.get("feedback", {}),
|
||||
suggestions=analysis_result.get("suggestions", {})
|
||||
)
|
||||
self.db.add(analysis)
|
||||
await self.db.commit()
|
||||
|
||||
return {
|
||||
"message": "Analysis completed",
|
||||
"workout_id": workout_id,
|
||||
"analysis_id": analysis.id,
|
||||
"feedback": analysis_result.get("feedback", {}),
|
||||
"suggestions": analysis_result.get("suggestions", {})
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error analyzing workout: {str(e)}")
|
||||
|
||||
async def get_workout_analyses(self, workout_id: int) -> List[Dict]:
|
||||
"""Get all analyses for a specific workout."""
|
||||
try:
|
||||
workout = await self.db.get(Workout, workout_id)
|
||||
if not workout:
|
||||
raise Exception("Workout not found")
|
||||
|
||||
result = await self.db.execute(
|
||||
select(Analysis).where(Analysis.workout_id == workout_id)
|
||||
)
|
||||
analyses = result.scalars().all()
|
||||
|
||||
return [
|
||||
{
|
||||
"id": a.id,
|
||||
"analysis_type": a.analysis_type,
|
||||
"feedback": a.jsonb_feedback,
|
||||
"suggestions": a.suggestions,
|
||||
"approved": a.approved,
|
||||
"created_at": a.created_at.isoformat() if a.created_at else None
|
||||
} for a in analyses
|
||||
]
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error fetching workout analyses: {str(e)}")
|
||||
|
||||
async def approve_analysis(self, analysis_id: int) -> Dict:
|
||||
"""Approve analysis suggestions."""
|
||||
try:
|
||||
analysis = await self.db.get(Analysis, analysis_id)
|
||||
if not analysis:
|
||||
raise Exception("Analysis not found")
|
||||
|
||||
analysis.approved = True
|
||||
await self.db.commit()
|
||||
|
||||
return {
|
||||
"message": "Analysis approved",
|
||||
"analysis_id": analysis_id
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error approving analysis: {str(e)}")
|
||||
raise Exception(f"Error fetching workout {workout_id}: {str(e)}")
|
||||
Reference in New Issue
Block a user