Onboarding Progress Service Implementation

This commit is contained in:
ajaysi
2025-10-24 17:22:06 +05:30
parent a3f25f23c9
commit caeb6e56a9
6 changed files with 509 additions and 228 deletions

View File

@@ -6,6 +6,7 @@ Manages caching of expensive comprehensive user data operations.
from typing import Dict, Any, Optional, Tuple
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
from sqlalchemy.exc import OperationalError
from sqlalchemy import and_
from loguru import logger
import json
@@ -19,6 +20,12 @@ class ComprehensiveUserDataCacheService:
def __init__(self, db_session: Session):
self.db = db_session
self.data_processor = ComprehensiveUserDataProcessor()
# Ensure table exists in dev environments where migrations may not have run yet
try:
ComprehensiveUserDataCache.__table__.create(bind=self.db.bind, checkfirst=True)
except Exception:
# Non-fatal; subsequent operations handle absence defensively
pass
async def get_cached_data(
self,
@@ -146,6 +153,10 @@ class ComprehensiveUserDataCacheService:
return None
except OperationalError as e:
# Table might not exist yet; treat as cache miss
logger.warning(f"❕ Cache table not found (get): {str(e)}")
return None
except Exception as e:
logger.error(f"❌ Error getting from cache: {str(e)}")
return None
@@ -185,6 +196,11 @@ class ComprehensiveUserDataCacheService:
f"Data Size: {len(str(data))} chars")
return True
except OperationalError as e:
# Table might not exist yet; skip storing
logger.warning(f"❕ Cache table not found (store): {str(e)}")
self.db.rollback()
return False
except Exception as e:
logger.error(f"❌ Error storing in cache: {str(e)}")
self.db.rollback()
@@ -225,6 +241,11 @@ class ComprehensiveUserDataCacheService:
return deleted_count
except OperationalError as e:
# Table might not exist yet; nothing to cleanup
logger.warning(f"❕ Cache table not found (cleanup): {str(e)}")
self.db.rollback()
return 0
except Exception as e:
logger.error(f"❌ Error cleaning up cache: {str(e)}")
self.db.rollback()
@@ -258,6 +279,15 @@ class ComprehensiveUserDataCacheService:
]
}
except OperationalError as e:
# Table might not exist yet; return empty stats to avoid noisy errors
logger.warning(f"❕ Cache table not found (stats): {str(e)}")
return {
"total_entries": 0,
"expired_entries": 0,
"valid_entries": 0,
"most_accessed": []
}
except Exception as e:
logger.error(f"❌ Error getting cache stats: {str(e)}")
return {"error": str(e)}

View File

@@ -0,0 +1,163 @@
"""
Database-only Onboarding Progress Service
Replaces file-based progress tracking with database-only implementation.
"""
from typing import Dict, Any, List, Optional
from datetime import datetime
from loguru import logger
from sqlalchemy.orm import Session
from sqlalchemy.exc import SQLAlchemyError
from services.database import SessionLocal
from services.onboarding_database_service import OnboardingDatabaseService
class OnboardingProgressService:
"""Database-only onboarding progress management."""
def __init__(self):
self.db_service = OnboardingDatabaseService()
def get_onboarding_status(self, user_id: str) -> Dict[str, Any]:
"""Get current onboarding status from database only."""
try:
db = SessionLocal()
try:
# Get session data
session = self.db_service.get_session_by_user(user_id, db)
if not session:
return {
"is_completed": False,
"current_step": 1,
"completion_percentage": 0.0,
"started_at": None,
"last_updated": None,
"completed_at": None
}
# Check if onboarding is complete
# Consider complete if either the final step is reached OR progress hit 100%
# This guards against partial writes where one field persisted but the other didn't.
is_completed = (session.current_step >= 6) or (session.progress >= 100.0)
return {
"is_completed": is_completed,
"current_step": session.current_step,
"completion_percentage": session.progress,
"started_at": session.started_at.isoformat() if session.started_at else None,
"last_updated": session.updated_at.isoformat() if session.updated_at else None,
"completed_at": session.updated_at.isoformat() if is_completed else None
}
finally:
db.close()
except Exception as e:
logger.error(f"Error getting onboarding status: {e}")
return {
"is_completed": False,
"current_step": 1,
"completion_percentage": 0.0,
"started_at": None,
"last_updated": None,
"completed_at": None
}
def update_step(self, user_id: str, step_number: int) -> bool:
"""Update current step in database."""
try:
db = SessionLocal()
try:
success = self.db_service.update_step(user_id, step_number, db)
if success:
logger.info(f"Updated user {user_id} to step {step_number}")
return success
finally:
db.close()
except Exception as e:
logger.error(f"Error updating step: {e}")
return False
def update_progress(self, user_id: str, progress_percentage: float) -> bool:
"""Update progress percentage in database."""
try:
db = SessionLocal()
try:
success = self.db_service.update_progress(user_id, progress_percentage, db)
if success:
logger.info(f"Updated user {user_id} progress to {progress_percentage}%")
return success
finally:
db.close()
except Exception as e:
logger.error(f"Error updating progress: {e}")
return False
def complete_onboarding(self, user_id: str) -> bool:
"""Mark onboarding as complete in database."""
try:
db = SessionLocal()
try:
success = self.db_service.mark_onboarding_complete(user_id, db)
if success:
logger.info(f"Marked onboarding complete for user {user_id}")
return success
finally:
db.close()
except Exception as e:
logger.error(f"Error completing onboarding: {e}")
return False
def reset_onboarding(self, user_id: str) -> bool:
"""Reset onboarding progress in database."""
try:
db = SessionLocal()
try:
# Reset to step 1, 0% progress
success = self.db_service.update_step(user_id, 1, db)
if success:
self.db_service.update_progress(user_id, 0.0, db)
logger.info(f"Reset onboarding for user {user_id}")
return success
finally:
db.close()
except Exception as e:
logger.error(f"Error resetting onboarding: {e}")
return False
def get_completion_data(self, user_id: str) -> Dict[str, Any]:
"""Get completion data for validation."""
try:
db = SessionLocal()
try:
# Get all relevant data for completion validation
session = self.db_service.get_session_by_user(user_id, db)
api_keys = self.db_service.get_api_keys(user_id, db)
website_analysis = self.db_service.get_website_analysis(user_id, db)
research_preferences = self.db_service.get_research_preferences(user_id, db)
persona_data = self.db_service.get_persona_data(user_id, db)
return {
"session": session,
"api_keys": api_keys,
"website_analysis": website_analysis,
"research_preferences": research_preferences,
"persona_data": persona_data
}
finally:
db.close()
except Exception as e:
logger.error(f"Error getting completion data: {e}")
return {}
# Global instance
_onboarding_progress_service = None
def get_onboarding_progress_service() -> OnboardingProgressService:
"""Get the global onboarding progress service instance."""
global _onboarding_progress_service
if _onboarding_progress_service is None:
_onboarding_progress_service = OnboardingProgressService()
return _onboarding_progress_service