Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts
This commit is contained in:
@@ -19,35 +19,48 @@ from services.bing_analytics_storage_service import BingAnalyticsStorageService
|
||||
import os
|
||||
|
||||
|
||||
from services.database import get_user_db_path
|
||||
|
||||
class BingAnalyticsHandler(BaseAnalyticsHandler):
|
||||
"""Handler for Bing Webmaster Tools analytics"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(PlatformType.BING)
|
||||
self.bing_service = BingOAuthService()
|
||||
# Initialize insights service
|
||||
database_url = os.getenv('DATABASE_URL', 'sqlite:///./bing_analytics.db')
|
||||
self.insights_service = BingInsightsService(database_url)
|
||||
# Storage service used in onboarding step 5
|
||||
self.storage_service = BingAnalyticsStorageService(os.getenv('DATABASE_URL', 'sqlite:///alwrity.db'))
|
||||
|
||||
def _get_storage_service(self, user_id: str) -> BingAnalyticsStorageService:
|
||||
"""Get user-specific storage service."""
|
||||
db_path = get_user_db_path(user_id)
|
||||
db_url = f'sqlite:///{db_path}'
|
||||
return BingAnalyticsStorageService(db_url)
|
||||
|
||||
def _get_insights_service(self, user_id: str) -> BingInsightsService:
|
||||
"""Get user-specific insights service."""
|
||||
# For now, insights might be in a separate DB or same.
|
||||
# User requested isolation, so same user DB is best.
|
||||
db_path = get_user_db_path(user_id)
|
||||
db_url = f'sqlite:///{db_path}'
|
||||
return BingInsightsService(db_url)
|
||||
|
||||
async def get_analytics(self, user_id: str) -> AnalyticsData:
|
||||
"""
|
||||
Get Bing Webmaster analytics data using Bing Webmaster API
|
||||
|
||||
Note: Bing Webmaster provides SEO insights and search performance data
|
||||
"""
|
||||
self.log_analytics_request(user_id, "get_analytics")
|
||||
|
||||
# Check cache first - this is an expensive operation
|
||||
# Check cache first
|
||||
cached_data = analytics_cache.get('bing_analytics', user_id)
|
||||
if cached_data:
|
||||
logger.info("Using cached Bing analytics for user {user_id}", user_id=user_id)
|
||||
logger.info(f"Using cached Bing analytics for user {user_id}")
|
||||
return AnalyticsData(**cached_data)
|
||||
|
||||
logger.info("Fetching fresh Bing analytics for user {user_id} (expensive operation)", user_id=user_id)
|
||||
logger.info(f"Fetching fresh Bing analytics for user {user_id}")
|
||||
try:
|
||||
# Get user's Bing connection status with detailed token info
|
||||
# Get services for this user
|
||||
storage_service = self._get_storage_service(user_id)
|
||||
insights_service = self._get_insights_service(user_id)
|
||||
|
||||
# Get user's Bing connection status
|
||||
token_status = self.bing_service.get_user_token_status(user_id)
|
||||
|
||||
if not token_status.get('has_active_tokens'):
|
||||
@@ -56,31 +69,24 @@ class BingAnalyticsHandler(BaseAnalyticsHandler):
|
||||
else:
|
||||
return self.create_error_response('Bing Webmaster not connected')
|
||||
|
||||
# Try once to fetch sites (may return empty if tokens are valid but no verified sites); do not block
|
||||
sites = self.bing_service.get_user_sites(user_id)
|
||||
|
||||
# Get active tokens for access token
|
||||
active_tokens = token_status.get('active_tokens', [])
|
||||
if not active_tokens:
|
||||
return self.create_error_response('No active Bing Webmaster tokens available')
|
||||
|
||||
# Get the first active token's access token
|
||||
token_info = active_tokens[0]
|
||||
access_token = token_info.get('access_token')
|
||||
|
||||
# Cache the sites for future use (even if empty)
|
||||
analytics_cache.set('bing_sites', user_id, sites or [], ttl_override=2*60*60)
|
||||
logger.info(f"Cached Bing sites for analytics for user {user_id} (TTL: 2 hours)")
|
||||
|
||||
if not access_token:
|
||||
return self.create_error_response('Bing Webmaster access token not available')
|
||||
|
||||
# Do NOT call live Bing APIs here; use stored analytics like step 5
|
||||
query_stats = {}
|
||||
try:
|
||||
# If sites available, use first; otherwise ask storage for any stored summary
|
||||
site_url_for_storage = sites[0].get('Url', '') if (sites and isinstance(sites[0], dict)) else None
|
||||
stored = self.storage_service.get_analytics_summary(user_id, site_url_for_storage, days=30)
|
||||
stored = storage_service.get_analytics_summary(user_id, site_url_for_storage, days=30)
|
||||
if stored and isinstance(stored, dict):
|
||||
query_stats = {
|
||||
'total_clicks': stored.get('summary', {}).get('total_clicks', 0),
|
||||
@@ -92,10 +98,9 @@ class BingAnalyticsHandler(BaseAnalyticsHandler):
|
||||
except Exception as e:
|
||||
logger.warning(f"Bing analytics: Failed to read stored analytics summary: {e}")
|
||||
|
||||
# Get enhanced insights from database
|
||||
insights = self._get_enhanced_insights(user_id, sites[0].get('Url', '') if sites else '')
|
||||
# Get enhanced insights
|
||||
insights = self._get_enhanced_insights_with_service(insights_service, user_id, sites[0].get('Url', '') if sites else '')
|
||||
|
||||
# Extract comprehensive site information with actual metrics
|
||||
metrics = {
|
||||
'connection_status': 'connected',
|
||||
'connected_sites': len(sites),
|
||||
@@ -111,25 +116,39 @@ class BingAnalyticsHandler(BaseAnalyticsHandler):
|
||||
'note': 'Bing Webmaster API provides SEO insights, search performance, and index status data'
|
||||
}
|
||||
|
||||
# If no stored data or no sites, return partial like step 5, else success
|
||||
if (not sites) or (metrics.get('total_impressions', 0) == 0 and metrics.get('total_clicks', 0) == 0):
|
||||
result = self.create_partial_response(metrics=metrics, error_message='Connected to Bing; waiting for stored analytics or site verification')
|
||||
else:
|
||||
result = self.create_success_response(metrics=metrics)
|
||||
|
||||
# Cache the result to avoid expensive API calls
|
||||
analytics_cache.set('bing_analytics', user_id, result.__dict__)
|
||||
logger.info("Cached Bing analytics data for user {user_id}", user_id=user_id)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
self.log_analytics_error(user_id, "get_analytics", e)
|
||||
error_result = self.create_error_response(str(e))
|
||||
|
||||
# Cache error result for shorter time to retry sooner
|
||||
analytics_cache.set('bing_analytics', user_id, error_result.__dict__, ttl_override=300) # 5 minutes
|
||||
analytics_cache.set('bing_analytics', user_id, error_result.__dict__, ttl_override=300)
|
||||
return error_result
|
||||
|
||||
def _get_enhanced_insights_with_service(self, insights_service: BingInsightsService, user_id: str, site_url: str) -> Dict[str, Any]:
|
||||
"""Get enhanced insights using provided service."""
|
||||
try:
|
||||
if not site_url:
|
||||
return {'status': 'no_site_url', 'message': 'No site URL available for insights'}
|
||||
|
||||
performance_insights = insights_service.get_performance_insights(user_id, site_url, days=30)
|
||||
seo_insights = insights_service.get_seo_insights(user_id, site_url, days=30)
|
||||
recommendations = insights_service.get_actionable_recommendations(user_id, site_url, days=30)
|
||||
|
||||
return {
|
||||
'performance': performance_insights,
|
||||
'seo': seo_insights,
|
||||
'recommendations': recommendations,
|
||||
'last_analyzed': datetime.now().isoformat()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"Error getting enhanced insights: {e}")
|
||||
return {'status': 'error', 'message': str(e)}
|
||||
|
||||
def get_connection_status(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Get Bing Webmaster connection status"""
|
||||
|
||||
@@ -17,7 +17,7 @@ from ...analytics_cache_service import AnalyticsCacheService
|
||||
class BingInsightsService:
|
||||
"""Service for generating Bing Webmaster insights and recommendations"""
|
||||
|
||||
def __init__(self, database_url: str):
|
||||
def __init__(self, database_url: Optional[str] = None):
|
||||
self.storage_service = BingAnalyticsStorageService(database_url)
|
||||
self.cache_service = AnalyticsCacheService()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user