""" Enhanced Strategy Database Service Handles database operations for enhanced content strategy functionality. """ import json import logging from typing import Dict, List, Any, Optional from datetime import datetime from sqlalchemy.orm import Session from sqlalchemy import and_, or_ # Import database models from models.enhanced_strategy_models import EnhancedContentStrategy, EnhancedAIAnalysisResult, OnboardingDataIntegration from models.enhanced_strategy_models import ContentStrategyAutofillInsights logger = logging.getLogger(__name__) class EnhancedStrategyDBService: """Database service for enhanced content strategy operations.""" def __init__(self, db: Session): self.db = db async def get_enhanced_strategy(self, strategy_id: int, user_id: Optional[str] = None) -> Optional[EnhancedContentStrategy]: """ Get an enhanced strategy by ID. Args: strategy_id: Strategy ID user_id: User ID for ownership verification (REQUIRED for security) Returns: Strategy if found and user_id matches, None otherwise """ try: query = self.db.query(EnhancedContentStrategy).filter(EnhancedContentStrategy.id == strategy_id) # CRITICAL: Always filter by user_id for security if user_id: query = query.filter(EnhancedContentStrategy.user_id == user_id) else: logger.warning(f"⚠️ get_enhanced_strategy called without user_id for strategy {strategy_id} - security risk") strategy = query.first() # Additional ownership check if strategy and user_id and strategy.user_id != user_id: logger.warning(f"⚠️ User {user_id} attempted to access strategy {strategy_id} owned by {strategy.user_id}") return None return strategy except Exception as e: logger.error(f"Error getting enhanced strategy {strategy_id}: {str(e)}") return None async def get_enhanced_strategies(self, user_id: Optional[str] = None, strategy_id: Optional[int] = None) -> List[EnhancedContentStrategy]: """Get enhanced strategies with optional filtering.""" try: query = self.db.query(EnhancedContentStrategy) if user_id: query = query.filter(EnhancedContentStrategy.user_id == user_id) if strategy_id: query = query.filter(EnhancedContentStrategy.id == strategy_id) return query.all() except Exception as e: logger.error(f"Error getting enhanced strategies: {str(e)}") return [] async def create_enhanced_strategy(self, strategy_data: Dict[str, Any]) -> Optional[EnhancedContentStrategy]: """Create a new enhanced strategy.""" try: strategy = EnhancedContentStrategy(**strategy_data) self.db.add(strategy) self.db.commit() self.db.refresh(strategy) return strategy except Exception as e: logger.error(f"Error creating enhanced strategy: {str(e)}") self.db.rollback() return None async def update_enhanced_strategy(self, strategy_id: int, update_data: Dict[str, Any]) -> Optional[EnhancedContentStrategy]: """Update an enhanced strategy.""" try: strategy = await self.get_enhanced_strategy(strategy_id) if not strategy: return None for key, value in update_data.items(): if hasattr(strategy, key): setattr(strategy, key, value) strategy.updated_at = datetime.utcnow() self.db.commit() self.db.refresh(strategy) return strategy except Exception as e: logger.error(f"Error updating enhanced strategy {strategy_id}: {str(e)}") self.db.rollback() return None async def delete_enhanced_strategy(self, strategy_id: int) -> bool: """Delete an enhanced strategy.""" try: strategy = await self.get_enhanced_strategy(strategy_id) if not strategy: return False self.db.delete(strategy) self.db.commit() return True except Exception as e: logger.error(f"Error deleting enhanced strategy {strategy_id}: {str(e)}") self.db.rollback() return False async def get_enhanced_strategies_with_analytics(self, strategy_id: Optional[int] = None) -> List[Dict[str, Any]]: """Get enhanced strategies with analytics data.""" try: strategies = await self.get_enhanced_strategies(strategy_id=strategy_id) result = [] for strategy in strategies: strategy_dict = strategy.to_dict() if hasattr(strategy, 'to_dict') else { 'id': strategy.id, 'name': strategy.name, 'industry': strategy.industry, 'user_id': strategy.user_id, 'created_at': strategy.created_at.isoformat() if strategy.created_at else None, 'updated_at': strategy.updated_at.isoformat() if strategy.updated_at else None } # Add analytics data analytics = await self.get_ai_analysis_history(strategy.id, limit=5) strategy_dict['analytics'] = analytics result.append(strategy_dict) return result except Exception as e: logger.error(f"Error getting enhanced strategies with analytics: {str(e)}") return [] async def get_ai_analysis_history(self, strategy_id: int, limit: int = 10) -> List[Dict[str, Any]]: """Get AI analysis history for a strategy.""" try: analyses = self.db.query(EnhancedAIAnalysisResult).filter( EnhancedAIAnalysisResult.strategy_id == strategy_id ).order_by(EnhancedAIAnalysisResult.created_at.desc()).limit(limit).all() return [analysis.to_dict() if hasattr(analysis, 'to_dict') else { 'id': analysis.id, 'analysis_type': analysis.analysis_type, 'insights': analysis.insights, 'recommendations': analysis.recommendations, 'created_at': analysis.created_at.isoformat() if analysis.created_at else None } for analysis in analyses] except Exception as e: logger.error(f"Error getting AI analysis history for strategy {strategy_id}: {str(e)}") return [] async def get_onboarding_integration(self, strategy_id: int) -> Optional[Dict[str, Any]]: """Get onboarding integration data for a strategy.""" try: integration = self.db.query(OnboardingDataIntegration).filter( OnboardingDataIntegration.strategy_id == strategy_id ).first() if integration: return integration.to_dict() if hasattr(integration, 'to_dict') else { 'id': integration.id, 'strategy_id': integration.strategy_id, 'data_sources': integration.data_sources, 'confidence_scores': integration.confidence_scores, 'created_at': integration.created_at.isoformat() if integration.created_at else None } return None except Exception as e: logger.error(f"Error getting onboarding integration for strategy {strategy_id}: {str(e)}") return None async def get_strategy_completion_stats(self, user_id: str) -> Dict[str, Any]: """Get completion statistics for all strategies of a user.""" try: strategies = await self.get_enhanced_strategies(user_id=user_id) total_strategies = len(strategies) completed_strategies = sum(1 for s in strategies if s.completion_percentage >= 80) avg_completion = sum(s.completion_percentage for s in strategies) / total_strategies if total_strategies > 0 else 0 return { 'total_strategies': total_strategies, 'completed_strategies': completed_strategies, 'avg_completion_percentage': avg_completion, 'user_id': user_id } except Exception as e: logger.error(f"Error getting strategy completion stats for user {user_id}: {str(e)}") return { 'total_strategies': 0, 'completed_strategies': 0, 'avg_completion_percentage': 0, 'user_id': user_id } async def search_enhanced_strategies(self, user_id: str, search_term: str) -> List[EnhancedContentStrategy]: """Search enhanced strategies by name or industry.""" try: return self.db.query(EnhancedContentStrategy).filter( and_( EnhancedContentStrategy.user_id == user_id, or_( EnhancedContentStrategy.name.ilike(f"%{search_term}%"), EnhancedContentStrategy.industry.ilike(f"%{search_term}%") ) ) ).all() except Exception as e: logger.error(f"Error searching enhanced strategies: {str(e)}") return [] async def get_strategy_export_data(self, strategy_id: int) -> Optional[Dict[str, Any]]: """Get comprehensive export data for a strategy.""" try: strategy = await self.get_enhanced_strategy(strategy_id) if not strategy: return None # Get strategy data strategy_data = strategy.to_dict() if hasattr(strategy, 'to_dict') else { 'id': strategy.id, 'name': strategy.name, 'industry': strategy.industry, 'user_id': strategy.user_id, 'created_at': strategy.created_at.isoformat() if strategy.created_at else None, 'updated_at': strategy.updated_at.isoformat() if strategy.updated_at else None } # Get analytics data analytics = await self.get_ai_analysis_history(strategy_id, limit=10) # Get onboarding integration onboarding = await self.get_onboarding_integration(strategy_id) return { 'strategy': strategy_data, 'analytics': analytics, 'onboarding_integration': onboarding, 'exported_at': datetime.utcnow().isoformat() } except Exception as e: logger.error(f"Error getting strategy export data for strategy {strategy_id}: {str(e)}") return None async def save_autofill_insights(self, *, strategy_id: int, user_id: str, payload: Dict[str, Any]) -> Optional[ContentStrategyAutofillInsights]: """Persist accepted auto-fill inputs used to create a strategy.""" try: record = ContentStrategyAutofillInsights( strategy_id=strategy_id, user_id=user_id, accepted_fields=payload.get('accepted_fields') or {}, sources=payload.get('sources') or {}, input_data_points=payload.get('input_data_points') or {}, quality_scores=payload.get('quality_scores') or {}, confidence_levels=payload.get('confidence_levels') or {}, data_freshness=payload.get('data_freshness') or {} ) self.db.add(record) self.db.commit() self.db.refresh(record) return record except Exception as e: logger.error(f"Error saving autofill insights for strategy {strategy_id}: {str(e)}") self.db.rollback() return None async def get_latest_autofill_insights(self, strategy_id: int) -> Optional[Dict[str, Any]]: """Fetch the most recent accepted auto-fill snapshot for a strategy.""" try: record = self.db.query(ContentStrategyAutofillInsights).filter( ContentStrategyAutofillInsights.strategy_id == strategy_id ).order_by(ContentStrategyAutofillInsights.created_at.desc()).first() if not record: return None return { 'id': record.id, 'strategy_id': record.strategy_id, 'user_id': record.user_id, 'accepted_fields': record.accepted_fields, 'sources': record.sources, 'input_data_points': record.input_data_points, 'quality_scores': record.quality_scores, 'confidence_levels': record.confidence_levels, 'data_freshness': record.data_freshness, 'created_at': record.created_at.isoformat() if record.created_at else None } except Exception as e: logger.error(f"Error fetching latest autofill insights for strategy {strategy_id}: {str(e)}") return None