Alwrity version 0.5.4
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
Content Strategy API Module
|
||||
Modular API endpoints for content strategy functionality.
|
||||
"""
|
||||
|
||||
from .routes import router
|
||||
|
||||
__all__ = ["router"]
|
||||
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Strategy Endpoints Module
|
||||
CRUD, analytics, utility, streaming, autofill, and AI generation endpoints for content strategies.
|
||||
"""
|
||||
|
||||
from .strategy_crud import router as crud_router
|
||||
from .analytics_endpoints import router as analytics_router
|
||||
from .utility_endpoints import router as utility_router
|
||||
from .streaming_endpoints import router as streaming_router
|
||||
from .autofill_endpoints import router as autofill_router
|
||||
from .ai_generation_endpoints import router as ai_generation_router
|
||||
|
||||
__all__ = ["crud_router", "analytics_router", "utility_router", "streaming_router", "autofill_router", "ai_generation_router"]
|
||||
@@ -0,0 +1,995 @@
|
||||
"""
|
||||
AI Generation Endpoints
|
||||
Handles AI-powered strategy generation endpoints.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from loguru import logger
|
||||
from datetime import datetime
|
||||
from fastapi.responses import StreamingResponse
|
||||
import json
|
||||
|
||||
# Import database
|
||||
from services.database import get_db_session
|
||||
|
||||
# Import services
|
||||
from ....services.content_strategy.ai_generation import AIStrategyGenerator, StrategyGenerationConfig
|
||||
from ....services.enhanced_strategy_service import EnhancedStrategyService
|
||||
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
|
||||
|
||||
# Import utilities
|
||||
from ....utils.error_handlers import ContentPlanningErrorHandler
|
||||
from ....utils.response_builders import ResponseBuilder
|
||||
from ....utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
|
||||
|
||||
router = APIRouter(tags=["AI Strategy Generation"])
|
||||
|
||||
# Helper function to get database session
|
||||
def get_db():
|
||||
db = get_db_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@router.post("/generate-comprehensive-strategy")
|
||||
async def generate_comprehensive_strategy(
|
||||
user_id: int,
|
||||
strategy_name: Optional[str] = None,
|
||||
config: Optional[Dict[str, Any]] = None,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate a comprehensive AI-powered content strategy."""
|
||||
try:
|
||||
logger.info(f"🚀 Generating comprehensive AI strategy for user: {user_id}")
|
||||
|
||||
# Get user context and onboarding data
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
# Get onboarding data for context
|
||||
onboarding_data = await enhanced_service._get_onboarding_data(user_id)
|
||||
|
||||
# Build context for AI generation
|
||||
context = {
|
||||
"onboarding_data": onboarding_data,
|
||||
"user_id": user_id,
|
||||
"generation_config": config or {}
|
||||
}
|
||||
|
||||
# Create strategy generation config
|
||||
generation_config = StrategyGenerationConfig(
|
||||
include_competitive_analysis=config.get("include_competitive_analysis", True) if config else True,
|
||||
include_content_calendar=config.get("include_content_calendar", True) if config else True,
|
||||
include_performance_predictions=config.get("include_performance_predictions", True) if config else True,
|
||||
include_implementation_roadmap=config.get("include_implementation_roadmap", True) if config else True,
|
||||
include_risk_assessment=config.get("include_risk_assessment", True) if config else True,
|
||||
max_content_pieces=config.get("max_content_pieces", 50) if config else 50,
|
||||
timeline_months=config.get("timeline_months", 12) if config else 12
|
||||
)
|
||||
|
||||
# Initialize AI strategy generator
|
||||
strategy_generator = AIStrategyGenerator(generation_config)
|
||||
|
||||
# Generate comprehensive strategy
|
||||
comprehensive_strategy = await strategy_generator.generate_comprehensive_strategy(
|
||||
user_id=user_id,
|
||||
context=context,
|
||||
strategy_name=strategy_name
|
||||
)
|
||||
|
||||
logger.info(f"✅ Comprehensive AI strategy generated successfully for user: {user_id}")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Comprehensive AI strategy generated successfully",
|
||||
data=comprehensive_strategy
|
||||
)
|
||||
|
||||
except RuntimeError as e:
|
||||
logger.error(f"❌ AI service error generating comprehensive strategy: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail=f"AI service temporarily unavailable: {str(e)}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating comprehensive strategy: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "generate_comprehensive_strategy")
|
||||
|
||||
@router.post("/generate-strategy-component")
|
||||
async def generate_strategy_component(
|
||||
user_id: int,
|
||||
component_type: str,
|
||||
base_strategy: Optional[Dict[str, Any]] = None,
|
||||
context: Optional[Dict[str, Any]] = None,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate a specific strategy component using AI."""
|
||||
try:
|
||||
logger.info(f"🚀 Generating strategy component '{component_type}' for user: {user_id}")
|
||||
|
||||
# Validate component type
|
||||
valid_components = [
|
||||
"strategic_insights",
|
||||
"competitive_analysis",
|
||||
"content_calendar",
|
||||
"performance_predictions",
|
||||
"implementation_roadmap",
|
||||
"risk_assessment"
|
||||
]
|
||||
|
||||
if component_type not in valid_components:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Invalid component type. Must be one of: {valid_components}"
|
||||
)
|
||||
|
||||
# Get context if not provided
|
||||
if not context:
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
onboarding_data = await enhanced_service._get_onboarding_data(user_id)
|
||||
context = {"onboarding_data": onboarding_data, "user_id": user_id}
|
||||
|
||||
# Get base strategy if not provided
|
||||
if not base_strategy:
|
||||
# Generate base strategy using autofill
|
||||
from ....services.content_strategy.autofill.ai_structured_autofill import AIStructuredAutofillService
|
||||
autofill_service = AIStructuredAutofillService()
|
||||
autofill_result = await autofill_service.generate_autofill_fields(user_id, context)
|
||||
base_strategy = autofill_result.get("fields", {})
|
||||
|
||||
# Initialize AI strategy generator
|
||||
strategy_generator = AIStrategyGenerator()
|
||||
|
||||
# Generate specific component
|
||||
if component_type == "strategic_insights":
|
||||
component = await strategy_generator._generate_strategic_insights(base_strategy, context)
|
||||
elif component_type == "competitive_analysis":
|
||||
component = await strategy_generator._generate_competitive_analysis(base_strategy, context)
|
||||
elif component_type == "content_calendar":
|
||||
component = await strategy_generator._generate_content_calendar(base_strategy, context)
|
||||
elif component_type == "performance_predictions":
|
||||
component = await strategy_generator._generate_performance_predictions(base_strategy, context)
|
||||
elif component_type == "implementation_roadmap":
|
||||
component = await strategy_generator._generate_implementation_roadmap(base_strategy, context)
|
||||
elif component_type == "risk_assessment":
|
||||
component = await strategy_generator._generate_risk_assessment(base_strategy, context)
|
||||
|
||||
logger.info(f"✅ Strategy component '{component_type}' generated successfully for user: {user_id}")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message=f"Strategy component '{component_type}' generated successfully",
|
||||
data={
|
||||
"component_type": component_type,
|
||||
"component_data": component,
|
||||
"generated_at": datetime.utcnow().isoformat(),
|
||||
"user_id": user_id
|
||||
}
|
||||
)
|
||||
|
||||
except RuntimeError as e:
|
||||
logger.error(f"❌ AI service error generating strategy component: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail=f"AI service temporarily unavailable for {component_type}: {str(e)}"
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating strategy component: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "generate_strategy_component")
|
||||
|
||||
@router.get("/strategy-generation-status")
|
||||
async def get_strategy_generation_status(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get the status of strategy generation for a user."""
|
||||
try:
|
||||
logger.info(f"Getting strategy generation status for user: {user_id}")
|
||||
|
||||
# Get user's strategies
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
strategies_data = await enhanced_service.get_enhanced_strategies(user_id, None, db)
|
||||
|
||||
# Analyze generation status
|
||||
strategies = strategies_data.get("strategies", [])
|
||||
|
||||
status_data = {
|
||||
"user_id": user_id,
|
||||
"total_strategies": len(strategies),
|
||||
"ai_generated_strategies": len([s for s in strategies if s.get("ai_generated", False)]),
|
||||
"last_generation": None,
|
||||
"generation_stats": {
|
||||
"comprehensive_strategies": 0,
|
||||
"partial_strategies": 0,
|
||||
"manual_strategies": 0
|
||||
}
|
||||
}
|
||||
|
||||
if strategies:
|
||||
# Find most recent AI-generated strategy
|
||||
ai_strategies = [s for s in strategies if s.get("ai_generated", False)]
|
||||
if ai_strategies:
|
||||
latest_ai = max(ai_strategies, key=lambda x: x.get("created_at", ""))
|
||||
status_data["last_generation"] = latest_ai.get("created_at")
|
||||
|
||||
# Categorize strategies
|
||||
for strategy in strategies:
|
||||
if strategy.get("ai_generated", False):
|
||||
if strategy.get("comprehensive", False):
|
||||
status_data["generation_stats"]["comprehensive_strategies"] += 1
|
||||
else:
|
||||
status_data["generation_stats"]["partial_strategies"] += 1
|
||||
else:
|
||||
status_data["generation_stats"]["manual_strategies"] += 1
|
||||
|
||||
logger.info(f"✅ Strategy generation status retrieved for user: {user_id}")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Strategy generation status retrieved successfully",
|
||||
data=status_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting strategy generation status: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "get_strategy_generation_status")
|
||||
|
||||
@router.post("/optimize-existing-strategy")
|
||||
async def optimize_existing_strategy(
|
||||
strategy_id: int,
|
||||
optimization_type: str = "comprehensive",
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Optimize an existing strategy using AI."""
|
||||
try:
|
||||
logger.info(f"🚀 Optimizing existing strategy {strategy_id} with type: {optimization_type}")
|
||||
|
||||
# Get existing strategy
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
strategies_data = await enhanced_service.get_enhanced_strategies(strategy_id=strategy_id, db=db)
|
||||
|
||||
if strategies_data.get("status") == "not_found" or not strategies_data.get("strategies"):
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
existing_strategy = strategies_data["strategies"][0]
|
||||
user_id = existing_strategy.get("user_id")
|
||||
|
||||
# Get user context
|
||||
onboarding_data = await enhanced_service._get_onboarding_data(user_id)
|
||||
context = {"onboarding_data": onboarding_data, "user_id": user_id}
|
||||
|
||||
# Initialize AI strategy generator
|
||||
strategy_generator = AIStrategyGenerator()
|
||||
|
||||
# Generate optimization based on type
|
||||
if optimization_type == "comprehensive":
|
||||
# Generate comprehensive optimization
|
||||
optimized_strategy = await strategy_generator.generate_comprehensive_strategy(
|
||||
user_id=user_id,
|
||||
context=context,
|
||||
strategy_name=f"Optimized: {existing_strategy.get('name', 'Strategy')}"
|
||||
)
|
||||
else:
|
||||
# Generate specific component optimization
|
||||
component = await strategy_generator._generate_strategic_insights(existing_strategy, context)
|
||||
optimized_strategy = {
|
||||
"optimization_type": optimization_type,
|
||||
"original_strategy": existing_strategy,
|
||||
"optimization_data": component,
|
||||
"optimized_at": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
logger.info(f"✅ Strategy {strategy_id} optimized successfully")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Strategy optimized successfully",
|
||||
data=optimized_strategy
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error optimizing strategy: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "optimize_existing_strategy")
|
||||
|
||||
@router.get("/generate-comprehensive-strategy/stream")
|
||||
async def generate_comprehensive_strategy_stream(
|
||||
user_id: int,
|
||||
strategy_name: Optional[str] = None,
|
||||
config: Optional[Dict[str, Any]] = None,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Generate comprehensive AI strategy with Server-Sent Events for progress updates."""
|
||||
try:
|
||||
logger.info(f"🚀 Starting streaming AI strategy generation for user: {user_id}")
|
||||
|
||||
async def generate_strategy_stream():
|
||||
try:
|
||||
# Step 1: Get user context with educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 1,
|
||||
'message': 'Getting user context...',
|
||||
'progress': 10,
|
||||
'educational_content': {
|
||||
'title': '🔍 Analyzing Your Data',
|
||||
'description': 'We\'re gathering all your onboarding information to create a personalized strategy.',
|
||||
'details': [
|
||||
'📊 Website analysis data',
|
||||
'🎯 Research preferences',
|
||||
'🔑 API configurations',
|
||||
'📈 Historical performance metrics'
|
||||
],
|
||||
'insight': 'Your data helps us understand your business context, target audience, and competitive landscape.',
|
||||
'ai_prompt_preview': 'Analyzing user onboarding data to extract business context, audience insights, and competitive positioning...'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
onboarding_data = await enhanced_service._get_onboarding_data(user_id)
|
||||
|
||||
context = {
|
||||
"onboarding_data": onboarding_data,
|
||||
"user_id": user_id,
|
||||
"generation_config": config or {}
|
||||
}
|
||||
|
||||
# Step 2: Generate base strategy fields
|
||||
yield f"data: {json.dumps({
|
||||
'step': 2,
|
||||
'message': 'Generating base strategy fields...',
|
||||
'progress': 20,
|
||||
'educational_content': {
|
||||
'title': '🏗️ Building Foundation',
|
||||
'description': 'Creating the core strategy framework based on your business objectives.',
|
||||
'details': [
|
||||
'🎯 Business objectives mapping',
|
||||
'📊 Target metrics definition',
|
||||
'💰 Budget allocation strategy',
|
||||
'⏰ Timeline planning'
|
||||
],
|
||||
'insight': 'A solid foundation ensures your content strategy aligns with business goals and resources.',
|
||||
'ai_prompt_preview': 'Generating strategic foundation: business objectives, target metrics, budget allocation, and timeline planning...'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
# Initialize AI strategy generator
|
||||
from ....services.content_strategy.ai_generation import AIStrategyGenerator
|
||||
strategy_generator = AIStrategyGenerator()
|
||||
|
||||
# Step 3: Generate strategic insights with real-time educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 3,
|
||||
'message': 'Generating strategic insights...',
|
||||
'progress': 30,
|
||||
'educational_content': {
|
||||
'title': '🧠 Strategic Intelligence Analysis',
|
||||
'description': 'AI is analyzing your market position and identifying strategic opportunities.',
|
||||
'details': [
|
||||
'🎯 Market positioning analysis',
|
||||
'💡 Opportunity identification',
|
||||
'📈 Growth potential assessment',
|
||||
'🎪 Competitive advantage mapping'
|
||||
],
|
||||
'insight': 'Strategic insights help you understand where you stand in the market and how to differentiate.',
|
||||
'ai_prompt_preview': 'Analyzing market position, identifying strategic opportunities, assessing growth potential, and mapping competitive advantages...',
|
||||
'estimated_time': '15-20 seconds'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
try:
|
||||
# Create a custom AI service manager that emits educational content to SSE
|
||||
from services.ai_service_manager import AIServiceManager, AIServiceType
|
||||
|
||||
class SSEAIServiceManager(AIServiceManager):
|
||||
def __init__(self, sse_yield_func):
|
||||
super().__init__()
|
||||
self.sse_yield = sse_yield_func
|
||||
|
||||
async def _emit_educational_content(self, service_type: AIServiceType, status: str, error_message: str = None, processing_time: float = None):
|
||||
"""Override to emit educational content to SSE stream."""
|
||||
try:
|
||||
educational_content = self._get_educational_content(service_type, status, error_message, processing_time)
|
||||
|
||||
# Emit to SSE stream
|
||||
yield_data = {
|
||||
'type': 'educational_content',
|
||||
'service_type': service_type.value,
|
||||
'status': status,
|
||||
'educational_content': educational_content
|
||||
}
|
||||
|
||||
if processing_time:
|
||||
yield_data['processing_time'] = processing_time
|
||||
if error_message:
|
||||
yield_data['error_message'] = error_message
|
||||
|
||||
await self.sse_yield(f"data: {json.dumps(yield_data)}\n\n")
|
||||
logger.info(f"📚 Emitted educational content for {service_type.value}: {status}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error emitting educational content to SSE: {e}")
|
||||
|
||||
# Use the SSE-enabled AI service manager
|
||||
sse_ai_manager = SSEAIServiceManager(lambda data: generate_strategy_stream().__anext__())
|
||||
|
||||
# Generate strategic insights with educational content
|
||||
strategic_insights = await strategy_generator._generate_strategic_insights({}, context, sse_ai_manager)
|
||||
|
||||
yield f"data: {json.dumps({
|
||||
'step': 3,
|
||||
'message': 'Strategic insights generated successfully',
|
||||
'progress': 35,
|
||||
'success': True,
|
||||
'educational_content': {
|
||||
'title': '✅ Strategic Insights Complete',
|
||||
'description': 'Successfully identified key strategic opportunities and market positioning.',
|
||||
'achievement': f'Generated {len(strategic_insights.get("insights", []))} strategic insights',
|
||||
'next_step': 'Moving to competitive analysis...'
|
||||
}
|
||||
})}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: {json.dumps({
|
||||
'step': 3,
|
||||
'message': f'Strategic insights generation failed: {str(e)}',
|
||||
'progress': 35,
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'educational_content': {
|
||||
'title': '⚠️ Strategic Insights Issue',
|
||||
'description': 'We encountered an issue with strategic analysis, but continuing with other components.',
|
||||
'fallback': 'Will use industry best practices for strategic positioning.'
|
||||
}
|
||||
})}\n\n"
|
||||
strategic_insights = {}
|
||||
|
||||
# Step 4: Generate competitive analysis with educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 4,
|
||||
'message': 'Generating competitive analysis...',
|
||||
'progress': 40,
|
||||
'educational_content': {
|
||||
'title': '🔍 Competitive Intelligence Analysis',
|
||||
'description': 'AI is analyzing your competitors to identify gaps and opportunities.',
|
||||
'details': [
|
||||
'🏢 Competitor content strategies',
|
||||
'📊 Market gap analysis',
|
||||
'🎯 Differentiation opportunities',
|
||||
'📈 Industry trend analysis'
|
||||
],
|
||||
'insight': 'Understanding your competitors helps you find unique angles and underserved market segments.',
|
||||
'ai_prompt_preview': 'Analyzing competitor content strategies, identifying market gaps, finding differentiation opportunities, and assessing industry trends...',
|
||||
'estimated_time': '20-25 seconds'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
try:
|
||||
competitive_analysis = await strategy_generator._generate_competitive_analysis({}, context, sse_ai_manager)
|
||||
yield f"data: {json.dumps({
|
||||
'step': 4,
|
||||
'message': 'Competitive analysis generated successfully',
|
||||
'progress': 45,
|
||||
'success': True,
|
||||
'educational_content': {
|
||||
'title': '✅ Competitive Analysis Complete',
|
||||
'description': 'Successfully analyzed competitive landscape and identified market opportunities.',
|
||||
'achievement': f'Analyzed {len(competitive_analysis.get("competitors", []))} competitors',
|
||||
'next_step': 'Moving to content calendar generation...'
|
||||
}
|
||||
})}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: {json.dumps({
|
||||
'step': 4,
|
||||
'message': f'Competitive analysis generation failed: {str(e)}',
|
||||
'progress': 45,
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'educational_content': {
|
||||
'title': '⚠️ Competitive Analysis Issue',
|
||||
'description': 'We encountered an issue with competitive analysis, but continuing with other components.',
|
||||
'fallback': 'Will use industry best practices for competitive positioning.'
|
||||
}
|
||||
})}\n\n"
|
||||
competitive_analysis = {}
|
||||
|
||||
# Step 5: Generate content calendar with educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 5,
|
||||
'message': 'Generating content calendar...',
|
||||
'progress': 50,
|
||||
'educational_content': {
|
||||
'title': '📅 Content Calendar Creation',
|
||||
'description': 'AI is building a comprehensive content schedule optimized for your audience.',
|
||||
'details': [
|
||||
'📝 Content piece generation',
|
||||
'📅 Optimal publishing schedule',
|
||||
'🎯 Audience engagement timing',
|
||||
'🔄 Content repurposing strategy'
|
||||
],
|
||||
'insight': 'A well-planned content calendar ensures consistent engagement and maximizes content ROI.',
|
||||
'ai_prompt_preview': 'Generating content pieces, optimizing publishing schedule, determining audience engagement timing, and planning content repurposing...',
|
||||
'estimated_time': '25-30 seconds'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
try:
|
||||
content_calendar = await strategy_generator._generate_content_calendar({}, context, sse_ai_manager)
|
||||
yield f"data: {json.dumps({
|
||||
'step': 5,
|
||||
'message': 'Content calendar generated successfully',
|
||||
'progress': 55,
|
||||
'success': True,
|
||||
'educational_content': {
|
||||
'title': '✅ Content Calendar Complete',
|
||||
'description': 'Successfully created comprehensive content schedule.',
|
||||
'achievement': f'Generated {len(content_calendar.get("content_pieces", []))} content pieces',
|
||||
'next_step': 'Moving to performance predictions...'
|
||||
}
|
||||
})}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: {json.dumps({
|
||||
'step': 5,
|
||||
'message': f'Content calendar generation failed: {str(e)}',
|
||||
'progress': 55,
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'educational_content': {
|
||||
'title': '⚠️ Content Calendar Issue',
|
||||
'description': 'We encountered an issue with content calendar generation, but continuing with other components.',
|
||||
'fallback': 'Will use industry best practices for content scheduling.'
|
||||
}
|
||||
})}\n\n"
|
||||
content_calendar = {}
|
||||
|
||||
# Step 6: Generate performance predictions with educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 6,
|
||||
'message': 'Generating performance predictions...',
|
||||
'progress': 60,
|
||||
'educational_content': {
|
||||
'title': '📊 Performance Forecasting',
|
||||
'description': 'AI is predicting content performance and ROI based on industry data.',
|
||||
'details': [
|
||||
'📈 Traffic growth projections',
|
||||
'💰 ROI predictions',
|
||||
'🎯 Conversion rate estimates',
|
||||
'📊 Engagement metrics forecasting'
|
||||
],
|
||||
'insight': 'Performance predictions help you set realistic expectations and optimize resource allocation.',
|
||||
'ai_prompt_preview': 'Analyzing industry benchmarks, predicting traffic growth, estimating ROI, forecasting conversion rates, and projecting engagement metrics...',
|
||||
'estimated_time': '15-20 seconds'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
try:
|
||||
performance_predictions = await strategy_generator._generate_performance_predictions({}, context, sse_ai_manager)
|
||||
yield f"data: {json.dumps({
|
||||
'step': 6,
|
||||
'message': 'Performance predictions generated successfully',
|
||||
'progress': 65,
|
||||
'success': True,
|
||||
'educational_content': {
|
||||
'title': '✅ Performance Predictions Complete',
|
||||
'description': 'Successfully predicted content performance and ROI.',
|
||||
'achievement': f'Predicted {performance_predictions.get("estimated_roi", "15-25%")} ROI',
|
||||
'next_step': 'Moving to implementation roadmap...'
|
||||
}
|
||||
})}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: {json.dumps({
|
||||
'step': 6,
|
||||
'message': f'Performance predictions generation failed: {str(e)}',
|
||||
'progress': 65,
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'educational_content': {
|
||||
'title': '⚠️ Performance Predictions Issue',
|
||||
'description': 'We encountered an issue with performance predictions, but continuing with other components.',
|
||||
'fallback': 'Will use industry benchmarks for performance estimates.'
|
||||
}
|
||||
})}\n\n"
|
||||
performance_predictions = {}
|
||||
|
||||
# Step 7: Generate implementation roadmap with educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 7,
|
||||
'message': 'Generating implementation roadmap...',
|
||||
'progress': 70,
|
||||
'educational_content': {
|
||||
'title': '🗺️ Implementation Roadmap',
|
||||
'description': 'AI is creating a detailed implementation plan for your content strategy.',
|
||||
'details': [
|
||||
'📋 Task breakdown and timeline',
|
||||
'👥 Resource allocation planning',
|
||||
'🎯 Milestone definition',
|
||||
'📊 Success metric tracking'
|
||||
],
|
||||
'insight': 'A clear implementation roadmap ensures successful strategy execution and measurable results.',
|
||||
'ai_prompt_preview': 'Creating implementation roadmap: task breakdown, resource allocation, milestone planning, and success metric definition...',
|
||||
'estimated_time': '15-20 seconds'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
try:
|
||||
implementation_roadmap = await strategy_generator._generate_implementation_roadmap({}, context, sse_ai_manager)
|
||||
yield f"data: {json.dumps({
|
||||
'step': 7,
|
||||
'message': 'Implementation roadmap generated successfully',
|
||||
'progress': 75,
|
||||
'success': True,
|
||||
'educational_content': {
|
||||
'title': '✅ Implementation Roadmap Complete',
|
||||
'description': 'Successfully created detailed implementation plan.',
|
||||
'achievement': f'Planned {implementation_roadmap.get("total_duration", "12 months")} implementation timeline',
|
||||
'next_step': 'Moving to risk assessment...'
|
||||
}
|
||||
})}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: {json.dumps({
|
||||
'step': 7,
|
||||
'message': f'Implementation roadmap generation failed: {str(e)}',
|
||||
'progress': 75,
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'educational_content': {
|
||||
'title': '⚠️ Implementation Roadmap Issue',
|
||||
'description': 'We encountered an issue with implementation roadmap generation, but continuing with other components.',
|
||||
'fallback': 'Will use industry best practices for implementation planning.'
|
||||
}
|
||||
})}\n\n"
|
||||
implementation_roadmap = {}
|
||||
|
||||
# Step 8: Generate risk assessment with educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 8,
|
||||
'message': 'Generating risk assessment...',
|
||||
'progress': 80,
|
||||
'educational_content': {
|
||||
'title': '⚠️ Risk Assessment',
|
||||
'description': 'AI is identifying potential risks and mitigation strategies for your content strategy.',
|
||||
'details': [
|
||||
'🔍 Risk identification and analysis',
|
||||
'📊 Risk probability assessment',
|
||||
'🛡️ Mitigation strategy development',
|
||||
'📈 Risk monitoring framework'
|
||||
],
|
||||
'insight': 'Proactive risk assessment helps you prepare for challenges and maintain strategy effectiveness.',
|
||||
'ai_prompt_preview': 'Assessing risks: identifying potential challenges, analyzing probability and impact, developing mitigation strategies, and creating monitoring framework...',
|
||||
'estimated_time': '10-15 seconds'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
try:
|
||||
risk_assessment = await strategy_generator._generate_risk_assessment({}, context, sse_ai_manager)
|
||||
yield f"data: {json.dumps({
|
||||
'step': 8,
|
||||
'message': 'Risk assessment generated successfully',
|
||||
'progress': 85,
|
||||
'success': True,
|
||||
'educational_content': {
|
||||
'title': '✅ Risk Assessment Complete',
|
||||
'description': 'Successfully identified risks and mitigation strategies.',
|
||||
'achievement': f'Assessed {risk_assessment.get("overall_risk_level", "Medium")} risk level',
|
||||
'next_step': 'Finalizing comprehensive strategy...'
|
||||
}
|
||||
})}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: {json.dumps({
|
||||
'step': 8,
|
||||
'message': f'Risk assessment generation failed: {str(e)}',
|
||||
'progress': 85,
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'educational_content': {
|
||||
'title': '⚠️ Risk Assessment Issue',
|
||||
'description': 'We encountered an issue with risk assessment, but continuing with strategy finalization.',
|
||||
'fallback': 'Will use industry best practices for risk management.'
|
||||
}
|
||||
})}\n\n"
|
||||
risk_assessment = {}
|
||||
|
||||
# Step 9: Compile comprehensive strategy
|
||||
yield f"data: {json.dumps({
|
||||
'step': 9,
|
||||
'message': 'Compiling comprehensive strategy...',
|
||||
'progress': 90,
|
||||
'educational_content': {
|
||||
'title': '📋 Strategy Compilation',
|
||||
'description': 'AI is compiling all components into a comprehensive content strategy.',
|
||||
'details': [
|
||||
'🔗 Component integration',
|
||||
'📊 Data synthesis',
|
||||
'📝 Strategy documentation',
|
||||
'✅ Quality validation'
|
||||
],
|
||||
'insight': 'A comprehensive strategy integrates all components into a cohesive, actionable plan.',
|
||||
'ai_prompt_preview': 'Compiling comprehensive strategy: integrating all components, synthesizing data, documenting strategy, and validating quality...',
|
||||
'estimated_time': '5-10 seconds'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
# Compile the comprehensive strategy
|
||||
comprehensive_strategy = {
|
||||
"strategic_insights": strategic_insights,
|
||||
"competitive_analysis": competitive_analysis,
|
||||
"content_calendar": content_calendar,
|
||||
"performance_predictions": performance_predictions,
|
||||
"implementation_roadmap": implementation_roadmap,
|
||||
"risk_assessment": risk_assessment,
|
||||
"metadata": {
|
||||
"ai_generated": True,
|
||||
"comprehensive": True,
|
||||
"generation_timestamp": datetime.utcnow().isoformat(),
|
||||
"user_id": user_id,
|
||||
"strategy_name": strategy_name or "Enhanced Content Strategy"
|
||||
}
|
||||
}
|
||||
|
||||
# Step 10: Complete with educational content
|
||||
yield f"data: {json.dumps({
|
||||
'step': 10,
|
||||
'message': 'Strategy generation completed successfully!',
|
||||
'progress': 100,
|
||||
'success': True,
|
||||
'strategy': comprehensive_strategy,
|
||||
'educational_content': {
|
||||
'title': '🎉 Strategy Generation Complete!',
|
||||
'description': 'Your comprehensive AI-powered content strategy is ready!',
|
||||
'summary': {
|
||||
'total_components': 6,
|
||||
'successful_components': sum([
|
||||
1 if strategic_insights else 0,
|
||||
1 if competitive_analysis else 0,
|
||||
1 if content_calendar else 0,
|
||||
1 if performance_predictions else 0,
|
||||
1 if implementation_roadmap else 0,
|
||||
1 if risk_assessment else 0
|
||||
]),
|
||||
'total_content_pieces': len(content_calendar.get("content_pieces", [])),
|
||||
'estimated_roi': performance_predictions.get("estimated_roi", "15-25%"),
|
||||
'implementation_timeline': implementation_roadmap.get("total_duration", "12 months"),
|
||||
'risk_level': risk_assessment.get("overall_risk_level", "Medium")
|
||||
},
|
||||
'key_achievements': [
|
||||
'🧠 Strategic insights generated',
|
||||
'🔍 Competitive analysis completed',
|
||||
'📅 Content calendar created',
|
||||
'📊 Performance predictions calculated',
|
||||
'🗺️ Implementation roadmap planned',
|
||||
'⚠️ Risk assessment conducted'
|
||||
],
|
||||
'next_steps': [
|
||||
'Review your comprehensive strategy',
|
||||
'Customize specific components as needed',
|
||||
'Share with your team for feedback',
|
||||
'Begin implementation following the roadmap'
|
||||
],
|
||||
'ai_insights': 'Your strategy leverages advanced AI analysis of your business context, competitive landscape, and industry best practices to create a data-driven content approach.',
|
||||
'personalization_note': 'This strategy is uniquely tailored to your business based on your onboarding data, ensuring relevance and effectiveness.'
|
||||
}
|
||||
})}\n\n"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error in streaming strategy generation: {str(e)}")
|
||||
yield f"data: {json.dumps({'error': f'Strategy generation failed: {str(e)}', 'progress': 0, 'success': False})}\n\n"
|
||||
|
||||
return StreamingResponse(
|
||||
generate_strategy_stream(),
|
||||
media_type="text/plain",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "Cache-Control"
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error starting streaming strategy generation: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to start streaming strategy generation: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/ai-generation-education")
|
||||
async def get_ai_generation_education() -> Dict[str, Any]:
|
||||
"""Get educational content about the AI generation process."""
|
||||
try:
|
||||
logger.info("📚 Providing AI generation educational content")
|
||||
|
||||
educational_content = {
|
||||
"title": "🤖 AI-Powered Strategy Generation",
|
||||
"subtitle": "Understanding How We Create Your Content Strategy",
|
||||
"overview": {
|
||||
"description": "Our AI system analyzes your business data and generates a comprehensive content strategy using advanced machine learning and industry best practices.",
|
||||
"total_time": "2-3 minutes",
|
||||
"components": 6,
|
||||
"ai_model": "Google Gemini Pro",
|
||||
"personalization_level": "High"
|
||||
},
|
||||
"process_steps": [
|
||||
{
|
||||
"step": 1,
|
||||
"title": "🔍 Data Analysis",
|
||||
"description": "Analyzing your onboarding data to understand your business context",
|
||||
"duration": "5-10 seconds",
|
||||
"details": [
|
||||
"Website analysis data processing",
|
||||
"Research preferences analysis",
|
||||
"API configuration review",
|
||||
"Historical performance assessment"
|
||||
],
|
||||
"ai_prompt_example": "Analyze user onboarding data to extract business context, audience insights, and competitive positioning..."
|
||||
},
|
||||
{
|
||||
"step": 2,
|
||||
"title": "🏗️ Foundation Building",
|
||||
"description": "Creating the core strategy framework based on your objectives",
|
||||
"duration": "5-10 seconds",
|
||||
"details": [
|
||||
"Business objectives mapping",
|
||||
"Target metrics definition",
|
||||
"Budget allocation strategy",
|
||||
"Timeline planning"
|
||||
],
|
||||
"ai_prompt_example": "Generate strategic foundation: business objectives, target metrics, budget allocation, and timeline planning..."
|
||||
},
|
||||
{
|
||||
"step": 3,
|
||||
"title": "🧠 Strategic Intelligence",
|
||||
"description": "AI analyzes your market position and identifies opportunities",
|
||||
"duration": "15-20 seconds",
|
||||
"details": [
|
||||
"Market positioning analysis",
|
||||
"Opportunity identification",
|
||||
"Growth potential assessment",
|
||||
"Competitive advantage mapping"
|
||||
],
|
||||
"ai_prompt_example": "Analyze market position, identify strategic opportunities, assess growth potential, and map competitive advantages..."
|
||||
},
|
||||
{
|
||||
"step": 4,
|
||||
"title": "🔍 Competitive Intelligence",
|
||||
"description": "Analyzing competitors to identify gaps and opportunities",
|
||||
"duration": "20-25 seconds",
|
||||
"details": [
|
||||
"Competitor content strategies",
|
||||
"Market gap analysis",
|
||||
"Differentiation opportunities",
|
||||
"Industry trend analysis"
|
||||
],
|
||||
"ai_prompt_example": "Analyze competitor content strategies, identify market gaps, find differentiation opportunities, and assess industry trends..."
|
||||
},
|
||||
{
|
||||
"step": 5,
|
||||
"title": "📅 Content Calendar Creation",
|
||||
"description": "Building a comprehensive content schedule optimized for your audience",
|
||||
"duration": "25-30 seconds",
|
||||
"details": [
|
||||
"Content piece generation",
|
||||
"Optimal publishing schedule",
|
||||
"Audience engagement timing",
|
||||
"Content repurposing strategy"
|
||||
],
|
||||
"ai_prompt_example": "Generate content pieces, optimize publishing schedule, determine audience engagement timing, and plan content repurposing..."
|
||||
},
|
||||
{
|
||||
"step": 6,
|
||||
"title": "📊 Performance Forecasting",
|
||||
"description": "Predicting content performance and ROI based on industry data",
|
||||
"duration": "15-20 seconds",
|
||||
"details": [
|
||||
"Traffic growth projections",
|
||||
"ROI predictions",
|
||||
"Conversion rate estimates",
|
||||
"Engagement metrics forecasting"
|
||||
],
|
||||
"ai_prompt_example": "Analyze industry benchmarks, predict traffic growth, estimate ROI, forecast conversion rates, and project engagement metrics..."
|
||||
},
|
||||
{
|
||||
"step": 7,
|
||||
"title": "🗺️ Implementation Roadmap",
|
||||
"description": "Creating a step-by-step plan to execute your strategy",
|
||||
"duration": "15-20 seconds",
|
||||
"details": [
|
||||
"Phase-by-phase breakdown",
|
||||
"Timeline with milestones",
|
||||
"Resource allocation",
|
||||
"Success checkpoints"
|
||||
],
|
||||
"ai_prompt_example": "Create phase-by-phase breakdown, establish timeline with milestones, allocate resources, and set success checkpoints..."
|
||||
},
|
||||
{
|
||||
"step": 8,
|
||||
"title": "⚠️ Risk Assessment",
|
||||
"description": "Identifying potential challenges and creating mitigation strategies",
|
||||
"duration": "10-15 seconds",
|
||||
"details": [
|
||||
"Risk identification",
|
||||
"Risk probability analysis",
|
||||
"Mitigation strategies",
|
||||
"Contingency planning"
|
||||
],
|
||||
"ai_prompt_example": "Identify potential risks, analyze risk probabilities, develop mitigation strategies, and create contingency plans..."
|
||||
}
|
||||
],
|
||||
"ai_technology": {
|
||||
"model": "Google Gemini Pro",
|
||||
"capabilities": [
|
||||
"Advanced natural language processing",
|
||||
"Context-aware analysis",
|
||||
"Industry knowledge integration",
|
||||
"Personalized recommendations"
|
||||
],
|
||||
"data_sources": [
|
||||
"Your onboarding data",
|
||||
"Industry benchmarks",
|
||||
"Best practices database",
|
||||
"Market research insights"
|
||||
]
|
||||
},
|
||||
"personalization_features": {
|
||||
"data_points_used": [
|
||||
"Business objectives and goals",
|
||||
"Target audience demographics",
|
||||
"Industry and market context",
|
||||
"Competitive landscape",
|
||||
"Content preferences and style",
|
||||
"Budget and resource constraints"
|
||||
],
|
||||
"customization_level": "High",
|
||||
"adaptation_factors": [
|
||||
"Industry-specific insights",
|
||||
"Audience behavior patterns",
|
||||
"Competitive positioning",
|
||||
"Resource availability"
|
||||
]
|
||||
},
|
||||
"quality_assurance": {
|
||||
"validation_steps": [
|
||||
"Data completeness check",
|
||||
"Strategy coherence validation",
|
||||
"Industry alignment verification",
|
||||
"Implementation feasibility assessment"
|
||||
],
|
||||
"fallback_mechanisms": [
|
||||
"Industry best practices",
|
||||
"Standard templates",
|
||||
"Benchmark data",
|
||||
"Expert recommendations"
|
||||
]
|
||||
},
|
||||
"tips_for_users": [
|
||||
"💡 The more detailed your onboarding data, the more personalized your strategy will be",
|
||||
"📊 Review and customize the generated strategy to match your specific needs",
|
||||
"🔄 Use the strategy as a starting point and iterate based on performance",
|
||||
"📈 Monitor results and adjust the strategy as your business evolves",
|
||||
"👥 Share the strategy with your team for feedback and buy-in"
|
||||
],
|
||||
"technical_details": {
|
||||
"processing_time": "2-3 minutes total",
|
||||
"ai_calls": "8 specialized AI analyses",
|
||||
"data_processing": "Real-time onboarding data integration",
|
||||
"output_format": "Structured JSON with comprehensive strategy components",
|
||||
"scalability": "Handles multiple concurrent generations"
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="AI generation educational content retrieved successfully",
|
||||
data=educational_content
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting AI generation education: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "get_ai_generation_education")
|
||||
@@ -0,0 +1,333 @@
|
||||
"""
|
||||
Analytics Endpoints
|
||||
Handles analytics and AI analysis endpoints for enhanced content strategies.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from loguru import logger
|
||||
from datetime import datetime
|
||||
|
||||
# Import database
|
||||
from services.database import get_db_session
|
||||
|
||||
# Import services
|
||||
from ....services.enhanced_strategy_service import EnhancedStrategyService
|
||||
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
|
||||
|
||||
# Import models
|
||||
from models.enhanced_strategy_models import EnhancedContentStrategy, EnhancedAIAnalysisResult
|
||||
|
||||
# Import utilities
|
||||
from ....utils.error_handlers import ContentPlanningErrorHandler
|
||||
from ....utils.response_builders import ResponseBuilder
|
||||
from ....utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
|
||||
|
||||
router = APIRouter(tags=["Strategy Analytics"])
|
||||
|
||||
# Helper function to get database session
|
||||
def get_db():
|
||||
db = get_db_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@router.get("/{strategy_id}/analytics")
|
||||
async def get_enhanced_strategy_analytics(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get analytics data for an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Getting analytics for strategy: {strategy_id}")
|
||||
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Calculate completion statistics
|
||||
strategy.calculate_completion_percentage()
|
||||
|
||||
# Get AI analysis results
|
||||
ai_analyses = db.query(EnhancedAIAnalysisResult).filter(
|
||||
EnhancedAIAnalysisResult.strategy_id == strategy_id
|
||||
).order_by(EnhancedAIAnalysisResult.created_at.desc()).all()
|
||||
|
||||
analytics_data = {
|
||||
"strategy_id": strategy_id,
|
||||
"completion_percentage": strategy.completion_percentage,
|
||||
"total_fields": 30,
|
||||
"completed_fields": len([f for f in strategy.get_field_values() if f is not None and f != ""]),
|
||||
"ai_analyses_count": len(ai_analyses),
|
||||
"last_ai_analysis": ai_analyses[0].to_dict() if ai_analyses else None,
|
||||
"created_at": strategy.created_at.isoformat() if strategy.created_at else None,
|
||||
"updated_at": strategy.updated_at.isoformat() if strategy.updated_at else None
|
||||
}
|
||||
|
||||
logger.info(f"Retrieved analytics for strategy: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['analytics_retrieved'],
|
||||
data=analytics_data
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting strategy analytics: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_analytics")
|
||||
|
||||
@router.get("/{strategy_id}/ai-analyses")
|
||||
async def get_enhanced_strategy_ai_analysis(
|
||||
strategy_id: int,
|
||||
limit: int = Query(10, description="Number of AI analysis results to return"),
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get AI analysis results for an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Getting AI analyses for strategy: {strategy_id}, limit: {limit}")
|
||||
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Get AI analysis results
|
||||
ai_analyses = db.query(EnhancedAIAnalysisResult).filter(
|
||||
EnhancedAIAnalysisResult.strategy_id == strategy_id
|
||||
).order_by(EnhancedAIAnalysisResult.created_at.desc()).limit(limit).all()
|
||||
|
||||
analyses_data = [analysis.to_dict() for analysis in ai_analyses]
|
||||
|
||||
logger.info(f"Retrieved {len(analyses_data)} AI analyses for strategy: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['ai_analyses_retrieved'],
|
||||
data={
|
||||
"strategy_id": strategy_id,
|
||||
"analyses": analyses_data,
|
||||
"total_count": len(analyses_data)
|
||||
}
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting AI analyses: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_ai_analysis")
|
||||
|
||||
@router.get("/{strategy_id}/completion")
|
||||
async def get_enhanced_strategy_completion_stats(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get completion statistics for an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Getting completion stats for strategy: {strategy_id}")
|
||||
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Calculate completion statistics
|
||||
strategy.calculate_completion_percentage()
|
||||
|
||||
# Get field values and categorize them
|
||||
field_values = strategy.get_field_values()
|
||||
completed_fields = []
|
||||
incomplete_fields = []
|
||||
|
||||
for field_name, value in field_values.items():
|
||||
if value is not None and value != "":
|
||||
completed_fields.append(field_name)
|
||||
else:
|
||||
incomplete_fields.append(field_name)
|
||||
|
||||
completion_stats = {
|
||||
"strategy_id": strategy_id,
|
||||
"completion_percentage": strategy.completion_percentage,
|
||||
"total_fields": 30,
|
||||
"completed_fields_count": len(completed_fields),
|
||||
"incomplete_fields_count": len(incomplete_fields),
|
||||
"completed_fields": completed_fields,
|
||||
"incomplete_fields": incomplete_fields,
|
||||
"last_updated": strategy.updated_at.isoformat() if strategy.updated_at else None
|
||||
}
|
||||
|
||||
logger.info(f"Retrieved completion stats for strategy: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['completion_stats_retrieved'],
|
||||
data=completion_stats
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting completion stats: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_completion_stats")
|
||||
|
||||
@router.get("/{strategy_id}/onboarding-integration")
|
||||
async def get_enhanced_strategy_onboarding_integration(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get onboarding integration data for an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Getting onboarding integration for strategy: {strategy_id}")
|
||||
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Get onboarding integration data
|
||||
onboarding_data = strategy.onboarding_data_used if hasattr(strategy, 'onboarding_data_used') else {}
|
||||
|
||||
integration_data = {
|
||||
"strategy_id": strategy_id,
|
||||
"onboarding_integration": onboarding_data,
|
||||
"has_onboarding_data": bool(onboarding_data),
|
||||
"auto_populated_fields": onboarding_data.get('auto_populated_fields', {}),
|
||||
"data_sources": onboarding_data.get('data_sources', []),
|
||||
"integration_id": onboarding_data.get('integration_id')
|
||||
}
|
||||
|
||||
logger.info(f"Retrieved onboarding integration for strategy: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['onboarding_integration_retrieved'],
|
||||
data=integration_data
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting onboarding integration: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_onboarding_integration")
|
||||
|
||||
@router.post("/{strategy_id}/ai-recommendations")
|
||||
async def generate_enhanced_ai_recommendations(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate AI recommendations for an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Generating AI recommendations for strategy: {strategy_id}")
|
||||
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Generate AI recommendations
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
# This would call the AI service to generate recommendations
|
||||
# For now, we'll return a placeholder
|
||||
recommendations = {
|
||||
"strategy_id": strategy_id,
|
||||
"recommendations": [
|
||||
{
|
||||
"type": "content_optimization",
|
||||
"title": "Optimize Content Strategy",
|
||||
"description": "Based on your current strategy, consider focusing on pillar content and topic clusters.",
|
||||
"priority": "high",
|
||||
"estimated_impact": "Increase organic traffic by 25%"
|
||||
}
|
||||
],
|
||||
"generated_at": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
logger.info(f"Generated AI recommendations for strategy: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['ai_recommendations_generated'],
|
||||
data=recommendations
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating AI recommendations: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "generate_enhanced_ai_recommendations")
|
||||
|
||||
@router.post("/{strategy_id}/ai-analysis/regenerate")
|
||||
async def regenerate_enhanced_strategy_ai_analysis(
|
||||
strategy_id: int,
|
||||
analysis_type: str,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Regenerate AI analysis for an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Regenerating AI analysis for strategy: {strategy_id}, type: {analysis_type}")
|
||||
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Regenerate AI analysis
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
# This would call the AI service to regenerate analysis
|
||||
# For now, we'll return a placeholder
|
||||
analysis_result = {
|
||||
"strategy_id": strategy_id,
|
||||
"analysis_type": analysis_type,
|
||||
"status": "regenerated",
|
||||
"regenerated_at": datetime.utcnow().isoformat(),
|
||||
"result": {
|
||||
"insights": ["New insight 1", "New insight 2"],
|
||||
"recommendations": ["New recommendation 1", "New recommendation 2"]
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(f"Regenerated AI analysis for strategy: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['ai_analysis_regenerated'],
|
||||
data=analysis_result
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error regenerating AI analysis: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "regenerate_enhanced_strategy_ai_analysis")
|
||||
@@ -0,0 +1,199 @@
|
||||
"""
|
||||
Autofill Endpoints
|
||||
Handles autofill endpoints for enhanced content strategies.
|
||||
CRITICAL PROTECTION ZONE - These endpoints are essential for autofill functionality.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from loguru import logger
|
||||
import json
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
# Import database
|
||||
from services.database import get_db_session
|
||||
|
||||
# Import services
|
||||
from ....services.enhanced_strategy_service import EnhancedStrategyService
|
||||
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
|
||||
from ....services.content_strategy.autofill.ai_refresh import AutoFillRefreshService
|
||||
|
||||
# Import utilities
|
||||
from ....utils.error_handlers import ContentPlanningErrorHandler
|
||||
from ....utils.response_builders import ResponseBuilder
|
||||
from ....utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
|
||||
|
||||
router = APIRouter(tags=["Strategy Autofill"])
|
||||
|
||||
# Helper function to get database session
|
||||
def get_db():
|
||||
db = get_db_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
async def stream_data(data_generator):
|
||||
"""Helper function to stream data as Server-Sent Events"""
|
||||
async for chunk in data_generator:
|
||||
if isinstance(chunk, dict):
|
||||
yield f"data: {json.dumps(chunk)}\n\n"
|
||||
else:
|
||||
yield f"data: {json.dumps({'message': str(chunk)})}\n\n"
|
||||
await asyncio.sleep(0.1) # Small delay to prevent overwhelming
|
||||
|
||||
@router.post("/{strategy_id}/autofill/accept")
|
||||
async def accept_autofill_inputs(
|
||||
strategy_id: int,
|
||||
payload: Dict[str, Any],
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Persist end-user accepted auto-fill inputs and associate with the strategy."""
|
||||
try:
|
||||
logger.info(f"🚀 Accepting autofill inputs for strategy: {strategy_id}")
|
||||
user_id = int(payload.get('user_id') or 1)
|
||||
accepted_fields = payload.get('accepted_fields') or {}
|
||||
# Optional transparency bundles
|
||||
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 {}
|
||||
|
||||
if not accepted_fields:
|
||||
raise HTTPException(status_code=400, detail="accepted_fields is required")
|
||||
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
record = await db_service.save_autofill_insights(
|
||||
strategy_id=strategy_id,
|
||||
user_id=user_id,
|
||||
payload={
|
||||
'accepted_fields': accepted_fields,
|
||||
'sources': sources,
|
||||
'input_data_points': input_data_points,
|
||||
'quality_scores': quality_scores,
|
||||
'confidence_levels': confidence_levels,
|
||||
'data_freshness': data_freshness,
|
||||
}
|
||||
)
|
||||
if not record:
|
||||
raise HTTPException(status_code=500, detail="Failed to persist autofill insights")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Accepted autofill inputs persisted successfully",
|
||||
data={
|
||||
'id': record.id,
|
||||
'strategy_id': record.strategy_id,
|
||||
'user_id': record.user_id,
|
||||
'created_at': record.created_at.isoformat() if getattr(record, 'created_at', None) else None
|
||||
}
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error accepting autofill inputs: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "accept_autofill_inputs")
|
||||
|
||||
@router.get("/autofill/refresh/stream")
|
||||
async def stream_autofill_refresh(
|
||||
user_id: Optional[int] = Query(None, description="User ID to build auto-fill for"),
|
||||
use_ai: bool = Query(True, description="Use AI augmentation during refresh"),
|
||||
ai_only: bool = Query(False, description="AI-first refresh: return AI overrides when available"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""SSE endpoint to stream steps while generating a fresh auto-fill payload (no DB writes)."""
|
||||
async def refresh_generator():
|
||||
try:
|
||||
actual_user_id = user_id or 1
|
||||
start_time = datetime.utcnow()
|
||||
logger.info(f"🚀 Starting auto-fill refresh stream for user: {actual_user_id}")
|
||||
yield {"type": "status", "phase": "init", "message": "Starting…", "progress": 5}
|
||||
|
||||
refresh_service = AutoFillRefreshService(db)
|
||||
|
||||
# Phase: Collect onboarding context
|
||||
yield {"type": "progress", "phase": "context", "message": "Collecting context…", "progress": 15}
|
||||
# We deliberately do not emit DB-derived values; context is used inside the service
|
||||
|
||||
# Phase: Build prompt
|
||||
yield {"type": "progress", "phase": "prompt", "message": "Preparing prompt…", "progress": 30}
|
||||
|
||||
# Phase: AI call - run in background and heartbeat until completion
|
||||
yield {"type": "progress", "phase": "ai", "message": "Calling AI…", "progress": 45}
|
||||
|
||||
import asyncio
|
||||
ai_task = asyncio.create_task(
|
||||
refresh_service.build_fresh_payload(actual_user_id, use_ai=use_ai, ai_only=ai_only)
|
||||
)
|
||||
|
||||
# Heartbeat loop while AI is running
|
||||
heartbeat_progress = 50
|
||||
while not ai_task.done():
|
||||
elapsed = (datetime.utcnow() - start_time).total_seconds()
|
||||
heartbeat_progress = min(heartbeat_progress + 3, 85)
|
||||
yield {"type": "progress", "phase": "ai_running", "message": f"AI running… {int(elapsed)}s", "progress": heartbeat_progress}
|
||||
await asyncio.sleep(2)
|
||||
|
||||
# Retrieve result or error
|
||||
final_payload = await ai_task
|
||||
|
||||
# Phase: Validate & map
|
||||
yield {"type": "progress", "phase": "validate", "message": "Validating…", "progress": 92}
|
||||
|
||||
# Phase: Transparency
|
||||
yield {"type": "progress", "phase": "finalize", "message": "Finalizing…", "progress": 96}
|
||||
|
||||
total_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)
|
||||
meta = final_payload.get('meta') or {}
|
||||
meta.update({
|
||||
'sse_total_ms': total_ms,
|
||||
'sse_started_at': start_time.isoformat()
|
||||
})
|
||||
final_payload['meta'] = meta
|
||||
|
||||
yield {"type": "result", "status": "success", "data": final_payload, "progress": 100}
|
||||
logger.info(f"✅ Auto-fill refresh stream completed for user: {actual_user_id} in {total_ms} ms")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error in auto-fill refresh stream: {str(e)}")
|
||||
yield {"type": "error", "message": str(e), "timestamp": datetime.utcnow().isoformat()}
|
||||
|
||||
return StreamingResponse(
|
||||
stream_data(refresh_generator()),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
"Access-Control-Allow-Credentials": "true"
|
||||
}
|
||||
)
|
||||
|
||||
@router.post("/autofill/refresh")
|
||||
async def refresh_autofill(
|
||||
user_id: Optional[int] = Query(None, description="User ID to build auto-fill for"),
|
||||
use_ai: bool = Query(True, description="Use AI augmentation during refresh"),
|
||||
ai_only: bool = Query(False, description="AI-first refresh: return AI overrides when available"),
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Non-stream endpoint to return a fresh auto-fill payload (no DB writes)."""
|
||||
try:
|
||||
actual_user_id = user_id or 1
|
||||
started = datetime.utcnow()
|
||||
refresh_service = AutoFillRefreshService(db)
|
||||
payload = await refresh_service.build_fresh_payload(actual_user_id, use_ai=use_ai, ai_only=ai_only)
|
||||
total_ms = int((datetime.utcnow() - started).total_seconds() * 1000)
|
||||
meta = payload.get('meta') or {}
|
||||
meta.update({'http_total_ms': total_ms, 'http_started_at': started.isoformat()})
|
||||
payload['meta'] = meta
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Fresh auto-fill payload generated successfully",
|
||||
data=payload
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating fresh auto-fill payload: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "refresh_autofill")
|
||||
@@ -0,0 +1,278 @@
|
||||
"""
|
||||
Strategy CRUD Endpoints
|
||||
Handles CRUD operations for enhanced content strategies.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from loguru import logger
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# Import database
|
||||
from services.database import get_db_session
|
||||
|
||||
# Import services
|
||||
from ....services.enhanced_strategy_service import EnhancedStrategyService
|
||||
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
|
||||
|
||||
# Import models
|
||||
from models.enhanced_strategy_models import EnhancedContentStrategy
|
||||
|
||||
# Import utilities
|
||||
from ....utils.error_handlers import ContentPlanningErrorHandler
|
||||
from ....utils.response_builders import ResponseBuilder
|
||||
from ....utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
|
||||
|
||||
router = APIRouter(tags=["Strategy CRUD"])
|
||||
|
||||
# Helper function to get database session
|
||||
def get_db():
|
||||
db = get_db_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@router.post("/create")
|
||||
async def create_enhanced_strategy(
|
||||
strategy_data: Dict[str, Any],
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Create a new enhanced content strategy."""
|
||||
try:
|
||||
logger.info(f"Creating enhanced strategy: {strategy_data.get('name', 'Unknown')}")
|
||||
|
||||
# Validate required fields
|
||||
required_fields = ['user_id', 'name']
|
||||
for field in required_fields:
|
||||
if field not in strategy_data or not strategy_data[field]:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Missing required field: {field}"
|
||||
)
|
||||
|
||||
# Parse and validate data types
|
||||
def parse_float(value: Any) -> Optional[float]:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
try:
|
||||
return float(value)
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
def parse_int(value: Any) -> Optional[int]:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
try:
|
||||
return int(value)
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
def parse_json(value: Any) -> Optional[Any]:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
return value
|
||||
return value
|
||||
|
||||
def parse_array(value: Any) -> Optional[list]:
|
||||
if value is None or value == "":
|
||||
return []
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
parsed = json.loads(value)
|
||||
return parsed if isinstance(parsed, list) else [parsed]
|
||||
except json.JSONDecodeError:
|
||||
return [value]
|
||||
elif isinstance(value, list):
|
||||
return value
|
||||
else:
|
||||
return [value]
|
||||
|
||||
# Parse numeric fields
|
||||
numeric_fields = ['content_budget', 'team_size', 'market_share', 'ab_testing_capabilities']
|
||||
for field in numeric_fields:
|
||||
if field in strategy_data:
|
||||
strategy_data[field] = parse_float(strategy_data[field])
|
||||
|
||||
# Parse array fields
|
||||
array_fields = ['content_preferences', 'consumption_patterns', 'audience_pain_points',
|
||||
'buying_journey', 'seasonal_trends', 'engagement_metrics', 'top_competitors',
|
||||
'competitor_content_strategies', 'market_gaps', 'industry_trends',
|
||||
'emerging_trends', 'preferred_formats', 'content_mix', 'content_frequency',
|
||||
'optimal_timing', 'quality_metrics', 'editorial_guidelines', 'brand_voice',
|
||||
'traffic_sources', 'conversion_rates', 'content_roi_targets', 'target_audience',
|
||||
'content_pillars']
|
||||
|
||||
for field in array_fields:
|
||||
if field in strategy_data:
|
||||
strategy_data[field] = parse_array(strategy_data[field])
|
||||
|
||||
# Parse JSON fields
|
||||
json_fields = ['business_objectives', 'target_metrics', 'performance_metrics',
|
||||
'competitive_position', 'ai_recommendations']
|
||||
for field in json_fields:
|
||||
if field in strategy_data:
|
||||
strategy_data[field] = parse_json(strategy_data[field])
|
||||
|
||||
# Create strategy
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
result = await enhanced_service.create_enhanced_strategy(strategy_data, db)
|
||||
|
||||
logger.info(f"Enhanced strategy created successfully: {result.get('strategy_id')}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['strategy_created'],
|
||||
data=result
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating enhanced strategy: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "create_enhanced_strategy")
|
||||
|
||||
@router.get("/")
|
||||
async def get_enhanced_strategies(
|
||||
user_id: Optional[int] = Query(None, description="User ID to filter strategies"),
|
||||
strategy_id: Optional[int] = Query(None, description="Specific strategy ID"),
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get enhanced content strategies."""
|
||||
try:
|
||||
logger.info(f"Getting enhanced strategies for user: {user_id}, strategy: {strategy_id}")
|
||||
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
strategies_data = await enhanced_service.get_enhanced_strategies(user_id, strategy_id, db)
|
||||
|
||||
logger.info(f"Retrieved {strategies_data.get('total_count', 0)} strategies")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['strategies_retrieved'],
|
||||
data=strategies_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting enhanced strategies: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategies")
|
||||
|
||||
@router.get("/{strategy_id}")
|
||||
async def get_enhanced_strategy_by_id(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get a specific enhanced strategy by ID."""
|
||||
try:
|
||||
logger.info(f"Getting enhanced strategy by ID: {strategy_id}")
|
||||
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
strategies_data = await enhanced_service.get_enhanced_strategies(strategy_id=strategy_id, db=db)
|
||||
|
||||
if strategies_data.get("status") == "not_found" or not strategies_data.get("strategies"):
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
strategy = strategies_data["strategies"][0]
|
||||
|
||||
logger.info(f"Retrieved strategy: {strategy.get('name')}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['strategy_retrieved'],
|
||||
data=strategy
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting enhanced strategy by ID: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_by_id")
|
||||
|
||||
@router.put("/{strategy_id}")
|
||||
async def update_enhanced_strategy(
|
||||
strategy_id: int,
|
||||
update_data: Dict[str, Any],
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Update an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Updating enhanced strategy: {strategy_id}")
|
||||
|
||||
# Check if strategy exists
|
||||
existing_strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not existing_strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Update strategy fields
|
||||
for field, value in update_data.items():
|
||||
if hasattr(existing_strategy, field):
|
||||
setattr(existing_strategy, field, value)
|
||||
|
||||
existing_strategy.updated_at = datetime.utcnow()
|
||||
|
||||
# Save to database
|
||||
db.commit()
|
||||
db.refresh(existing_strategy)
|
||||
|
||||
logger.info(f"Enhanced strategy updated successfully: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['strategy_updated'],
|
||||
data=existing_strategy.to_dict()
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating enhanced strategy: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "update_enhanced_strategy")
|
||||
|
||||
@router.delete("/{strategy_id}")
|
||||
async def delete_enhanced_strategy(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Delete an enhanced strategy."""
|
||||
try:
|
||||
logger.info(f"Deleting enhanced strategy: {strategy_id}")
|
||||
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Enhanced strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Delete strategy
|
||||
db.delete(strategy)
|
||||
db.commit()
|
||||
|
||||
logger.info(f"Enhanced strategy deleted successfully: {strategy_id}")
|
||||
return ResponseBuilder.success_response(
|
||||
message=SUCCESS_MESSAGES['strategy_deleted'],
|
||||
data={"strategy_id": strategy_id}
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting enhanced strategy: {str(e)}")
|
||||
return ContentPlanningErrorHandler.handle_general_error(e, "delete_enhanced_strategy")
|
||||
@@ -0,0 +1,357 @@
|
||||
"""
|
||||
Streaming Endpoints
|
||||
Handles streaming endpoints for enhanced content strategies.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from loguru import logger
|
||||
import json
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
import time
|
||||
|
||||
# Import database
|
||||
from services.database import get_db_session
|
||||
|
||||
# Import services
|
||||
from ....services.enhanced_strategy_service import EnhancedStrategyService
|
||||
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
|
||||
|
||||
# Import utilities
|
||||
from ....utils.error_handlers import ContentPlanningErrorHandler
|
||||
from ....utils.response_builders import ResponseBuilder
|
||||
from ....utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
|
||||
|
||||
router = APIRouter(tags=["Strategy Streaming"])
|
||||
|
||||
# Cache for streaming endpoints (5 minutes cache)
|
||||
streaming_cache = defaultdict(dict)
|
||||
CACHE_DURATION = 300 # 5 minutes
|
||||
|
||||
def get_cached_data(cache_key: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get cached data if it exists and is not expired."""
|
||||
if cache_key in streaming_cache:
|
||||
cached_data = streaming_cache[cache_key]
|
||||
if time.time() - cached_data.get("timestamp", 0) < CACHE_DURATION:
|
||||
return cached_data.get("data")
|
||||
return None
|
||||
|
||||
def set_cached_data(cache_key: str, data: Dict[str, Any]):
|
||||
"""Set cached data with timestamp."""
|
||||
streaming_cache[cache_key] = {
|
||||
"data": data,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
|
||||
# Helper function to get database session
|
||||
def get_db():
|
||||
db = get_db_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
async def stream_data(data_generator):
|
||||
"""Helper function to stream data as Server-Sent Events"""
|
||||
async for chunk in data_generator:
|
||||
if isinstance(chunk, dict):
|
||||
yield f"data: {json.dumps(chunk)}\n\n"
|
||||
else:
|
||||
yield f"data: {json.dumps({'message': str(chunk)})}\n\n"
|
||||
await asyncio.sleep(0.1) # Small delay to prevent overwhelming
|
||||
|
||||
@router.get("/stream/strategies")
|
||||
async def stream_enhanced_strategies(
|
||||
user_id: Optional[int] = Query(None, description="User ID to filter strategies"),
|
||||
strategy_id: Optional[int] = Query(None, description="Specific strategy ID"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Stream enhanced strategies with real-time updates."""
|
||||
|
||||
async def strategy_generator():
|
||||
try:
|
||||
logger.info(f"🚀 Starting strategy stream for user: {user_id}, strategy: {strategy_id}")
|
||||
|
||||
# Send initial status
|
||||
yield {"type": "status", "message": "Starting strategy retrieval...", "timestamp": datetime.utcnow().isoformat()}
|
||||
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Querying database...", "progress": 25}
|
||||
|
||||
strategies_data = await enhanced_service.get_enhanced_strategies(user_id, strategy_id, db)
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Processing strategies...", "progress": 50}
|
||||
|
||||
if strategies_data.get("status") == "not_found":
|
||||
yield {"type": "result", "status": "not_found", "data": strategies_data}
|
||||
return
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Finalizing data...", "progress": 75}
|
||||
|
||||
# Send final result
|
||||
yield {"type": "result", "status": "success", "data": strategies_data, "progress": 100}
|
||||
|
||||
logger.info(f"✅ Strategy stream completed for user: {user_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error in strategy stream: {str(e)}")
|
||||
yield {"type": "error", "message": str(e), "timestamp": datetime.utcnow().isoformat()}
|
||||
|
||||
return StreamingResponse(
|
||||
stream_data(strategy_generator()),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
"Access-Control-Allow-Credentials": "true"
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/stream/strategic-intelligence")
|
||||
async def stream_strategic_intelligence(
|
||||
user_id: Optional[int] = Query(None, description="User ID"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Stream strategic intelligence data with real-time updates."""
|
||||
|
||||
async def intelligence_generator():
|
||||
try:
|
||||
logger.info(f"🚀 Starting strategic intelligence stream for user: {user_id}")
|
||||
|
||||
# Check cache first
|
||||
cache_key = f"strategic_intelligence_{user_id}"
|
||||
cached_data = get_cached_data(cache_key)
|
||||
if cached_data:
|
||||
logger.info(f"✅ Returning cached strategic intelligence data for user: {user_id}")
|
||||
yield {"type": "result", "status": "success", "data": cached_data, "progress": 100}
|
||||
return
|
||||
|
||||
# Send initial status
|
||||
yield {"type": "status", "message": "Loading strategic intelligence...", "timestamp": datetime.utcnow().isoformat()}
|
||||
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Retrieving strategies...", "progress": 20}
|
||||
|
||||
strategies_data = await enhanced_service.get_enhanced_strategies(user_id, None, db)
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Analyzing market positioning...", "progress": 40}
|
||||
|
||||
if strategies_data.get("status") == "not_found":
|
||||
yield {"type": "error", "status": "not_ready", "message": "No strategies found. Complete onboarding and create a strategy before generating intelligence.", "progress": 100}
|
||||
return
|
||||
|
||||
# Extract strategic intelligence from first strategy
|
||||
strategy = strategies_data.get("strategies", [{}])[0]
|
||||
|
||||
# Parse ai_recommendations if it's a JSON string
|
||||
ai_recommendations = {}
|
||||
if strategy.get("ai_recommendations"):
|
||||
try:
|
||||
if isinstance(strategy["ai_recommendations"], str):
|
||||
ai_recommendations = json.loads(strategy["ai_recommendations"])
|
||||
else:
|
||||
ai_recommendations = strategy["ai_recommendations"]
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
ai_recommendations = {}
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Processing intelligence data...", "progress": 60}
|
||||
|
||||
strategic_intelligence = {
|
||||
"market_positioning": {
|
||||
"current_position": strategy.get("competitive_position", "Challenger"),
|
||||
"target_position": "Market Leader",
|
||||
"differentiation_factors": [
|
||||
"AI-powered content optimization",
|
||||
"Data-driven strategy development",
|
||||
"Personalized user experience"
|
||||
]
|
||||
},
|
||||
"competitive_analysis": {
|
||||
"top_competitors": strategy.get("top_competitors", [])[:3] or [
|
||||
"Competitor A", "Competitor B", "Competitor C"
|
||||
],
|
||||
"competitive_advantages": [
|
||||
"Advanced AI capabilities",
|
||||
"Comprehensive data integration",
|
||||
"User-centric design"
|
||||
],
|
||||
"market_gaps": strategy.get("market_gaps", []) or [
|
||||
"AI-driven content personalization",
|
||||
"Real-time performance optimization",
|
||||
"Predictive analytics"
|
||||
]
|
||||
},
|
||||
"ai_insights": ai_recommendations.get("strategic_insights", []) or [
|
||||
"Focus on pillar content strategy",
|
||||
"Implement topic clustering",
|
||||
"Optimize for voice search"
|
||||
],
|
||||
"opportunities": [
|
||||
{
|
||||
"area": "Content Personalization",
|
||||
"potential_impact": "High",
|
||||
"implementation_timeline": "3-6 months",
|
||||
"estimated_roi": "25-40%"
|
||||
},
|
||||
{
|
||||
"area": "AI-Powered Optimization",
|
||||
"potential_impact": "Medium",
|
||||
"implementation_timeline": "6-12 months",
|
||||
"estimated_roi": "15-30%"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Cache the strategic intelligence data
|
||||
set_cached_data(cache_key, strategic_intelligence)
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Finalizing strategic intelligence...", "progress": 80}
|
||||
|
||||
# Send final result
|
||||
yield {"type": "result", "status": "success", "data": strategic_intelligence, "progress": 100}
|
||||
|
||||
logger.info(f"✅ Strategic intelligence stream completed for user: {user_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error in strategic intelligence stream: {str(e)}")
|
||||
yield {"type": "error", "message": str(e), "timestamp": datetime.utcnow().isoformat()}
|
||||
|
||||
return StreamingResponse(
|
||||
stream_data(intelligence_generator()),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
"Access-Control-Allow-Credentials": "true"
|
||||
}
|
||||
)
|
||||
|
||||
@router.get("/stream/keyword-research")
|
||||
async def stream_keyword_research(
|
||||
user_id: Optional[int] = Query(None, description="User ID"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Stream keyword research data with real-time updates."""
|
||||
|
||||
async def keyword_generator():
|
||||
try:
|
||||
logger.info(f"🚀 Starting keyword research stream for user: {user_id}")
|
||||
|
||||
# Check cache first
|
||||
cache_key = f"keyword_research_{user_id}"
|
||||
cached_data = get_cached_data(cache_key)
|
||||
if cached_data:
|
||||
logger.info(f"✅ Returning cached keyword research data for user: {user_id}")
|
||||
yield {"type": "result", "status": "success", "data": cached_data, "progress": 100}
|
||||
return
|
||||
|
||||
# Send initial status
|
||||
yield {"type": "status", "message": "Loading keyword research...", "timestamp": datetime.utcnow().isoformat()}
|
||||
|
||||
# Import gap analysis service
|
||||
from ....services.gap_analysis_service import GapAnalysisService
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Retrieving gap analyses...", "progress": 20}
|
||||
|
||||
gap_service = GapAnalysisService()
|
||||
gap_analyses = await gap_service.get_gap_analyses(user_id)
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Analyzing keyword opportunities...", "progress": 40}
|
||||
|
||||
# Handle case where gap_analyses is 0, None, or empty
|
||||
if not gap_analyses or gap_analyses == 0 or len(gap_analyses) == 0:
|
||||
yield {"type": "error", "status": "not_ready", "message": "No keyword research data available. Connect data sources or run analysis first.", "progress": 100}
|
||||
return
|
||||
|
||||
# Extract keyword data from first gap analysis
|
||||
gap_analysis = gap_analyses[0] if isinstance(gap_analyses, list) else gap_analyses
|
||||
|
||||
# Parse analysis_results if it's a JSON string
|
||||
analysis_results = {}
|
||||
if gap_analysis.get("analysis_results"):
|
||||
try:
|
||||
if isinstance(gap_analysis["analysis_results"], str):
|
||||
analysis_results = json.loads(gap_analysis["analysis_results"])
|
||||
else:
|
||||
analysis_results = gap_analysis["analysis_results"]
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
analysis_results = {}
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Processing keyword data...", "progress": 60}
|
||||
|
||||
keyword_data = {
|
||||
"trend_analysis": {
|
||||
"high_volume_keywords": analysis_results.get("opportunities", [])[:3] or [
|
||||
{"keyword": "AI marketing automation", "volume": "10K-100K", "difficulty": "Medium"},
|
||||
{"keyword": "content strategy 2024", "volume": "1K-10K", "difficulty": "Low"},
|
||||
{"keyword": "digital marketing trends", "volume": "10K-100K", "difficulty": "High"}
|
||||
],
|
||||
"trending_keywords": [
|
||||
{"keyword": "AI content generation", "growth": "+45%", "opportunity": "High"},
|
||||
{"keyword": "voice search optimization", "growth": "+32%", "opportunity": "Medium"},
|
||||
{"keyword": "video marketing strategy", "growth": "+28%", "opportunity": "High"}
|
||||
]
|
||||
},
|
||||
"intent_analysis": {
|
||||
"informational": ["how to", "what is", "guide to"],
|
||||
"navigational": ["company name", "brand name", "website"],
|
||||
"transactional": ["buy", "purchase", "download", "sign up"]
|
||||
},
|
||||
"opportunities": analysis_results.get("opportunities", []) or [
|
||||
{"keyword": "AI content tools", "search_volume": "5K-10K", "competition": "Low", "cpc": "$2.50"},
|
||||
{"keyword": "content marketing ROI", "search_volume": "1K-5K", "competition": "Medium", "cpc": "$4.20"},
|
||||
{"keyword": "social media strategy", "search_volume": "10K-50K", "competition": "High", "cpc": "$3.80"}
|
||||
]
|
||||
}
|
||||
|
||||
# Cache the keyword data
|
||||
set_cached_data(cache_key, keyword_data)
|
||||
|
||||
# Send progress update
|
||||
yield {"type": "progress", "message": "Finalizing keyword research...", "progress": 80}
|
||||
|
||||
# Send final result
|
||||
yield {"type": "result", "status": "success", "data": keyword_data, "progress": 100}
|
||||
|
||||
logger.info(f"✅ Keyword research stream completed for user: {user_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error in keyword research stream: {str(e)}")
|
||||
yield {"type": "error", "message": str(e), "timestamp": datetime.utcnow().isoformat()}
|
||||
|
||||
return StreamingResponse(
|
||||
stream_data(keyword_generator()),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
"Access-Control-Allow-Credentials": "true"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,237 @@
|
||||
"""
|
||||
Utility Endpoints
|
||||
Handles utility endpoints for enhanced content strategies.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from loguru import logger
|
||||
|
||||
# Import database
|
||||
from services.database import get_db_session
|
||||
|
||||
# Import services
|
||||
from ....services.enhanced_strategy_service import EnhancedStrategyService
|
||||
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
|
||||
|
||||
# Import utilities
|
||||
from ....utils.error_handlers import ContentPlanningErrorHandler
|
||||
from ....utils.response_builders import ResponseBuilder
|
||||
from ....utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
|
||||
|
||||
router = APIRouter(tags=["Strategy Utilities"])
|
||||
|
||||
# Helper function to get database session
|
||||
def get_db():
|
||||
db = get_db_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@router.get("/onboarding-data")
|
||||
async def get_onboarding_data(
|
||||
user_id: Optional[int] = Query(None, description="User ID to get onboarding data for"),
|
||||
db: Session = Depends(get_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""Get onboarding data for enhanced strategy auto-population."""
|
||||
try:
|
||||
logger.info(f"🚀 Getting onboarding data for user: {user_id}")
|
||||
|
||||
db_service = EnhancedStrategyDBService(db)
|
||||
enhanced_service = EnhancedStrategyService(db_service)
|
||||
|
||||
# Ensure we have a valid user_id
|
||||
actual_user_id = user_id or 1
|
||||
onboarding_data = await enhanced_service._get_onboarding_data(actual_user_id)
|
||||
|
||||
logger.info(f"✅ Onboarding data retrieved successfully for user: {actual_user_id}")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Onboarding data retrieved successfully",
|
||||
data=onboarding_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting onboarding data: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "get_onboarding_data")
|
||||
|
||||
@router.get("/tooltips")
|
||||
async def get_enhanced_strategy_tooltips() -> Dict[str, Any]:
|
||||
"""Get tooltip data for enhanced strategy fields."""
|
||||
try:
|
||||
logger.info("🚀 Getting enhanced strategy tooltips")
|
||||
|
||||
# Mock tooltip data - in real implementation, this would come from a database
|
||||
tooltip_data = {
|
||||
"business_objectives": {
|
||||
"title": "Business Objectives",
|
||||
"description": "Define your primary and secondary business goals that content will support.",
|
||||
"examples": ["Increase brand awareness by 25%", "Generate 100 qualified leads per month"],
|
||||
"best_practices": ["Be specific and measurable", "Align with overall business strategy"]
|
||||
},
|
||||
"target_metrics": {
|
||||
"title": "Target Metrics",
|
||||
"description": "Specify the KPIs that will measure content strategy success.",
|
||||
"examples": ["Traffic growth: 30%", "Engagement rate: 5%", "Conversion rate: 2%"],
|
||||
"best_practices": ["Set realistic targets", "Track both leading and lagging indicators"]
|
||||
},
|
||||
"content_budget": {
|
||||
"title": "Content Budget",
|
||||
"description": "Define your allocated budget for content creation and distribution.",
|
||||
"examples": ["$10,000 per month", "15% of marketing budget"],
|
||||
"best_practices": ["Include both creation and distribution costs", "Plan for seasonal variations"]
|
||||
},
|
||||
"team_size": {
|
||||
"title": "Team Size",
|
||||
"description": "Number of team members dedicated to content creation and management.",
|
||||
"examples": ["3 content creators", "1 content manager", "2 designers"],
|
||||
"best_practices": ["Consider skill sets and workload", "Plan for growth"]
|
||||
},
|
||||
"implementation_timeline": {
|
||||
"title": "Implementation Timeline",
|
||||
"description": "Timeline for implementing your content strategy.",
|
||||
"examples": ["3 months for setup", "6 months for full implementation"],
|
||||
"best_practices": ["Set realistic milestones", "Allow for iteration"]
|
||||
},
|
||||
"market_share": {
|
||||
"title": "Market Share",
|
||||
"description": "Your current market share and target market share.",
|
||||
"examples": ["Current: 5%", "Target: 15%"],
|
||||
"best_practices": ["Use reliable data sources", "Set achievable targets"]
|
||||
},
|
||||
"competitive_position": {
|
||||
"title": "Competitive Position",
|
||||
"description": "Your position relative to competitors in the market.",
|
||||
"examples": ["Market leader", "Challenger", "Niche player"],
|
||||
"best_practices": ["Be honest about your position", "Identify opportunities"]
|
||||
},
|
||||
"performance_metrics": {
|
||||
"title": "Performance Metrics",
|
||||
"description": "Key metrics to track content performance.",
|
||||
"examples": ["Organic traffic", "Engagement rate", "Conversion rate"],
|
||||
"best_practices": ["Focus on actionable metrics", "Set up proper tracking"]
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("✅ Enhanced strategy tooltips retrieved successfully")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Enhanced strategy tooltips retrieved successfully",
|
||||
data=tooltip_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting enhanced strategy tooltips: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_tooltips")
|
||||
|
||||
@router.get("/disclosure-steps")
|
||||
async def get_enhanced_strategy_disclosure_steps() -> Dict[str, Any]:
|
||||
"""Get progressive disclosure steps for enhanced strategy."""
|
||||
try:
|
||||
logger.info("🚀 Getting enhanced strategy disclosure steps")
|
||||
|
||||
# Progressive disclosure steps configuration
|
||||
disclosure_steps = [
|
||||
{
|
||||
"id": "business_context",
|
||||
"title": "Business Context",
|
||||
"description": "Define your business objectives and context",
|
||||
"fields": ["business_objectives", "target_metrics", "content_budget", "team_size", "implementation_timeline", "market_share", "competitive_position", "performance_metrics"],
|
||||
"is_complete": False,
|
||||
"is_visible": True,
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"id": "audience_intelligence",
|
||||
"title": "Audience Intelligence",
|
||||
"description": "Understand your target audience",
|
||||
"fields": ["content_preferences", "consumption_patterns", "audience_pain_points", "buying_journey", "seasonal_trends", "engagement_metrics"],
|
||||
"is_complete": False,
|
||||
"is_visible": False,
|
||||
"dependencies": ["business_context"]
|
||||
},
|
||||
{
|
||||
"id": "competitive_intelligence",
|
||||
"title": "Competitive Intelligence",
|
||||
"description": "Analyze your competitive landscape",
|
||||
"fields": ["top_competitors", "competitor_content_strategies", "market_gaps", "industry_trends", "emerging_trends"],
|
||||
"is_complete": False,
|
||||
"is_visible": False,
|
||||
"dependencies": ["audience_intelligence"]
|
||||
},
|
||||
{
|
||||
"id": "content_strategy",
|
||||
"title": "Content Strategy",
|
||||
"description": "Define your content approach",
|
||||
"fields": ["preferred_formats", "content_mix", "content_frequency", "optimal_timing", "quality_metrics", "editorial_guidelines", "brand_voice"],
|
||||
"is_complete": False,
|
||||
"is_visible": False,
|
||||
"dependencies": ["competitive_intelligence"]
|
||||
},
|
||||
{
|
||||
"id": "distribution_channels",
|
||||
"title": "Distribution Channels",
|
||||
"description": "Plan your content distribution",
|
||||
"fields": ["traffic_sources", "conversion_rates", "content_roi_targets"],
|
||||
"is_complete": False,
|
||||
"is_visible": False,
|
||||
"dependencies": ["content_strategy"]
|
||||
},
|
||||
{
|
||||
"id": "target_audience",
|
||||
"title": "Target Audience",
|
||||
"description": "Define your target audience segments",
|
||||
"fields": ["target_audience", "content_pillars"],
|
||||
"is_complete": False,
|
||||
"is_visible": False,
|
||||
"dependencies": ["distribution_channels"]
|
||||
}
|
||||
]
|
||||
|
||||
logger.info("✅ Enhanced strategy disclosure steps retrieved successfully")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Enhanced strategy disclosure steps retrieved successfully",
|
||||
data=disclosure_steps
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting enhanced strategy disclosure steps: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_disclosure_steps")
|
||||
|
||||
@router.post("/cache/clear")
|
||||
async def clear_streaming_cache(
|
||||
user_id: Optional[int] = Query(None, description="User ID to clear cache for")
|
||||
):
|
||||
"""Clear streaming cache for a specific user or all users."""
|
||||
try:
|
||||
logger.info(f"🚀 Clearing streaming cache for user: {user_id}")
|
||||
|
||||
# Import the cache from the streaming endpoints module
|
||||
from .streaming_endpoints import streaming_cache
|
||||
|
||||
if user_id:
|
||||
# Clear cache for specific user
|
||||
cache_keys_to_remove = [
|
||||
f"strategic_intelligence_{user_id}",
|
||||
f"keyword_research_{user_id}"
|
||||
]
|
||||
for key in cache_keys_to_remove:
|
||||
if key in streaming_cache:
|
||||
del streaming_cache[key]
|
||||
logger.info(f"✅ Cleared cache for key: {key}")
|
||||
else:
|
||||
# Clear all cache
|
||||
streaming_cache.clear()
|
||||
logger.info("✅ Cleared all streaming cache")
|
||||
|
||||
return ResponseBuilder.create_success_response(
|
||||
message="Streaming cache cleared successfully",
|
||||
data={"cleared_for_user": user_id}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error clearing streaming cache: {str(e)}")
|
||||
raise ContentPlanningErrorHandler.handle_general_error(e, "clear_streaming_cache")
|
||||
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
Strategy Middleware Module
|
||||
Validation and error handling middleware for content strategies.
|
||||
"""
|
||||
|
||||
# Future middleware modules will be imported here
|
||||
__all__ = []
|
||||
25
backend/api/content_planning/api/content_strategy/routes.py
Normal file
25
backend/api/content_planning/api/content_strategy/routes.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Content Strategy Routes
|
||||
Main router that includes all content strategy endpoint modules.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
# Import endpoint modules
|
||||
from .endpoints.strategy_crud import router as crud_router
|
||||
from .endpoints.analytics_endpoints import router as analytics_router
|
||||
from .endpoints.utility_endpoints import router as utility_router
|
||||
from .endpoints.streaming_endpoints import router as streaming_router
|
||||
from .endpoints.autofill_endpoints import router as autofill_router
|
||||
from .endpoints.ai_generation_endpoints import router as ai_generation_router
|
||||
|
||||
# Create main router
|
||||
router = APIRouter(prefix="/content-strategy", tags=["Content Strategy"])
|
||||
|
||||
# Include all endpoint routers
|
||||
router.include_router(crud_router, prefix="/strategies")
|
||||
router.include_router(analytics_router, prefix="/strategies")
|
||||
router.include_router(utility_router, prefix="")
|
||||
router.include_router(streaming_router, prefix="")
|
||||
router.include_router(autofill_router, prefix="/strategies")
|
||||
router.include_router(ai_generation_router, prefix="/ai-generation")
|
||||
@@ -14,6 +14,9 @@ from .routes import strategies, calendar_events, gap_analysis, ai_analytics, cal
|
||||
# Import enhanced strategy routes
|
||||
from .enhanced_strategy_routes import router as enhanced_strategy_router
|
||||
|
||||
# Import content strategy routes
|
||||
from .content_strategy.routes import router as content_strategy_router
|
||||
|
||||
# Create main router
|
||||
router = APIRouter(prefix="/api/content-planning", tags=["content-planning"])
|
||||
|
||||
@@ -28,6 +31,9 @@ router.include_router(health_monitoring.router)
|
||||
# Include enhanced strategy routes with correct prefix
|
||||
router.include_router(enhanced_strategy_router, prefix="/enhanced-strategies")
|
||||
|
||||
# Include content strategy routes
|
||||
router.include_router(content_strategy_router)
|
||||
|
||||
# Add health check endpoint
|
||||
@router.get("/health")
|
||||
async def content_planning_health_check():
|
||||
|
||||
@@ -1,18 +1,38 @@
|
||||
"""
|
||||
AI Analysis Module
|
||||
AI recommendation generation and analysis.
|
||||
AI-powered analysis and recommendations for content strategy.
|
||||
"""
|
||||
|
||||
from .ai_recommendations import AIRecommendationsService
|
||||
from .quality_validation import QualityValidationService
|
||||
from .prompt_engineering import PromptEngineeringService
|
||||
from .strategic_intelligence_analyzer import StrategicIntelligenceAnalyzer
|
||||
from .content_distribution_analyzer import ContentDistributionAnalyzer
|
||||
from .prompt_engineering import PromptEngineeringService
|
||||
from .strategy_analyzer import (
|
||||
StrategyAnalyzer,
|
||||
generate_comprehensive_ai_recommendations,
|
||||
generate_specialized_recommendations,
|
||||
create_specialized_prompt,
|
||||
call_ai_service,
|
||||
parse_ai_response,
|
||||
get_fallback_recommendations,
|
||||
get_latest_ai_analysis,
|
||||
get_onboarding_integration
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'AIRecommendationsService',
|
||||
'QualityValidationService',
|
||||
'PromptEngineeringService',
|
||||
'StrategicIntelligenceAnalyzer',
|
||||
'ContentDistributionAnalyzer'
|
||||
'ContentDistributionAnalyzer',
|
||||
'PromptEngineeringService',
|
||||
'StrategyAnalyzer',
|
||||
'generate_comprehensive_ai_recommendations',
|
||||
'generate_specialized_recommendations',
|
||||
'create_specialized_prompt',
|
||||
'call_ai_service',
|
||||
'parse_ai_response',
|
||||
'get_fallback_recommendations',
|
||||
'get_latest_ai_analysis',
|
||||
'get_onboarding_integration'
|
||||
]
|
||||
@@ -0,0 +1,629 @@
|
||||
"""
|
||||
Strategy analyzer for AI-powered content strategy recommendations.
|
||||
Provides comprehensive AI analysis functions for content strategy generation,
|
||||
including specialized prompts, response parsing, and recommendation processing.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Any, Optional
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models.enhanced_strategy_models import EnhancedContentStrategy, EnhancedAIAnalysisResult
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StrategyAnalyzer:
|
||||
"""AI-powered strategy analyzer for content strategy recommendations."""
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
# Performance optimization settings
|
||||
self.prompt_versions = {
|
||||
'comprehensive_strategy': 'v2.1',
|
||||
'audience_intelligence': 'v2.0',
|
||||
'competitive_intelligence': 'v2.0',
|
||||
'performance_optimization': 'v2.1',
|
||||
'content_calendar_optimization': 'v2.0'
|
||||
}
|
||||
|
||||
self.quality_thresholds = {
|
||||
'min_confidence': 0.7,
|
||||
'min_completeness': 0.8,
|
||||
'max_response_time': 30.0 # seconds
|
||||
}
|
||||
|
||||
async def generate_comprehensive_ai_recommendations(self, strategy: EnhancedContentStrategy, db: Session) -> None:
|
||||
"""
|
||||
Generate comprehensive AI recommendations using 5 specialized prompts.
|
||||
|
||||
Args:
|
||||
strategy: The enhanced content strategy object
|
||||
db: Database session
|
||||
"""
|
||||
try:
|
||||
self.logger.info(f"Generating comprehensive AI recommendations for strategy: {strategy.id}")
|
||||
|
||||
start_time = datetime.utcnow()
|
||||
|
||||
# Generate recommendations for each analysis type
|
||||
analysis_types = [
|
||||
'comprehensive_strategy',
|
||||
'audience_intelligence',
|
||||
'competitive_intelligence',
|
||||
'performance_optimization',
|
||||
'content_calendar_optimization'
|
||||
]
|
||||
|
||||
ai_recommendations = {}
|
||||
successful_analyses = 0
|
||||
failed_analyses = 0
|
||||
|
||||
for analysis_type in analysis_types:
|
||||
try:
|
||||
# Generate recommendations without timeout (allow natural processing time)
|
||||
recommendations = await self.generate_specialized_recommendations(strategy, analysis_type, db)
|
||||
|
||||
# Validate recommendations before storing
|
||||
if recommendations and (recommendations.get('recommendations') or recommendations.get('insights')):
|
||||
ai_recommendations[analysis_type] = recommendations
|
||||
successful_analyses += 1
|
||||
|
||||
# Store individual analysis result
|
||||
analysis_result = EnhancedAIAnalysisResult(
|
||||
user_id=strategy.user_id,
|
||||
strategy_id=strategy.id,
|
||||
analysis_type=analysis_type,
|
||||
comprehensive_insights=recommendations.get('comprehensive_insights'),
|
||||
audience_intelligence=recommendations.get('audience_intelligence'),
|
||||
competitive_intelligence=recommendations.get('competitive_intelligence'),
|
||||
performance_optimization=recommendations.get('performance_optimization'),
|
||||
content_calendar_optimization=recommendations.get('content_calendar_optimization'),
|
||||
onboarding_data_used=strategy.onboarding_data_used,
|
||||
processing_time=(datetime.utcnow() - start_time).total_seconds(),
|
||||
ai_service_status="operational"
|
||||
)
|
||||
|
||||
db.add(analysis_result)
|
||||
else:
|
||||
self.logger.warning(f"Empty or invalid recommendations for {analysis_type}")
|
||||
failed_analyses += 1
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error generating {analysis_type} recommendations: {str(e)}")
|
||||
failed_analyses += 1
|
||||
continue
|
||||
|
||||
# Only commit if we have at least one successful analysis
|
||||
if successful_analyses > 0:
|
||||
db.commit()
|
||||
|
||||
# Update strategy with comprehensive AI analysis
|
||||
strategy.comprehensive_ai_analysis = ai_recommendations
|
||||
|
||||
# Import strategy utilities for scoring and analysis
|
||||
from ..utils.strategy_utils import (
|
||||
calculate_strategic_scores,
|
||||
extract_market_positioning,
|
||||
extract_competitive_advantages,
|
||||
extract_strategic_risks,
|
||||
extract_opportunity_analysis
|
||||
)
|
||||
|
||||
strategy.strategic_scores = calculate_strategic_scores(ai_recommendations)
|
||||
strategy.market_positioning = extract_market_positioning(ai_recommendations)
|
||||
strategy.competitive_advantages = extract_competitive_advantages(ai_recommendations)
|
||||
strategy.strategic_risks = extract_strategic_risks(ai_recommendations)
|
||||
strategy.opportunity_analysis = extract_opportunity_analysis(ai_recommendations)
|
||||
|
||||
db.commit()
|
||||
|
||||
processing_time = (datetime.utcnow() - start_time).total_seconds()
|
||||
self.logger.info(f"Comprehensive AI recommendations generated in {processing_time:.2f} seconds - {successful_analyses} successful, {failed_analyses} failed")
|
||||
else:
|
||||
self.logger.error("No successful AI analyses generated - strategy creation will continue without AI recommendations")
|
||||
# Don't raise error, allow strategy creation to continue without AI recommendations
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error generating comprehensive AI recommendations: {str(e)}")
|
||||
# Don't raise error, just log it as this is enhancement, not core functionality
|
||||
|
||||
async def generate_specialized_recommendations(self, strategy: EnhancedContentStrategy, analysis_type: str, db: Session) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate specialized recommendations using specific AI prompts.
|
||||
|
||||
Args:
|
||||
strategy: The enhanced content strategy object
|
||||
analysis_type: Type of analysis to perform
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Dictionary with structured AI recommendations
|
||||
"""
|
||||
try:
|
||||
# Prepare strategy data for AI analysis
|
||||
strategy_data = strategy.to_dict()
|
||||
|
||||
# Get onboarding data for context
|
||||
onboarding_integration = await self.get_onboarding_integration(strategy.id, db)
|
||||
|
||||
# Create prompt based on analysis type
|
||||
prompt = self.create_specialized_prompt(strategy, analysis_type)
|
||||
|
||||
# Generate AI response (placeholder - integrate with actual AI service)
|
||||
ai_response = await self.call_ai_service(prompt, analysis_type)
|
||||
|
||||
# Parse and structure the response
|
||||
structured_response = self.parse_ai_response(ai_response, analysis_type)
|
||||
|
||||
return structured_response
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error generating {analysis_type} recommendations: {str(e)}")
|
||||
raise
|
||||
|
||||
def create_specialized_prompt(self, strategy: EnhancedContentStrategy, analysis_type: str) -> str:
|
||||
"""
|
||||
Create specialized AI prompts for each analysis type.
|
||||
|
||||
Args:
|
||||
strategy: The enhanced content strategy object
|
||||
analysis_type: Type of analysis to perform
|
||||
|
||||
Returns:
|
||||
Specialized prompt string for AI analysis
|
||||
"""
|
||||
|
||||
base_context = f"""
|
||||
Business Context:
|
||||
- Industry: {strategy.industry}
|
||||
- Business Objectives: {strategy.business_objectives}
|
||||
- Target Metrics: {strategy.target_metrics}
|
||||
- Content Budget: {strategy.content_budget}
|
||||
- Team Size: {strategy.team_size}
|
||||
- Implementation Timeline: {strategy.implementation_timeline}
|
||||
- Market Share: {strategy.market_share}
|
||||
- Competitive Position: {strategy.competitive_position}
|
||||
- Performance Metrics: {strategy.performance_metrics}
|
||||
|
||||
Audience Intelligence:
|
||||
- Content Preferences: {strategy.content_preferences}
|
||||
- Consumption Patterns: {strategy.consumption_patterns}
|
||||
- Audience Pain Points: {strategy.audience_pain_points}
|
||||
- Buying Journey: {strategy.buying_journey}
|
||||
- Seasonal Trends: {strategy.seasonal_trends}
|
||||
- Engagement Metrics: {strategy.engagement_metrics}
|
||||
|
||||
Competitive Intelligence:
|
||||
- Top Competitors: {strategy.top_competitors}
|
||||
- Competitor Content Strategies: {strategy.competitor_content_strategies}
|
||||
- Market Gaps: {strategy.market_gaps}
|
||||
- Industry Trends: {strategy.industry_trends}
|
||||
- Emerging Trends: {strategy.emerging_trends}
|
||||
|
||||
Content Strategy:
|
||||
- Preferred Formats: {strategy.preferred_formats}
|
||||
- Content Mix: {strategy.content_mix}
|
||||
- Content Frequency: {strategy.content_frequency}
|
||||
- Optimal Timing: {strategy.optimal_timing}
|
||||
- Quality Metrics: {strategy.quality_metrics}
|
||||
- Editorial Guidelines: {strategy.editorial_guidelines}
|
||||
- Brand Voice: {strategy.brand_voice}
|
||||
|
||||
Performance & Analytics:
|
||||
- Traffic Sources: {strategy.traffic_sources}
|
||||
- Conversion Rates: {strategy.conversion_rates}
|
||||
- Content ROI Targets: {strategy.content_roi_targets}
|
||||
- A/B Testing Capabilities: {strategy.ab_testing_capabilities}
|
||||
"""
|
||||
|
||||
specialized_prompts = {
|
||||
'comprehensive_strategy': f"""
|
||||
{base_context}
|
||||
|
||||
TASK: Generate a comprehensive content strategy analysis that provides:
|
||||
1. Strategic positioning and market analysis
|
||||
2. Audience targeting and persona development
|
||||
3. Content pillar recommendations with rationale
|
||||
4. Competitive advantage identification
|
||||
5. Performance optimization strategies
|
||||
6. Risk assessment and mitigation plans
|
||||
7. Implementation roadmap with milestones
|
||||
8. Success metrics and KPIs
|
||||
|
||||
REQUIREMENTS:
|
||||
- Provide actionable, specific recommendations
|
||||
- Include data-driven insights
|
||||
- Consider industry best practices
|
||||
- Address both short-term and long-term goals
|
||||
- Provide confidence levels for each recommendation
|
||||
""",
|
||||
|
||||
'audience_intelligence': f"""
|
||||
{base_context}
|
||||
|
||||
TASK: Generate detailed audience intelligence analysis including:
|
||||
1. Comprehensive audience persona development
|
||||
2. Content preference analysis and recommendations
|
||||
3. Consumption pattern insights and optimization
|
||||
4. Pain point identification and content solutions
|
||||
5. Buying journey mapping and content alignment
|
||||
6. Seasonal trend analysis and content planning
|
||||
7. Engagement pattern analysis and optimization
|
||||
8. Audience segmentation strategies
|
||||
|
||||
REQUIREMENTS:
|
||||
- Use data-driven insights from provided metrics
|
||||
- Provide specific content recommendations for each audience segment
|
||||
- Include engagement optimization strategies
|
||||
- Consider cultural and behavioral factors
|
||||
""",
|
||||
|
||||
'competitive_intelligence': f"""
|
||||
{base_context}
|
||||
|
||||
TASK: Generate comprehensive competitive intelligence analysis including:
|
||||
1. Competitor content strategy analysis
|
||||
2. Market gap identification and opportunities
|
||||
3. Competitive advantage development strategies
|
||||
4. Industry trend analysis and implications
|
||||
5. Emerging trend identification and early adoption strategies
|
||||
6. Competitive positioning recommendations
|
||||
7. Market opportunity assessment
|
||||
8. Competitive response strategies
|
||||
|
||||
REQUIREMENTS:
|
||||
- Analyze provided competitor data thoroughly
|
||||
- Identify unique market opportunities
|
||||
- Provide actionable competitive strategies
|
||||
- Consider both direct and indirect competitors
|
||||
""",
|
||||
|
||||
'performance_optimization': f"""
|
||||
{base_context}
|
||||
|
||||
TASK: Generate performance optimization analysis including:
|
||||
1. Current performance analysis and benchmarking
|
||||
2. Traffic source optimization strategies
|
||||
3. Conversion rate improvement recommendations
|
||||
4. Content ROI optimization strategies
|
||||
5. A/B testing framework and recommendations
|
||||
6. Performance monitoring and analytics setup
|
||||
7. Optimization roadmap and priorities
|
||||
8. Success metrics and tracking implementation
|
||||
|
||||
REQUIREMENTS:
|
||||
- Provide specific, measurable optimization strategies
|
||||
- Include data-driven recommendations
|
||||
- Consider both technical and content optimizations
|
||||
- Provide implementation timelines and priorities
|
||||
""",
|
||||
|
||||
'content_calendar_optimization': f"""
|
||||
{base_context}
|
||||
|
||||
TASK: Generate content calendar optimization analysis including:
|
||||
1. Optimal content frequency and timing analysis
|
||||
2. Content mix optimization and balance
|
||||
3. Seasonal content planning and scheduling
|
||||
4. Content pillar integration and scheduling
|
||||
5. Platform-specific content adaptation
|
||||
6. Content repurposing and amplification strategies
|
||||
7. Editorial calendar optimization
|
||||
8. Content performance tracking and adjustment
|
||||
|
||||
REQUIREMENTS:
|
||||
- Provide specific scheduling recommendations
|
||||
- Include content mix optimization strategies
|
||||
- Consider platform-specific requirements
|
||||
- Provide seasonal and trend-based planning
|
||||
"""
|
||||
}
|
||||
|
||||
return specialized_prompts.get(analysis_type, base_context)
|
||||
|
||||
async def call_ai_service(self, prompt: str, analysis_type: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Call AI service to generate recommendations.
|
||||
|
||||
Args:
|
||||
prompt: The AI prompt to send
|
||||
analysis_type: Type of analysis being performed
|
||||
|
||||
Returns:
|
||||
Dictionary with AI response
|
||||
|
||||
Raises:
|
||||
RuntimeError: If AI service is not available or fails
|
||||
"""
|
||||
try:
|
||||
# Import AI service manager
|
||||
from services.ai_service_manager import AIServiceManager, AIServiceType
|
||||
|
||||
# Initialize AI service
|
||||
ai_service = AIServiceManager()
|
||||
|
||||
# Map analysis types to AI service types
|
||||
service_type_mapping = {
|
||||
'comprehensive_strategy': AIServiceType.STRATEGIC_INTELLIGENCE,
|
||||
'audience_intelligence': AIServiceType.STRATEGIC_INTELLIGENCE,
|
||||
'competitive_intelligence': AIServiceType.MARKET_POSITION_ANALYSIS,
|
||||
'performance_optimization': AIServiceType.PERFORMANCE_PREDICTION,
|
||||
'content_calendar_optimization': AIServiceType.CONTENT_SCHEDULE_GENERATION
|
||||
}
|
||||
|
||||
# Get the appropriate service type, default to strategic intelligence
|
||||
service_type = service_type_mapping.get(analysis_type, AIServiceType.STRATEGIC_INTELLIGENCE)
|
||||
|
||||
# Define schema for AI response
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"recommendations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"priority": {"type": "string"},
|
||||
"impact": {"type": "string"},
|
||||
"implementation_difficulty": {"type": "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"insights": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"insight": {"type": "string"},
|
||||
"confidence": {"type": "string"},
|
||||
"data_support": {"type": "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"confidence": {"type": "number"},
|
||||
"completeness": {"type": "number"},
|
||||
"actionability": {"type": "number"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate AI response using the service manager
|
||||
response = await ai_service.execute_structured_json_call(
|
||||
service_type,
|
||||
prompt,
|
||||
schema
|
||||
)
|
||||
|
||||
# Validate that we got actual AI response
|
||||
if not response:
|
||||
raise RuntimeError(f"AI service returned null response for {analysis_type}")
|
||||
|
||||
# Check for error in response
|
||||
if response.get("error"):
|
||||
error_msg = response.get("error", "Unknown error")
|
||||
if "Failed to parse JSON" in error_msg:
|
||||
# Try to extract partial data from raw response
|
||||
raw_response = response.get("raw_response", "")
|
||||
if raw_response:
|
||||
self.logger.warning(f"JSON parsing failed for {analysis_type}, attempting to extract partial data")
|
||||
partial_data = self._extract_partial_data_from_raw(raw_response)
|
||||
if partial_data:
|
||||
self.logger.info(f"Successfully extracted partial data for {analysis_type}")
|
||||
return partial_data
|
||||
|
||||
raise RuntimeError(f"AI service error for {analysis_type}: {error_msg}")
|
||||
|
||||
# Check if response has data
|
||||
if not response.get("data"):
|
||||
# Check if response itself contains the expected structure
|
||||
if response.get("recommendations") or response.get("insights"):
|
||||
self.logger.info(f"Using direct response structure for {analysis_type}")
|
||||
return response
|
||||
else:
|
||||
raise RuntimeError(f"AI service returned empty data for {analysis_type}")
|
||||
|
||||
# Return the structured response
|
||||
return response.get("data", {})
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"AI service failed for {analysis_type}: {str(e)}")
|
||||
raise RuntimeError(f"AI service integration failed for {analysis_type}: {str(e)}")
|
||||
|
||||
def _extract_partial_data_from_raw(self, raw_response: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Extract partial data from raw AI response when JSON parsing fails.
|
||||
"""
|
||||
try:
|
||||
# Look for common patterns in the raw response
|
||||
import re
|
||||
|
||||
# Extract recommendations
|
||||
recommendations = []
|
||||
rec_pattern = r'"title"\s*:\s*"([^"]+)"[^}]*"description"\s*:\s*"([^"]*)"'
|
||||
rec_matches = re.findall(rec_pattern, raw_response)
|
||||
for title, description in rec_matches:
|
||||
recommendations.append({
|
||||
"title": title,
|
||||
"description": description,
|
||||
"priority": "medium",
|
||||
"impact": "moderate",
|
||||
"implementation_difficulty": "medium"
|
||||
})
|
||||
|
||||
# Extract insights
|
||||
insights = []
|
||||
insight_pattern = r'"insight"\s*:\s*"([^"]+)"'
|
||||
insight_matches = re.findall(insight_pattern, raw_response)
|
||||
for insight in insight_matches:
|
||||
insights.append({
|
||||
"insight": insight,
|
||||
"confidence": "medium",
|
||||
"data_support": "industry_analysis"
|
||||
})
|
||||
|
||||
if recommendations or insights:
|
||||
return {
|
||||
"recommendations": recommendations,
|
||||
"insights": insights,
|
||||
"metrics": {
|
||||
"confidence": 0.6,
|
||||
"completeness": 0.5,
|
||||
"actionability": 0.7
|
||||
}
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Error extracting partial data: {e}")
|
||||
return None
|
||||
|
||||
def parse_ai_response(self, ai_response: Dict[str, Any], analysis_type: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Parse and structure AI response.
|
||||
|
||||
Args:
|
||||
ai_response: Raw AI response
|
||||
analysis_type: Type of analysis performed
|
||||
|
||||
Returns:
|
||||
Structured response dictionary
|
||||
|
||||
Raises:
|
||||
RuntimeError: If AI response is invalid or empty
|
||||
"""
|
||||
if not ai_response:
|
||||
raise RuntimeError(f"Empty AI response received for {analysis_type}")
|
||||
|
||||
# Validate that we have actual recommendations
|
||||
recommendations = ai_response.get('recommendations', [])
|
||||
insights = ai_response.get('insights', [])
|
||||
|
||||
if not recommendations and not insights:
|
||||
raise RuntimeError(f"No recommendations or insights found in AI response for {analysis_type}")
|
||||
|
||||
return {
|
||||
'analysis_type': analysis_type,
|
||||
'recommendations': recommendations,
|
||||
'insights': insights,
|
||||
'metrics': ai_response.get('metrics', {}),
|
||||
'confidence_score': ai_response.get('metrics', {}).get('confidence', 0.8)
|
||||
}
|
||||
|
||||
def get_fallback_recommendations(self, analysis_type: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get fallback recommendations - DISABLED.
|
||||
|
||||
Args:
|
||||
analysis_type: Type of analysis
|
||||
|
||||
Returns:
|
||||
Never returns - always raises error
|
||||
|
||||
Raises:
|
||||
RuntimeError: Always raised as fallbacks are disabled
|
||||
"""
|
||||
raise RuntimeError(f"Fallback recommendations are disabled for {analysis_type}. Real AI insights required.")
|
||||
|
||||
async def get_latest_ai_analysis(self, strategy_id: int, db: Session) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get the latest AI analysis for a strategy.
|
||||
|
||||
Args:
|
||||
strategy_id: The strategy ID
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Latest AI analysis result or None
|
||||
"""
|
||||
try:
|
||||
analysis = db.query(EnhancedAIAnalysisResult).filter(
|
||||
EnhancedAIAnalysisResult.strategy_id == strategy_id
|
||||
).order_by(EnhancedAIAnalysisResult.created_at.desc()).first()
|
||||
|
||||
return analysis.to_dict() if analysis else None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting latest AI analysis: {str(e)}")
|
||||
return None
|
||||
|
||||
async def get_onboarding_integration(self, strategy_id: int, db: Session) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get onboarding data integration for a strategy.
|
||||
|
||||
Args:
|
||||
strategy_id: The strategy ID
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Onboarding integration data or None
|
||||
"""
|
||||
try:
|
||||
from models.enhanced_strategy_models import OnboardingDataIntegration
|
||||
integration = db.query(OnboardingDataIntegration).filter(
|
||||
OnboardingDataIntegration.strategy_id == strategy_id
|
||||
).first()
|
||||
|
||||
return integration.to_dict() if integration else None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting onboarding integration: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
# Standalone functions for backward compatibility
|
||||
async def generate_comprehensive_ai_recommendations(strategy: EnhancedContentStrategy, db: Session) -> None:
|
||||
"""Generate comprehensive AI recommendations using 5 specialized prompts."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return await analyzer.generate_comprehensive_ai_recommendations(strategy, db)
|
||||
|
||||
|
||||
async def generate_specialized_recommendations(strategy: EnhancedContentStrategy, analysis_type: str, db: Session) -> Dict[str, Any]:
|
||||
"""Generate specialized recommendations using specific AI prompts."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return await analyzer.generate_specialized_recommendations(strategy, analysis_type, db)
|
||||
|
||||
|
||||
def create_specialized_prompt(strategy: EnhancedContentStrategy, analysis_type: str) -> str:
|
||||
"""Create specialized AI prompts for each analysis type."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return analyzer.create_specialized_prompt(strategy, analysis_type)
|
||||
|
||||
|
||||
async def call_ai_service(prompt: str, analysis_type: str) -> Dict[str, Any]:
|
||||
"""Call AI service to generate recommendations."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return await analyzer.call_ai_service(prompt, analysis_type)
|
||||
|
||||
|
||||
def parse_ai_response(ai_response: Dict[str, Any], analysis_type: str) -> Dict[str, Any]:
|
||||
"""Parse and structure AI response."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return analyzer.parse_ai_response(ai_response, analysis_type)
|
||||
|
||||
|
||||
def get_fallback_recommendations(analysis_type: str) -> Dict[str, Any]:
|
||||
"""Get fallback recommendations (disabled)."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return analyzer.get_fallback_recommendations(analysis_type)
|
||||
|
||||
|
||||
async def get_latest_ai_analysis(strategy_id: int, db: Session) -> Optional[Dict[str, Any]]:
|
||||
"""Get the latest AI analysis for a strategy."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return await analyzer.get_latest_ai_analysis(strategy_id, db)
|
||||
|
||||
|
||||
async def get_onboarding_integration(strategy_id: int, db: Session) -> Optional[Dict[str, Any]]:
|
||||
"""Get onboarding data integration for a strategy."""
|
||||
analyzer = StrategyAnalyzer()
|
||||
return await analyzer.get_onboarding_integration(strategy_id, db)
|
||||
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
AI Generation Module
|
||||
AI-powered content strategy generation with comprehensive insights and recommendations.
|
||||
"""
|
||||
|
||||
from .strategy_generator import AIStrategyGenerator, StrategyGenerationConfig
|
||||
|
||||
__all__ = ["AIStrategyGenerator", "StrategyGenerationConfig"]
|
||||
@@ -0,0 +1,699 @@
|
||||
"""
|
||||
AI-Powered Strategy Generation Service
|
||||
Generates comprehensive content strategies using AI with enhanced insights and recommendations.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
|
||||
from services.ai_service_manager import AIServiceManager, AIServiceType
|
||||
from ..autofill.ai_structured_autofill import AIStructuredAutofillService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@dataclass
|
||||
class StrategyGenerationConfig:
|
||||
"""Configuration for strategy generation."""
|
||||
include_competitive_analysis: bool = True
|
||||
include_content_calendar: bool = True
|
||||
include_performance_predictions: bool = True
|
||||
include_implementation_roadmap: bool = True
|
||||
include_risk_assessment: bool = True
|
||||
max_content_pieces: int = 50
|
||||
timeline_months: int = 12
|
||||
|
||||
class AIStrategyGenerator:
|
||||
"""
|
||||
AI-Powered Content Strategy Generator
|
||||
|
||||
Generates comprehensive content strategies including:
|
||||
- Strategic field autofill (leveraging existing 100% success system)
|
||||
- Competitive analysis and positioning
|
||||
- Content calendar and publishing schedule
|
||||
- Performance predictions and KPIs
|
||||
- Implementation roadmap
|
||||
- Risk assessment and mitigation
|
||||
"""
|
||||
|
||||
def __init__(self, config: Optional[StrategyGenerationConfig] = None):
|
||||
"""Initialize the AI strategy generator."""
|
||||
self.config = config or StrategyGenerationConfig()
|
||||
self.ai_manager = AIServiceManager()
|
||||
self.autofill_service = AIStructuredAutofillService()
|
||||
self.logger = logger
|
||||
|
||||
async def generate_comprehensive_strategy(
|
||||
self,
|
||||
user_id: int,
|
||||
context: Dict[str, Any],
|
||||
strategy_name: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate a comprehensive content strategy using AI.
|
||||
|
||||
Args:
|
||||
user_id: User ID for personalization
|
||||
context: User context and onboarding data
|
||||
strategy_name: Optional custom strategy name
|
||||
|
||||
Returns:
|
||||
Comprehensive strategy with all components
|
||||
|
||||
Raises:
|
||||
RuntimeError: If any AI component fails to generate
|
||||
"""
|
||||
try:
|
||||
self.logger.info(f"🚀 Generating comprehensive AI strategy for user: {user_id}")
|
||||
|
||||
# Step 1: Generate base strategy fields (using existing autofill system)
|
||||
base_strategy = await self._generate_base_strategy_fields(user_id, context)
|
||||
|
||||
# Step 2: Generate strategic insights and recommendations
|
||||
strategic_insights = await self._generate_strategic_insights(base_strategy, context)
|
||||
|
||||
# Step 3: Generate competitive analysis
|
||||
competitive_analysis = await self._generate_competitive_analysis(base_strategy, context)
|
||||
|
||||
# Step 4: Generate content calendar
|
||||
content_calendar = await self._generate_content_calendar(base_strategy, context)
|
||||
|
||||
# Step 5: Generate performance predictions
|
||||
performance_predictions = await self._generate_performance_predictions(base_strategy, context)
|
||||
|
||||
# Step 6: Generate implementation roadmap
|
||||
implementation_roadmap = await self._generate_implementation_roadmap(base_strategy, context)
|
||||
|
||||
# Step 7: Generate risk assessment
|
||||
risk_assessment = await self._generate_risk_assessment(base_strategy, context)
|
||||
|
||||
# Step 8: Compile comprehensive strategy
|
||||
comprehensive_strategy = {
|
||||
"strategy_metadata": {
|
||||
"generated_at": datetime.utcnow().isoformat(),
|
||||
"user_id": user_id,
|
||||
"strategy_name": strategy_name or f"AI-Generated Strategy {datetime.utcnow().strftime('%Y-%m-%d')}",
|
||||
"generation_version": "2.0",
|
||||
"ai_model": "gemini-pro",
|
||||
"personalization_level": "high",
|
||||
"ai_generated": True,
|
||||
"comprehensive": True
|
||||
},
|
||||
"base_strategy": base_strategy,
|
||||
"strategic_insights": strategic_insights,
|
||||
"competitive_analysis": competitive_analysis,
|
||||
"content_calendar": content_calendar,
|
||||
"performance_predictions": performance_predictions,
|
||||
"implementation_roadmap": implementation_roadmap,
|
||||
"risk_assessment": risk_assessment,
|
||||
"summary": {
|
||||
"total_content_pieces": len(content_calendar.get("content_pieces", [])),
|
||||
"estimated_roi": performance_predictions.get("estimated_roi", "15-25%"),
|
||||
"implementation_timeline": implementation_roadmap.get("total_duration", "12 months"),
|
||||
"risk_level": risk_assessment.get("overall_risk_level", "Medium"),
|
||||
"success_probability": performance_predictions.get("success_probability", "85%")
|
||||
}
|
||||
}
|
||||
|
||||
self.logger.info(f"✅ Comprehensive AI strategy generated successfully for user: {user_id}")
|
||||
return comprehensive_strategy
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"❌ Error generating comprehensive strategy: {str(e)}")
|
||||
raise RuntimeError(f"Failed to generate comprehensive strategy: {str(e)}")
|
||||
|
||||
async def _generate_base_strategy_fields(
|
||||
self,
|
||||
user_id: int,
|
||||
context: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate base strategy fields using existing autofill system."""
|
||||
try:
|
||||
self.logger.info(f"Generating base strategy fields for user: {user_id}")
|
||||
|
||||
# Use existing autofill service (100% success rate)
|
||||
autofill_result = await self.autofill_service.generate_autofill_fields(user_id, context)
|
||||
|
||||
# Extract the fields from autofill result
|
||||
base_strategy = autofill_result.get("fields", {})
|
||||
|
||||
# Add generation metadata
|
||||
base_strategy["generation_metadata"] = {
|
||||
"generated_by": "ai_autofill_system",
|
||||
"success_rate": autofill_result.get("success_rate", 100),
|
||||
"personalized": autofill_result.get("personalized", True),
|
||||
"data_sources": autofill_result.get("data_sources", [])
|
||||
}
|
||||
|
||||
return base_strategy
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error generating base strategy fields: {str(e)}")
|
||||
raise
|
||||
|
||||
async def _generate_strategic_insights(self, base_strategy: Dict[str, Any], context: Dict[str, Any], ai_manager: Optional[Any] = None) -> Dict[str, Any]:
|
||||
"""Generate strategic insights using AI."""
|
||||
try:
|
||||
logger.info("🧠 Generating strategic insights...")
|
||||
|
||||
# Use provided AI manager or create default one
|
||||
if ai_manager is None:
|
||||
from services.ai_service_manager import AIServiceManager
|
||||
ai_manager = AIServiceManager()
|
||||
|
||||
prompt = f"""
|
||||
Generate comprehensive strategic insights for content strategy based on the following context:
|
||||
|
||||
CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
BASE STRATEGY:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
Please provide strategic insights including:
|
||||
1. Market positioning analysis
|
||||
2. Content opportunity identification
|
||||
3. Competitive advantage mapping
|
||||
4. Growth potential assessment
|
||||
5. Strategic recommendations
|
||||
|
||||
Format as structured JSON with insights, reasoning, and confidence levels.
|
||||
"""
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"insights": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"type": "string"},
|
||||
"insight": {"type": "string"},
|
||||
"reasoning": {"type": "string"},
|
||||
"priority": {"type": "string"},
|
||||
"estimated_impact": {"type": "string"},
|
||||
"implementation_time": {"type": "string"},
|
||||
"confidence_level": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = await ai_manager.execute_structured_json_call(
|
||||
AIServiceType.STRATEGIC_INTELLIGENCE,
|
||||
prompt,
|
||||
schema
|
||||
)
|
||||
|
||||
if not response or not response.get("data"):
|
||||
raise RuntimeError("AI service returned empty strategic insights")
|
||||
|
||||
logger.info("✅ Strategic insights generated successfully")
|
||||
return response.get("data", {})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating strategic insights: {str(e)}")
|
||||
raise RuntimeError(f"Failed to generate strategic insights: {str(e)}")
|
||||
|
||||
async def _generate_competitive_analysis(self, base_strategy: Dict[str, Any], context: Dict[str, Any], ai_manager: Optional[Any] = None) -> Dict[str, Any]:
|
||||
"""Generate competitive analysis using AI."""
|
||||
try:
|
||||
logger.info("🔍 Generating competitive analysis...")
|
||||
|
||||
# Use provided AI manager or create default one
|
||||
if ai_manager is None:
|
||||
from services.ai_service_manager import AIServiceManager
|
||||
ai_manager = AIServiceManager()
|
||||
|
||||
prompt = f"""
|
||||
Generate comprehensive competitive analysis for content strategy based on the following context:
|
||||
|
||||
CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
BASE STRATEGY:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
Please provide competitive analysis including:
|
||||
1. Competitor identification and analysis
|
||||
2. Market gap identification
|
||||
3. Differentiation opportunities
|
||||
4. Competitive positioning
|
||||
5. Strategic recommendations
|
||||
|
||||
Format as structured JSON with detailed analysis and recommendations.
|
||||
"""
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"competitors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"strengths": {"type": "array", "items": {"type": "string"}},
|
||||
"weaknesses": {"type": "array", "items": {"type": "string"}},
|
||||
"content_strategy": {"type": "string"},
|
||||
"market_position": {"type": "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"market_gaps": {"type": "array", "items": {"type": "string"}},
|
||||
"opportunities": {"type": "array", "items": {"type": "string"}},
|
||||
"recommendations": {"type": "array", "items": {"type": "string"}}
|
||||
}
|
||||
}
|
||||
|
||||
response = await ai_manager.execute_structured_json_call(
|
||||
AIServiceType.MARKET_POSITION_ANALYSIS,
|
||||
prompt,
|
||||
schema
|
||||
)
|
||||
|
||||
if not response or not response.get("data"):
|
||||
raise RuntimeError("AI service returned empty competitive analysis")
|
||||
|
||||
logger.info("✅ Competitive analysis generated successfully")
|
||||
return response.get("data", {})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating competitive analysis: {str(e)}")
|
||||
raise RuntimeError(f"Failed to generate competitive analysis: {str(e)}")
|
||||
|
||||
async def _generate_content_calendar(self, base_strategy: Dict[str, Any], context: Dict[str, Any], ai_manager: Optional[Any] = None) -> Dict[str, Any]:
|
||||
"""Generate content calendar using AI."""
|
||||
try:
|
||||
logger.info("📅 Generating content calendar...")
|
||||
|
||||
# Use provided AI manager or create default one
|
||||
if ai_manager is None:
|
||||
from services.ai_service_manager import AIServiceManager
|
||||
ai_manager = AIServiceManager()
|
||||
|
||||
prompt = f"""
|
||||
Generate comprehensive content calendar for content strategy based on the following context:
|
||||
|
||||
CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
BASE STRATEGY:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
Please provide content calendar including:
|
||||
1. Content pieces with titles and descriptions
|
||||
2. Publishing schedule and timing
|
||||
3. Content types and formats
|
||||
4. Platform distribution strategy
|
||||
5. Content themes and pillars
|
||||
|
||||
Format as structured JSON with detailed content schedule.
|
||||
"""
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_pieces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"content_type": {"type": "string"},
|
||||
"platform": {"type": "string"},
|
||||
"publishing_date": {"type": "string"},
|
||||
"theme": {"type": "string"},
|
||||
"priority": {"type": "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"themes": {"type": "array", "items": {"type": "string"}},
|
||||
"schedule": {"type": "object"},
|
||||
"distribution_strategy": {"type": "object"}
|
||||
}
|
||||
}
|
||||
|
||||
response = await ai_manager.execute_structured_json_call(
|
||||
AIServiceType.CONTENT_SCHEDULE_GENERATION,
|
||||
prompt,
|
||||
schema
|
||||
)
|
||||
|
||||
if not response or not response.get("data"):
|
||||
raise RuntimeError("AI service returned empty content calendar")
|
||||
|
||||
logger.info("✅ Content calendar generated successfully")
|
||||
return response.get("data", {})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating content calendar: {str(e)}")
|
||||
raise RuntimeError(f"Failed to generate content calendar: {str(e)}")
|
||||
|
||||
async def _generate_performance_predictions(self, base_strategy: Dict[str, Any], context: Dict[str, Any], ai_manager: Optional[Any] = None) -> Dict[str, Any]:
|
||||
"""Generate performance predictions using AI."""
|
||||
try:
|
||||
logger.info("📊 Generating performance predictions...")
|
||||
|
||||
# Use provided AI manager or create default one
|
||||
if ai_manager is None:
|
||||
from services.ai_service_manager import AIServiceManager
|
||||
ai_manager = AIServiceManager()
|
||||
|
||||
prompt = f"""
|
||||
Generate comprehensive performance predictions for content strategy based on the following context:
|
||||
|
||||
CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
BASE STRATEGY:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
Please provide performance predictions including:
|
||||
1. Traffic growth projections
|
||||
2. Engagement rate predictions
|
||||
3. Conversion rate estimates
|
||||
4. ROI projections
|
||||
5. Success probability assessment
|
||||
|
||||
Format as structured JSON with detailed predictions and confidence levels.
|
||||
"""
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"traffic_predictions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"monthly_traffic": {"type": "string"},
|
||||
"growth_rate": {"type": "string"},
|
||||
"peak_traffic": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"engagement_predictions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"engagement_rate": {"type": "string"},
|
||||
"time_on_page": {"type": "string"},
|
||||
"bounce_rate": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"conversion_predictions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"conversion_rate": {"type": "string"},
|
||||
"lead_generation": {"type": "string"},
|
||||
"sales_impact": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"roi_predictions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"estimated_roi": {"type": "string"},
|
||||
"cost_benefit": {"type": "string"},
|
||||
"payback_period": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = await ai_manager.execute_structured_json_call(
|
||||
AIServiceType.PERFORMANCE_PREDICTION,
|
||||
prompt,
|
||||
schema
|
||||
)
|
||||
|
||||
if not response or not response.get("data"):
|
||||
raise RuntimeError("AI service returned empty performance predictions")
|
||||
|
||||
logger.info("✅ Performance predictions generated successfully")
|
||||
return response.get("data", {})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating performance predictions: {str(e)}")
|
||||
raise RuntimeError(f"Failed to generate performance predictions: {str(e)}")
|
||||
|
||||
async def _generate_implementation_roadmap(self, base_strategy: Dict[str, Any], context: Dict[str, Any], ai_manager: Optional[Any] = None) -> Dict[str, Any]:
|
||||
"""Generate implementation roadmap using AI."""
|
||||
try:
|
||||
logger.info("🗺️ Generating implementation roadmap...")
|
||||
|
||||
# Use provided AI manager or create default one
|
||||
if ai_manager is None:
|
||||
from services.ai_service_manager import AIServiceManager
|
||||
ai_manager = AIServiceManager()
|
||||
|
||||
prompt = f"""
|
||||
Generate comprehensive implementation roadmap for content strategy based on the following context:
|
||||
|
||||
CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
BASE STRATEGY:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
Please provide implementation roadmap including:
|
||||
1. Phase-by-phase breakdown
|
||||
2. Timeline with milestones
|
||||
3. Resource allocation
|
||||
4. Success metrics
|
||||
5. Risk mitigation strategies
|
||||
|
||||
Format as structured JSON with detailed implementation plan.
|
||||
"""
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"phases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"phase": {"type": "string"},
|
||||
"duration": {"type": "string"},
|
||||
"tasks": {"type": "array", "items": {"type": "string"}},
|
||||
"milestones": {"type": "array", "items": {"type": "string"}},
|
||||
"resources": {"type": "array", "items": {"type": "string"}}
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeline": {"type": "object"},
|
||||
"resource_allocation": {"type": "object"},
|
||||
"success_metrics": {"type": "array", "items": {"type": "string"}},
|
||||
"total_duration": {"type": "string"}
|
||||
}
|
||||
}
|
||||
|
||||
response = await ai_manager.execute_structured_json_call(
|
||||
AIServiceType.STRATEGIC_INTELLIGENCE,
|
||||
prompt,
|
||||
schema
|
||||
)
|
||||
|
||||
if not response or not response.get("data"):
|
||||
raise RuntimeError("AI service returned empty implementation roadmap")
|
||||
|
||||
logger.info("✅ Implementation roadmap generated successfully")
|
||||
return response.get("data", {})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating implementation roadmap: {str(e)}")
|
||||
raise RuntimeError(f"Failed to generate implementation roadmap: {str(e)}")
|
||||
|
||||
async def _generate_risk_assessment(self, base_strategy: Dict[str, Any], context: Dict[str, Any], ai_manager: Optional[Any] = None) -> Dict[str, Any]:
|
||||
"""Generate risk assessment using AI."""
|
||||
try:
|
||||
logger.info("⚠️ Generating risk assessment...")
|
||||
|
||||
# Use provided AI manager or create default one
|
||||
if ai_manager is None:
|
||||
from services.ai_service_manager import AIServiceManager
|
||||
ai_manager = AIServiceManager()
|
||||
|
||||
prompt = f"""
|
||||
Generate comprehensive risk assessment for content strategy based on the following context:
|
||||
|
||||
CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
BASE STRATEGY:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
Please provide risk assessment including:
|
||||
1. Risk identification and analysis
|
||||
2. Probability and impact assessment
|
||||
3. Mitigation strategies
|
||||
4. Contingency planning
|
||||
5. Risk monitoring framework
|
||||
|
||||
Format as structured JSON with detailed risk analysis and mitigation plans.
|
||||
"""
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"risks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"risk": {"type": "string"},
|
||||
"probability": {"type": "string"},
|
||||
"impact": {"type": "string"},
|
||||
"mitigation": {"type": "string"},
|
||||
"contingency": {"type": "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"overall_risk_level": {"type": "string"},
|
||||
"risk_categories": {"type": "object"},
|
||||
"mitigation_strategies": {"type": "array", "items": {"type": "string"}},
|
||||
"monitoring_framework": {"type": "object"}
|
||||
}
|
||||
}
|
||||
|
||||
response = await ai_manager.execute_structured_json_call(
|
||||
AIServiceType.STRATEGIC_INTELLIGENCE,
|
||||
prompt,
|
||||
schema
|
||||
)
|
||||
|
||||
if not response or not response.get("data"):
|
||||
raise RuntimeError("AI service returned empty risk assessment")
|
||||
|
||||
logger.info("✅ Risk assessment generated successfully")
|
||||
return response.get("data", {})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error generating risk assessment: {str(e)}")
|
||||
raise RuntimeError(f"Failed to generate risk assessment: {str(e)}")
|
||||
|
||||
def _build_strategic_insights_prompt(self, base_strategy: Dict[str, Any], context: Dict[str, Any]) -> str:
|
||||
"""Build prompt for strategic insights generation."""
|
||||
return f"""
|
||||
As an expert content strategy consultant with 15+ years of experience, analyze this content strategy and provide strategic insights:
|
||||
|
||||
STRATEGY CONTEXT:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
USER CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
Provide comprehensive strategic insights covering:
|
||||
1. Key insights about the strategy's strengths and opportunities
|
||||
2. Strategic recommendations with priority levels
|
||||
3. Identified opportunity areas for growth
|
||||
4. Competitive advantages to leverage
|
||||
|
||||
Focus on actionable, data-driven insights that will drive content strategy success.
|
||||
"""
|
||||
|
||||
def _build_competitive_analysis_prompt(self, base_strategy: Dict[str, Any], context: Dict[str, Any]) -> str:
|
||||
"""Build prompt for competitive analysis generation."""
|
||||
return f"""
|
||||
As a competitive intelligence expert, analyze the competitive landscape for this content strategy:
|
||||
|
||||
STRATEGY CONTEXT:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
USER CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
Provide comprehensive competitive analysis covering:
|
||||
1. Competitive landscape analysis with key players
|
||||
2. Positioning strategy and differentiation factors
|
||||
3. Market gaps and opportunities
|
||||
4. Competitive advantages and unique value propositions
|
||||
|
||||
Focus on actionable competitive intelligence that will inform strategic positioning.
|
||||
"""
|
||||
|
||||
def _build_content_calendar_prompt(self, base_strategy: Dict[str, Any], context: Dict[str, Any]) -> str:
|
||||
"""Build prompt for content calendar generation."""
|
||||
return f"""
|
||||
As a content strategy expert, create a comprehensive content calendar for this strategy:
|
||||
|
||||
STRATEGY CONTEXT:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
USER CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
Generate a {self.config.max_content_pieces}-piece content calendar covering {self.config.timeline_months} months including:
|
||||
1. Diverse content pieces (blog posts, social media, videos, etc.)
|
||||
2. Publishing schedule with optimal timing
|
||||
3. Content mix distribution
|
||||
4. Topic clusters and content pillars
|
||||
5. Target audience alignment
|
||||
|
||||
Ensure content aligns with business objectives and audience preferences.
|
||||
"""
|
||||
|
||||
def _build_performance_predictions_prompt(self, base_strategy: Dict[str, Any], context: Dict[str, Any]) -> str:
|
||||
"""Build prompt for performance predictions generation."""
|
||||
return f"""
|
||||
As a data-driven content strategist, predict performance outcomes for this content strategy:
|
||||
|
||||
STRATEGY CONTEXT:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
USER CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
Provide realistic performance predictions covering:
|
||||
1. Traffic growth projections (3, 6, 12 months)
|
||||
2. Engagement metrics predictions
|
||||
3. Conversion and lead generation forecasts
|
||||
4. ROI estimates and success probability
|
||||
5. Key performance indicators with targets
|
||||
|
||||
Base predictions on industry benchmarks and strategy characteristics.
|
||||
"""
|
||||
|
||||
def _build_implementation_roadmap_prompt(self, base_strategy: Dict[str, Any], context: Dict[str, Any]) -> str:
|
||||
"""Build prompt for implementation roadmap generation."""
|
||||
return f"""
|
||||
As a project management expert, create an implementation roadmap for this content strategy:
|
||||
|
||||
STRATEGY CONTEXT:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
USER CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
Create a detailed implementation roadmap covering:
|
||||
1. Phased implementation approach
|
||||
2. Resource requirements and budget allocation
|
||||
3. Timeline with milestones and deliverables
|
||||
4. Critical path and dependencies
|
||||
5. Success metrics and evaluation criteria
|
||||
|
||||
Ensure roadmap is realistic and achievable given available resources.
|
||||
"""
|
||||
|
||||
def _build_risk_assessment_prompt(self, base_strategy: Dict[str, Any], context: Dict[str, Any]) -> str:
|
||||
"""Build prompt for risk assessment generation."""
|
||||
return f"""
|
||||
As a risk management expert, assess potential risks for this content strategy:
|
||||
|
||||
STRATEGY CONTEXT:
|
||||
{json.dumps(base_strategy, indent=2)}
|
||||
|
||||
USER CONTEXT:
|
||||
{json.dumps(context, indent=2)}
|
||||
|
||||
Provide comprehensive risk assessment covering:
|
||||
1. Identified risks with probability and impact
|
||||
2. Risk categorization (market, operational, competitive, resource)
|
||||
3. Mitigation strategies for each risk
|
||||
4. Contingency plans for high-impact scenarios
|
||||
5. Overall risk level assessment
|
||||
|
||||
Focus on practical risk mitigation strategies.
|
||||
"""
|
||||
@@ -1,7 +1,8 @@
|
||||
import json
|
||||
import logging
|
||||
import traceback
|
||||
from typing import Any, Dict
|
||||
from typing import Any, Dict, List
|
||||
from datetime import datetime
|
||||
|
||||
from services.ai_service_manager import AIServiceManager, AIServiceType
|
||||
|
||||
@@ -60,44 +61,104 @@ class AIStructuredAutofillService:
|
||||
research = context.get('research_preferences') or {}
|
||||
api_keys = context.get('api_keys_data') or {}
|
||||
session = context.get('onboarding_session') or {}
|
||||
|
||||
# Extract detailed personalization data
|
||||
writing_style = website.get('writing_style', {})
|
||||
target_audience = website.get('target_audience', {})
|
||||
content_type = website.get('content_type', {})
|
||||
recommended_settings = website.get('recommended_settings', {})
|
||||
content_characteristics = website.get('content_characteristics', {})
|
||||
|
||||
summary = {
|
||||
'website_summary': {
|
||||
'user_profile': {
|
||||
'website_url': website.get('website_url'),
|
||||
'industry': website.get('industry'),
|
||||
'content_types': website.get('content_types'),
|
||||
'target_audience': website.get('target_audience'),
|
||||
'performance_metrics': website.get('performance_metrics'),
|
||||
'seo_summary': website.get('seo_analysis')
|
||||
},
|
||||
'research_summary': {
|
||||
'audience_segments': research.get('audience_segments'),
|
||||
'content_preferences': research.get('content_preferences'),
|
||||
'consumption_patterns': research.get('consumption_patterns'),
|
||||
'seasonality': research.get('seasonal_trends')
|
||||
},
|
||||
'api_summary': {
|
||||
'providers': api_keys.get('providers'),
|
||||
'total_keys': api_keys.get('total_keys')
|
||||
},
|
||||
'session_summary': {
|
||||
'business_size': session.get('business_size'),
|
||||
'region': session.get('region')
|
||||
'region': session.get('region'),
|
||||
'onboarding_progress': session.get('progress', 0)
|
||||
},
|
||||
'content_analysis': {
|
||||
'writing_style': {
|
||||
'tone': writing_style.get('tone'),
|
||||
'voice': writing_style.get('voice'),
|
||||
'complexity': writing_style.get('complexity'),
|
||||
'engagement_level': writing_style.get('engagement_level')
|
||||
},
|
||||
'content_characteristics': {
|
||||
'sentence_structure': content_characteristics.get('sentence_structure'),
|
||||
'vocabulary': content_characteristics.get('vocabulary'),
|
||||
'paragraph_organization': content_characteristics.get('paragraph_organization')
|
||||
},
|
||||
'content_type': {
|
||||
'primary_type': content_type.get('primary_type'),
|
||||
'secondary_types': content_type.get('secondary_types'),
|
||||
'purpose': content_type.get('purpose')
|
||||
}
|
||||
},
|
||||
'audience_insights': {
|
||||
'demographics': target_audience.get('demographics'),
|
||||
'expertise_level': target_audience.get('expertise_level'),
|
||||
'industry_focus': target_audience.get('industry_focus'),
|
||||
'pain_points': target_audience.get('pain_points'),
|
||||
'content_preferences': target_audience.get('content_preferences')
|
||||
},
|
||||
'ai_recommendations': {
|
||||
'recommended_tone': recommended_settings.get('writing_tone'),
|
||||
'recommended_audience': recommended_settings.get('target_audience'),
|
||||
'recommended_content_type': recommended_settings.get('content_type'),
|
||||
'style_guidelines': website.get('style_guidelines')
|
||||
},
|
||||
'research_config': {
|
||||
'research_depth': research.get('research_depth'),
|
||||
'content_types': research.get('content_types'),
|
||||
'auto_research': research.get('auto_research'),
|
||||
'factual_content': research.get('factual_content')
|
||||
},
|
||||
'api_capabilities': {
|
||||
'providers': api_keys.get('providers', []),
|
||||
'total_keys': api_keys.get('total_keys', 0),
|
||||
'available_services': self._extract_available_services(api_keys)
|
||||
},
|
||||
'data_quality': {
|
||||
'website_freshness': website.get('data_freshness'),
|
||||
'confidence_level': website.get('confidence_level'),
|
||||
'analysis_status': website.get('status')
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
logger.debug(
|
||||
"AI Structured Autofill: context presence | website=%s research=%s api=%s session=%s",
|
||||
"AI Structured Autofill: personalized context | website=%s research=%s api=%s session=%s",
|
||||
bool(website), bool(research), bool(api_keys), bool(session)
|
||||
)
|
||||
logger.debug(
|
||||
"AI Structured Autofill: website keys=%s research keys=%s",
|
||||
len(list(website.keys())) if hasattr(website, 'keys') else 0,
|
||||
len(list(research.keys())) if hasattr(research, 'keys') else 0,
|
||||
"AI Structured Autofill: personalization data | writing_style=%s target_audience=%s content_type=%s",
|
||||
bool(writing_style), bool(target_audience), bool(content_type)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return summary
|
||||
|
||||
def _extract_available_services(self, api_keys: Dict[str, Any]) -> List[str]:
|
||||
"""Extract available services from API keys."""
|
||||
services = []
|
||||
providers = api_keys.get('providers', [])
|
||||
|
||||
# Map providers to services
|
||||
provider_service_map = {
|
||||
'google_search_console': ['SEO Analytics', 'Search Performance'],
|
||||
'google_analytics': ['Web Analytics', 'User Behavior'],
|
||||
'semrush': ['Competitive Analysis', 'Keyword Research'],
|
||||
'ahrefs': ['Backlink Analysis', 'SEO Tools'],
|
||||
'moz': ['SEO Tools', 'Rank Tracking'],
|
||||
'social_media': ['Social Media Analytics', 'Social Listening']
|
||||
}
|
||||
|
||||
for provider in providers:
|
||||
if provider in provider_service_map:
|
||||
services.extend(provider_service_map[provider])
|
||||
|
||||
return list(set(services)) # Remove duplicates
|
||||
|
||||
def _build_schema(self) -> Dict[str, Any]:
|
||||
# Simplified schema following Gemini best practices
|
||||
# Reduce complexity by flattening nested structures and simplifying constraints
|
||||
@@ -167,47 +228,109 @@ class AIStructuredAutofillService:
|
||||
return schema
|
||||
|
||||
def _build_prompt(self, context_summary: Dict[str, Any]) -> str:
|
||||
# Ultra-simplified prompt to avoid JSON parsing issues
|
||||
prompt = (
|
||||
"Generate a JSON object with exactly 30 fields for content strategy. Use this exact format:\n\n"
|
||||
|
||||
'{\n'
|
||||
'"business_objectives": "Increase traffic and leads",\n'
|
||||
'"target_metrics": "25% growth, 15% conversion",\n'
|
||||
'"content_budget": 3000,\n'
|
||||
'"team_size": 3,\n'
|
||||
'"implementation_timeline": "6 months",\n'
|
||||
'"market_share": "15%",\n'
|
||||
'"competitive_position": "Leader",\n'
|
||||
'"performance_metrics": "Current metrics data",\n'
|
||||
'"content_preferences": "Blog posts, videos",\n'
|
||||
'"consumption_patterns": "Peak hours 9-11 AM",\n'
|
||||
'"audience_pain_points": "Time constraints, complexity",\n'
|
||||
'"buying_journey": "Awareness to Decision",\n'
|
||||
'"seasonal_trends": "Q1 planning, Q2 execution",\n'
|
||||
'"engagement_metrics": "3.5% engagement rate",\n'
|
||||
'"top_competitors": "Competitor A, B, C",\n'
|
||||
'"competitor_content_strategies": "Educational content approach",\n'
|
||||
'"market_gaps": "AI tools, automation guides",\n'
|
||||
'"industry_trends": "AI integration, video content",\n'
|
||||
'"emerging_trends": "Voice search, interactive content",\n'
|
||||
'"preferred_formats": "Blog posts, videos, infographics",\n'
|
||||
'"content_mix": "70% educational, 30% promotional",\n'
|
||||
'"content_frequency": "Weekly",\n'
|
||||
'"optimal_timing": "Tuesday/Thursday 10 AM",\n'
|
||||
'"quality_metrics": "SEO score >90, engagement >3%",\n'
|
||||
'"editorial_guidelines": "Professional tone, actionable insights",\n'
|
||||
'"brand_voice": "Professional",\n'
|
||||
'"traffic_sources": "Organic search, social media",\n'
|
||||
'"conversion_rates": "15% conversion, $200 CPA",\n'
|
||||
'"content_roi_targets": "15% conversion, 3:1 ROI",\n'
|
||||
'"ab_testing_capabilities": true\n'
|
||||
'}\n\n'
|
||||
|
||||
f"Business context: {json.dumps(context_summary, indent=2)}\n\n"
|
||||
"Generate the complete JSON with all 30 fields:"
|
||||
)
|
||||
logger.debug("AI Structured Autofill: ultra-simplified prompt (%d chars)", len(prompt))
|
||||
# Build personalized prompt using actual user data
|
||||
user_profile = context_summary.get('user_profile', {})
|
||||
content_analysis = context_summary.get('content_analysis', {})
|
||||
audience_insights = context_summary.get('audience_insights', {})
|
||||
ai_recommendations = context_summary.get('ai_recommendations', {})
|
||||
research_config = context_summary.get('research_config', {})
|
||||
api_capabilities = context_summary.get('api_capabilities', {})
|
||||
|
||||
# Extract specific personalization data
|
||||
website_url = user_profile.get('website_url', 'your website')
|
||||
writing_tone = content_analysis.get('writing_style', {}).get('tone', 'professional')
|
||||
target_demographics = audience_insights.get('demographics', ['professionals'])
|
||||
industry_focus = audience_insights.get('industry_focus', 'general')
|
||||
expertise_level = audience_insights.get('expertise_level', 'intermediate')
|
||||
primary_content_type = content_analysis.get('content_type', {}).get('primary_type', 'blog')
|
||||
research_depth = research_config.get('research_depth', 'Standard')
|
||||
available_services = api_capabilities.get('available_services', [])
|
||||
|
||||
# Build personalized context description
|
||||
personalization_context = f"""
|
||||
PERSONALIZED CONTEXT FOR {website_url.upper()}:
|
||||
|
||||
🎯 YOUR BUSINESS PROFILE:
|
||||
- Website: {website_url}
|
||||
- Industry Focus: {industry_focus}
|
||||
- Business Size: {user_profile.get('business_size', 'SME')}
|
||||
- Region: {user_profile.get('region', 'Global')}
|
||||
|
||||
📝 YOUR CONTENT ANALYSIS:
|
||||
- Current Writing Tone: {writing_tone}
|
||||
- Primary Content Type: {primary_content_type}
|
||||
- Target Demographics: {', '.join(target_demographics) if isinstance(target_demographics, list) else target_demographics}
|
||||
- Audience Expertise Level: {expertise_level}
|
||||
- Content Purpose: {content_analysis.get('content_type', {}).get('purpose', 'informational')}
|
||||
|
||||
🔍 YOUR AUDIENCE INSIGHTS:
|
||||
- Pain Points: {audience_insights.get('pain_points', 'time constraints, complexity')}
|
||||
- Content Preferences: {audience_insights.get('content_preferences', 'educational, actionable')}
|
||||
- Industry Focus: {industry_focus}
|
||||
|
||||
🤖 AI RECOMMENDATIONS FOR YOUR SITE:
|
||||
- Recommended Tone: {ai_recommendations.get('recommended_tone', writing_tone)}
|
||||
- Recommended Content Type: {ai_recommendations.get('recommended_content_type', primary_content_type)}
|
||||
- Style Guidelines: {ai_recommendations.get('style_guidelines', 'professional, engaging')}
|
||||
|
||||
⚙️ YOUR RESEARCH CONFIGURATION:
|
||||
- Research Depth: {research_depth}
|
||||
- Content Types: {', '.join(research_config.get('content_types', ['blog', 'article'])) if isinstance(research_config.get('content_types'), list) else research_config.get('content_types', 'blog, article')}
|
||||
- Auto Research: {research_config.get('auto_research', True)}
|
||||
- Factual Content: {research_config.get('factual_content', True)}
|
||||
|
||||
🔧 YOUR AVAILABLE TOOLS:
|
||||
- Analytics Services: {', '.join(available_services) if available_services else 'Basic analytics'}
|
||||
- API Providers: {', '.join(api_capabilities.get('providers', [])) if api_capabilities.get('providers') else 'Manual tracking'}
|
||||
"""
|
||||
|
||||
# Personalized prompt with specific instructions
|
||||
prompt = f"""
|
||||
You are a content strategy expert analyzing {website_url}. Based on the detailed analysis of this website and user's onboarding data, generate a personalized content strategy with exactly 30 fields.
|
||||
|
||||
{personalization_context}
|
||||
|
||||
IMPORTANT: Make each field specific to {website_url} and the user's actual data. Avoid generic placeholder values. Use the real insights from their website analysis.
|
||||
|
||||
Generate a JSON object with exactly 30 fields using this exact format:
|
||||
|
||||
{{
|
||||
"business_objectives": "Specific goals for {website_url} based on {industry_focus} industry",
|
||||
"target_metrics": "Realistic KPIs for {user_profile.get('business_size', 'SME')} business",
|
||||
"content_budget": 3000,
|
||||
"team_size": 3,
|
||||
"implementation_timeline": "6 months",
|
||||
"market_share": "15%",
|
||||
"competitive_position": "Leader",
|
||||
"performance_metrics": "Current performance data for {website_url}",
|
||||
"content_preferences": "Content formats preferred by {', '.join(target_demographics) if isinstance(target_demographics, list) else target_demographics} audience",
|
||||
"consumption_patterns": "When {expertise_level} level audience consumes content",
|
||||
"audience_pain_points": "Specific challenges for {industry_focus} professionals",
|
||||
"buying_journey": "Customer journey for {industry_focus} industry",
|
||||
"seasonal_trends": "Seasonal patterns in {industry_focus}",
|
||||
"engagement_metrics": "Expected engagement for {writing_tone} tone content",
|
||||
"top_competitors": "Main competitors in {industry_focus} space",
|
||||
"competitor_content_strategies": "How competitors approach {primary_content_type} content",
|
||||
"market_gaps": "Opportunities in {industry_focus} content market",
|
||||
"industry_trends": "Current trends in {industry_focus} industry",
|
||||
"emerging_trends": "Upcoming trends for {industry_focus}",
|
||||
"preferred_formats": "Formats that work for {expertise_level} audience",
|
||||
"content_mix": "Optimal mix for {primary_content_type} focus",
|
||||
"content_frequency": "Frequency for {research_depth} research depth",
|
||||
"optimal_timing": "Best times for {target_demographics[0] if isinstance(target_demographics, list) and target_demographics else 'your'} audience",
|
||||
"quality_metrics": "Quality standards for {writing_tone} content",
|
||||
"editorial_guidelines": "Guidelines matching {writing_tone} tone",
|
||||
"brand_voice": "{writing_tone.title()}",
|
||||
"traffic_sources": "Primary sources for {industry_focus} content",
|
||||
"conversion_rates": "Realistic rates for {user_profile.get('business_size', 'SME')}",
|
||||
"content_roi_targets": "ROI goals for {industry_focus} content",
|
||||
"ab_testing_capabilities": true
|
||||
}}
|
||||
|
||||
Generate the complete JSON with all 30 fields personalized for {website_url}:
|
||||
"""
|
||||
|
||||
logger.debug("AI Structured Autofill: personalized prompt (%d chars)", len(prompt))
|
||||
return prompt
|
||||
|
||||
def _normalize_value(self, key: str, value: Any) -> Any:
|
||||
@@ -459,7 +582,15 @@ class AIStructuredAutofillService:
|
||||
raw_value = last_result.get(key)
|
||||
norm_value = self._normalize_value(key, raw_value)
|
||||
if norm_value is not None and norm_value != "" and norm_value != []:
|
||||
fields[key] = { 'value': norm_value, 'source': 'ai_refresh', 'confidence': 0.8 }
|
||||
# Add personalization metadata to each field
|
||||
personalized_metadata = self._add_personalization_metadata(key, norm_value, context_summary)
|
||||
fields[key] = {
|
||||
'value': norm_value,
|
||||
'source': 'ai_refresh',
|
||||
'confidence': 0.8,
|
||||
'personalized': True,
|
||||
'personalization_data': personalized_metadata
|
||||
}
|
||||
sources[key] = 'ai_refresh'
|
||||
non_null_keys.append(key)
|
||||
else:
|
||||
@@ -479,31 +610,93 @@ class AIStructuredAutofillService:
|
||||
'performance_analytics': ['traffic_sources', 'conversion_rates', 'content_roi_targets', 'ab_testing_capabilities']
|
||||
}
|
||||
|
||||
# Log category-wise success rates
|
||||
for category, category_fields in field_categories.items():
|
||||
generated_in_category = [f for f in category_fields if f in non_null_keys]
|
||||
missing_in_category = [f for f in category_fields if f in missing_fields]
|
||||
logger.info("📊 %s: %d/%d fields generated (%s missing: %s)",
|
||||
category.upper(), len(generated_in_category), len(category_fields),
|
||||
len(missing_in_category), missing_in_category)
|
||||
generated_count = len([f for f in category_fields if f in non_null_keys])
|
||||
missing_count = len([f for f in category_fields if f in missing_fields])
|
||||
logger.info(f"📊 {category.upper()}: {generated_count}/{len(category_fields)} fields generated ({missing_count} missing: {[f for f in category_fields if f in missing_fields]})")
|
||||
|
||||
success_rate = self._calculate_success_rate(last_result)
|
||||
logger.info(f"AI structured autofill completed | non_null_fields={len(non_null_keys)} missing={len(missing_fields)} success_rate={success_rate:.1f}% attempts={self.max_retries + 1}")
|
||||
|
||||
payload = {
|
||||
return {
|
||||
'fields': fields,
|
||||
'sources': sources,
|
||||
'meta': {
|
||||
'ai_used': len(non_null_keys) > 0,
|
||||
'ai_used': True,
|
||||
'ai_overrides_count': len(non_null_keys),
|
||||
'ai_override_fields': non_null_keys,
|
||||
'ai_only': True,
|
||||
'missing_fields': missing_fields,
|
||||
'success_rate': success_rate,
|
||||
'attempts': self.max_retries + 1
|
||||
'attempts': self.max_retries + 1,
|
||||
'personalization_level': 'high',
|
||||
'data_sources_used': list(set(sources.values())),
|
||||
'website_analyzed': context_summary.get('user_profile', {}).get('website_url'),
|
||||
'generated_at': datetime.utcnow().isoformat()
|
||||
}
|
||||
}
|
||||
|
||||
def _add_personalization_metadata(self, field_key: str, value: Any, context_summary: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Add personalization metadata to explain how the value was personalized."""
|
||||
user_profile = context_summary.get('user_profile', {})
|
||||
content_analysis = context_summary.get('content_analysis', {})
|
||||
audience_insights = context_summary.get('audience_insights', {})
|
||||
ai_recommendations = context_summary.get('ai_recommendations', {})
|
||||
|
||||
website_url = user_profile.get('website_url', 'your website')
|
||||
writing_tone = content_analysis.get('writing_style', {}).get('tone', 'professional')
|
||||
industry_focus = audience_insights.get('industry_focus', 'general')
|
||||
expertise_level = audience_insights.get('expertise_level', 'intermediate')
|
||||
|
||||
# Create personalized explanation for each field
|
||||
personalization_explanations = {
|
||||
'business_objectives': f"Based on {industry_focus} industry analysis and {user_profile.get('business_size', 'SME')} business profile",
|
||||
'target_metrics': f"Realistic KPIs for {user_profile.get('business_size', 'SME')} business in {industry_focus}",
|
||||
'content_budget': f"Budget recommendation based on {user_profile.get('business_size', 'SME')} scale and {industry_focus} content needs",
|
||||
'team_size': f"Team size optimized for {user_profile.get('business_size', 'SME')} business and {content_analysis.get('content_type', {}).get('primary_type', 'blog')} content",
|
||||
'implementation_timeline': f"Timeline based on {user_profile.get('business_size', 'SME')} resources and {industry_focus} complexity",
|
||||
'market_share': f"Market position analysis for {industry_focus} industry",
|
||||
'competitive_position': f"Competitive analysis for {industry_focus} market",
|
||||
'performance_metrics': f"Current performance data from {website_url} analysis",
|
||||
'content_preferences': f"Formats preferred by {', '.join(audience_insights.get('demographics', ['professionals']))} audience",
|
||||
'consumption_patterns': f"Patterns for {expertise_level} level audience in {industry_focus}",
|
||||
'audience_pain_points': f"Specific challenges for {industry_focus} professionals",
|
||||
'buying_journey': f"Customer journey mapped for {industry_focus} industry",
|
||||
'seasonal_trends': f"Seasonal patterns specific to {industry_focus} content",
|
||||
'engagement_metrics': f"Expected engagement for {writing_tone} tone content",
|
||||
'top_competitors': f"Main competitors in {industry_focus} space",
|
||||
'competitor_content_strategies': f"Competitor analysis for {industry_focus} content strategies",
|
||||
'market_gaps': f"Opportunities identified in {industry_focus} content market",
|
||||
'industry_trends': f"Current trends in {industry_focus} industry",
|
||||
'emerging_trends': f"Upcoming trends for {industry_focus} content",
|
||||
'preferred_formats': f"Formats optimized for {expertise_level} audience",
|
||||
'content_mix': f"Optimal mix for {content_analysis.get('content_type', {}).get('primary_type', 'blog')} focus",
|
||||
'content_frequency': f"Frequency based on {context_summary.get('research_config', {}).get('research_depth', 'Standard')} research depth",
|
||||
'optimal_timing': f"Best times for {audience_insights.get('demographics', ['professionals'])[0] if isinstance(audience_insights.get('demographics'), list) and audience_insights.get('demographics') else 'your'} audience",
|
||||
'quality_metrics': f"Quality standards for {writing_tone} content",
|
||||
'editorial_guidelines': f"Guidelines matching {writing_tone} tone from {website_url} analysis",
|
||||
'brand_voice': f"Voice derived from {writing_tone} tone analysis of {website_url}",
|
||||
'traffic_sources': f"Primary sources for {industry_focus} content",
|
||||
'conversion_rates': f"Realistic rates for {user_profile.get('business_size', 'SME')} business",
|
||||
'content_roi_targets': f"ROI goals for {industry_focus} content",
|
||||
'ab_testing_capabilities': f"A/B testing availability based on {user_profile.get('business_size', 'SME')} capabilities"
|
||||
}
|
||||
|
||||
return {
|
||||
'explanation': personalization_explanations.get(field_key, f"Personalized for {website_url}"),
|
||||
'data_sources': {
|
||||
'website_analysis': bool(context_summary.get('content_analysis')),
|
||||
'audience_insights': bool(context_summary.get('audience_insights')),
|
||||
'ai_recommendations': bool(context_summary.get('ai_recommendations')),
|
||||
'research_config': bool(context_summary.get('research_config'))
|
||||
},
|
||||
'personalization_factors': {
|
||||
'website_url': website_url,
|
||||
'industry_focus': industry_focus,
|
||||
'writing_tone': writing_tone,
|
||||
'expertise_level': expertise_level,
|
||||
'business_size': user_profile.get('business_size', 'SME')
|
||||
}
|
||||
}
|
||||
logger.info("AI structured autofill completed | non_null_fields=%d missing=%d success_rate=%.1f%% attempts=%d",
|
||||
len(non_null_keys), len(missing_fields), success_rate, self.max_retries + 1)
|
||||
return payload
|
||||
|
||||
def _extract_fields_from_raw_response(self, result: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Extract fields from malformed JSON response using regex patterns."""
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
"""
|
||||
Core Content Strategy Services
|
||||
Main orchestration and core functionality.
|
||||
Core Module
|
||||
Core strategy service and essential components.
|
||||
"""
|
||||
|
||||
from .strategy_service import EnhancedStrategyService
|
||||
from .field_mappings import STRATEGIC_INPUT_FIELDS
|
||||
from .constants import SERVICE_CONSTANTS
|
||||
|
||||
__all__ = ['EnhancedStrategyService', 'STRATEGIC_INPUT_FIELDS', 'SERVICE_CONSTANTS']
|
||||
__all__ = [
|
||||
'EnhancedStrategyService',
|
||||
'STRATEGIC_INPUT_FIELDS',
|
||||
'SERVICE_CONSTANTS'
|
||||
]
|
||||
@@ -9,12 +9,14 @@ from datetime import datetime
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
# Import database models
|
||||
from models.enhanced_strategy_models import EnhancedContentStrategy, EnhancedAIAnalysisResult
|
||||
from models.enhanced_strategy_models import EnhancedContentStrategy, EnhancedAIAnalysisResult, OnboardingDataIntegration
|
||||
from models.onboarding import OnboardingSession, WebsiteAnalysis, ResearchPreferences, APIKey
|
||||
|
||||
# Import modular services
|
||||
from ..ai_analysis.ai_recommendations import AIRecommendationsService
|
||||
from ..ai_analysis.prompt_engineering import PromptEngineeringService
|
||||
from ..ai_analysis.quality_validation import QualityValidationService
|
||||
from ..ai_analysis.strategy_analyzer import StrategyAnalyzer
|
||||
|
||||
# Import onboarding services
|
||||
from ..onboarding.data_integration import OnboardingDataIntegrationService
|
||||
@@ -29,6 +31,13 @@ from ..performance.health_monitoring import HealthMonitoringService
|
||||
# Import utils services
|
||||
from ..utils.data_processors import DataProcessorService
|
||||
from ..utils.validators import ValidationService
|
||||
from ..utils.strategy_utils import (
|
||||
extract_content_preferences_from_style,
|
||||
extract_brand_voice_from_guidelines,
|
||||
extract_editorial_guidelines_from_style,
|
||||
create_field_mappings,
|
||||
calculate_data_quality_scores
|
||||
)
|
||||
|
||||
# Import core components
|
||||
from .field_mappings import STRATEGIC_INPUT_FIELDS
|
||||
@@ -39,11 +48,15 @@ logger = logging.getLogger(__name__)
|
||||
class EnhancedStrategyService:
|
||||
"""Enhanced content strategy service with modular architecture."""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, db_service: Optional[Any] = None):
|
||||
# Store db_service for compatibility
|
||||
self.db_service = db_service
|
||||
|
||||
# Initialize AI analysis services
|
||||
self.ai_recommendations_service = AIRecommendationsService()
|
||||
self.prompt_engineering_service = PromptEngineeringService()
|
||||
self.quality_validation_service = QualityValidationService()
|
||||
self.strategy_analyzer = StrategyAnalyzer()
|
||||
|
||||
# Initialize onboarding services
|
||||
self.onboarding_data_service = OnboardingDataIntegrationService()
|
||||
@@ -59,8 +72,292 @@ class EnhancedStrategyService:
|
||||
self.data_processor_service = DataProcessorService()
|
||||
self.validation_service = ValidationService()
|
||||
|
||||
async def create_enhanced_strategy(self, strategy_data: Dict[str, Any], user_id: int, db: Session) -> EnhancedContentStrategy:
|
||||
"""Create enhanced content strategy with all integrations."""
|
||||
async def create_enhanced_strategy(self, strategy_data: Dict[str, Any], db: Session) -> Dict[str, Any]:
|
||||
"""Create a new enhanced content strategy with 30+ strategic inputs."""
|
||||
try:
|
||||
logger.info(f"Creating enhanced content strategy: {strategy_data.get('name', 'Unknown')}")
|
||||
|
||||
# Extract user_id from strategy_data
|
||||
user_id = strategy_data.get('user_id')
|
||||
if not user_id:
|
||||
raise ValueError("user_id is required for creating enhanced strategy")
|
||||
|
||||
# Create the enhanced strategy
|
||||
enhanced_strategy = EnhancedContentStrategy(
|
||||
user_id=user_id,
|
||||
name=strategy_data.get('name', 'Enhanced Content Strategy'),
|
||||
industry=strategy_data.get('industry'),
|
||||
|
||||
# Business Context
|
||||
business_objectives=strategy_data.get('business_objectives'),
|
||||
target_metrics=strategy_data.get('target_metrics'),
|
||||
content_budget=strategy_data.get('content_budget'),
|
||||
team_size=strategy_data.get('team_size'),
|
||||
implementation_timeline=strategy_data.get('implementation_timeline'),
|
||||
market_share=strategy_data.get('market_share'),
|
||||
competitive_position=strategy_data.get('competitive_position'),
|
||||
performance_metrics=strategy_data.get('performance_metrics'),
|
||||
|
||||
# Audience Intelligence
|
||||
content_preferences=strategy_data.get('content_preferences'),
|
||||
consumption_patterns=strategy_data.get('consumption_patterns'),
|
||||
audience_pain_points=strategy_data.get('audience_pain_points'),
|
||||
buying_journey=strategy_data.get('buying_journey'),
|
||||
seasonal_trends=strategy_data.get('seasonal_trends'),
|
||||
engagement_metrics=strategy_data.get('engagement_metrics'),
|
||||
|
||||
# Competitive Intelligence
|
||||
top_competitors=strategy_data.get('top_competitors'),
|
||||
competitor_content_strategies=strategy_data.get('competitor_content_strategies'),
|
||||
market_gaps=strategy_data.get('market_gaps'),
|
||||
industry_trends=strategy_data.get('industry_trends'),
|
||||
emerging_trends=strategy_data.get('emerging_trends'),
|
||||
|
||||
# Content Strategy
|
||||
preferred_formats=strategy_data.get('preferred_formats'),
|
||||
content_mix=strategy_data.get('content_mix'),
|
||||
content_frequency=strategy_data.get('content_frequency'),
|
||||
optimal_timing=strategy_data.get('optimal_timing'),
|
||||
quality_metrics=strategy_data.get('quality_metrics'),
|
||||
editorial_guidelines=strategy_data.get('editorial_guidelines'),
|
||||
brand_voice=strategy_data.get('brand_voice'),
|
||||
|
||||
# Performance & Analytics
|
||||
traffic_sources=strategy_data.get('traffic_sources'),
|
||||
conversion_rates=strategy_data.get('conversion_rates'),
|
||||
content_roi_targets=strategy_data.get('content_roi_targets'),
|
||||
ab_testing_capabilities=strategy_data.get('ab_testing_capabilities', False),
|
||||
|
||||
# Legacy fields
|
||||
target_audience=strategy_data.get('target_audience'),
|
||||
content_pillars=strategy_data.get('content_pillars'),
|
||||
ai_recommendations=strategy_data.get('ai_recommendations')
|
||||
)
|
||||
|
||||
# Calculate completion percentage
|
||||
enhanced_strategy.calculate_completion_percentage()
|
||||
|
||||
# Add to database
|
||||
db.add(enhanced_strategy)
|
||||
db.commit()
|
||||
db.refresh(enhanced_strategy)
|
||||
|
||||
# Integrate onboarding data if available
|
||||
await self._enhance_strategy_with_onboarding_data(enhanced_strategy, user_id, db)
|
||||
|
||||
# Generate comprehensive AI recommendations
|
||||
try:
|
||||
# Generate AI recommendations without timeout (allow natural processing time)
|
||||
await self.strategy_analyzer.generate_comprehensive_ai_recommendations(enhanced_strategy, db)
|
||||
logger.info(f"✅ AI recommendations generated successfully for strategy: {enhanced_strategy.id}")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ AI recommendations generation failed for strategy: {enhanced_strategy.id}: {str(e)} - continuing without AI recommendations")
|
||||
# Continue without AI recommendations
|
||||
|
||||
# Cache the strategy
|
||||
await self.caching_service.cache_strategy(enhanced_strategy.id, enhanced_strategy.to_dict())
|
||||
|
||||
logger.info(f"✅ Enhanced strategy created successfully: {enhanced_strategy.id}")
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Enhanced content strategy created successfully",
|
||||
"strategy": enhanced_strategy.to_dict(),
|
||||
"strategy_id": enhanced_strategy.id,
|
||||
"completion_percentage": enhanced_strategy.completion_percentage
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error creating enhanced strategy: {str(e)}")
|
||||
db.rollback()
|
||||
raise
|
||||
|
||||
async def get_enhanced_strategies(self, user_id: Optional[int] = None, strategy_id: Optional[int] = None, db: Session = None) -> Dict[str, Any]:
|
||||
"""Get enhanced content strategies with comprehensive data and AI recommendations."""
|
||||
try:
|
||||
logger.info(f"🚀 Starting enhanced strategy analysis for user: {user_id}, strategy: {strategy_id}")
|
||||
|
||||
# Use db_service if available, otherwise use direct db
|
||||
if self.db_service and hasattr(self.db_service, 'db'):
|
||||
# Use db_service methods
|
||||
if strategy_id:
|
||||
strategy = await self.db_service.get_enhanced_strategy(strategy_id)
|
||||
strategies = [strategy] if strategy else []
|
||||
else:
|
||||
strategies = await self.db_service.get_enhanced_strategies(user_id)
|
||||
else:
|
||||
# Fallback to direct db access
|
||||
if not db:
|
||||
raise ValueError("Database session is required when db_service is not available")
|
||||
|
||||
# Build query
|
||||
query = db.query(EnhancedContentStrategy)
|
||||
|
||||
if user_id:
|
||||
query = query.filter(EnhancedContentStrategy.user_id == user_id)
|
||||
|
||||
if strategy_id:
|
||||
query = query.filter(EnhancedContentStrategy.id == strategy_id)
|
||||
|
||||
# Get strategies
|
||||
strategies = query.all()
|
||||
|
||||
if not strategies:
|
||||
logger.warning("⚠️ No enhanced strategies found")
|
||||
return {
|
||||
"status": "not_found",
|
||||
"message": "No enhanced content strategies found",
|
||||
"strategies": [],
|
||||
"total_count": 0,
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
# Process each strategy
|
||||
enhanced_strategies = []
|
||||
for strategy in strategies:
|
||||
# Calculate completion percentage
|
||||
if hasattr(strategy, 'calculate_completion_percentage'):
|
||||
strategy.calculate_completion_percentage()
|
||||
|
||||
# Get AI analysis results
|
||||
ai_analysis = await self.strategy_analyzer.get_latest_ai_analysis(strategy.id, db) if db else None
|
||||
|
||||
# Get onboarding data integration
|
||||
onboarding_integration = await self.strategy_analyzer.get_onboarding_integration(strategy.id, db) if db else None
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
strategy_dict.update({
|
||||
'ai_analysis': ai_analysis,
|
||||
'onboarding_integration': onboarding_integration,
|
||||
'completion_percentage': getattr(strategy, 'completion_percentage', 0)
|
||||
})
|
||||
|
||||
enhanced_strategies.append(strategy_dict)
|
||||
|
||||
logger.info(f"✅ Retrieved {len(enhanced_strategies)} enhanced strategies")
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Enhanced content strategies retrieved successfully",
|
||||
"strategies": enhanced_strategies,
|
||||
"total_count": len(enhanced_strategies),
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error retrieving enhanced strategies: {str(e)}")
|
||||
raise
|
||||
|
||||
async def _enhance_strategy_with_onboarding_data(self, strategy: EnhancedContentStrategy, user_id: int, db: Session) -> None:
|
||||
"""Enhance strategy with intelligent auto-population from onboarding data."""
|
||||
try:
|
||||
logger.info(f"Enhancing strategy with onboarding data for user: {user_id}")
|
||||
|
||||
# Get onboarding session
|
||||
onboarding_session = db.query(OnboardingSession).filter(
|
||||
OnboardingSession.user_id == user_id
|
||||
).first()
|
||||
|
||||
if not onboarding_session:
|
||||
logger.info("No onboarding session found for user")
|
||||
return
|
||||
|
||||
# Get website analysis data
|
||||
website_analysis = db.query(WebsiteAnalysis).filter(
|
||||
WebsiteAnalysis.session_id == onboarding_session.id
|
||||
).first()
|
||||
|
||||
# Get research preferences data
|
||||
research_preferences = db.query(ResearchPreferences).filter(
|
||||
ResearchPreferences.session_id == onboarding_session.id
|
||||
).first()
|
||||
|
||||
# Get API keys data
|
||||
api_keys = db.query(APIKey).filter(
|
||||
APIKey.session_id == onboarding_session.id
|
||||
).all()
|
||||
|
||||
# Auto-populate fields from onboarding data
|
||||
auto_populated_fields = {}
|
||||
data_sources = {}
|
||||
|
||||
if website_analysis:
|
||||
# Extract content preferences from writing style
|
||||
if website_analysis.writing_style:
|
||||
strategy.content_preferences = extract_content_preferences_from_style(
|
||||
website_analysis.writing_style
|
||||
)
|
||||
auto_populated_fields['content_preferences'] = 'website_analysis'
|
||||
|
||||
# Extract target audience from analysis
|
||||
if website_analysis.target_audience:
|
||||
strategy.target_audience = website_analysis.target_audience
|
||||
auto_populated_fields['target_audience'] = 'website_analysis'
|
||||
|
||||
# Extract brand voice from style guidelines
|
||||
if website_analysis.style_guidelines:
|
||||
strategy.brand_voice = extract_brand_voice_from_guidelines(
|
||||
website_analysis.style_guidelines
|
||||
)
|
||||
auto_populated_fields['brand_voice'] = 'website_analysis'
|
||||
|
||||
data_sources['website_analysis'] = website_analysis.to_dict()
|
||||
|
||||
if research_preferences:
|
||||
# Extract content types from research preferences
|
||||
if research_preferences.content_types:
|
||||
strategy.preferred_formats = research_preferences.content_types
|
||||
auto_populated_fields['preferred_formats'] = 'research_preferences'
|
||||
|
||||
# Extract writing style from preferences
|
||||
if research_preferences.writing_style:
|
||||
strategy.editorial_guidelines = extract_editorial_guidelines_from_style(
|
||||
research_preferences.writing_style
|
||||
)
|
||||
auto_populated_fields['editorial_guidelines'] = 'research_preferences'
|
||||
|
||||
data_sources['research_preferences'] = research_preferences.to_dict()
|
||||
|
||||
# Create onboarding data integration record
|
||||
integration = OnboardingDataIntegration(
|
||||
user_id=user_id,
|
||||
strategy_id=strategy.id,
|
||||
website_analysis_data=data_sources.get('website_analysis'),
|
||||
research_preferences_data=data_sources.get('research_preferences'),
|
||||
api_keys_data=[key.to_dict() for key in api_keys] if api_keys else None,
|
||||
auto_populated_fields=auto_populated_fields,
|
||||
field_mappings=create_field_mappings(),
|
||||
data_quality_scores=calculate_data_quality_scores(data_sources),
|
||||
confidence_levels={}, # Will be calculated by data quality service
|
||||
data_freshness={} # Will be calculated by data quality service
|
||||
)
|
||||
|
||||
db.add(integration)
|
||||
db.commit()
|
||||
|
||||
# Update strategy with onboarding data used
|
||||
strategy.onboarding_data_used = {
|
||||
'auto_populated_fields': auto_populated_fields,
|
||||
'data_sources': list(data_sources.keys()),
|
||||
'integration_id': integration.id
|
||||
}
|
||||
|
||||
logger.info(f"Strategy enhanced with onboarding data: {len(auto_populated_fields)} fields auto-populated")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error enhancing strategy with onboarding data: {str(e)}")
|
||||
# Don't raise error, just log it as this is enhancement, not core functionality
|
||||
|
||||
async def create_enhanced_strategy_legacy(self, strategy_data: Dict[str, Any], user_id: int, db: Session) -> EnhancedContentStrategy:
|
||||
"""Create enhanced content strategy with all integrations (legacy method for compatibility)."""
|
||||
try:
|
||||
logger.info(f"Creating enhanced strategy for user: {user_id}")
|
||||
|
||||
@@ -98,7 +395,6 @@ class EnhancedStrategyService:
|
||||
# Cache strategy data
|
||||
await self.caching_service.cache_strategy(strategy.id, strategy.to_dict())
|
||||
|
||||
logger.info(f"Enhanced strategy created successfully: {strategy.id}")
|
||||
return strategy
|
||||
|
||||
except Exception as e:
|
||||
@@ -107,12 +403,11 @@ class EnhancedStrategyService:
|
||||
raise
|
||||
|
||||
async def get_enhanced_strategy(self, strategy_id: int, db: Session) -> Optional[EnhancedContentStrategy]:
|
||||
"""Get enhanced strategy with cached data."""
|
||||
"""Get a single enhanced strategy by ID."""
|
||||
try:
|
||||
# Try to get from cache first
|
||||
# Try cache first
|
||||
cached_strategy = await self.caching_service.get_cached_strategy(strategy_id)
|
||||
if cached_strategy:
|
||||
logger.info(f"Retrieved strategy {strategy_id} from cache")
|
||||
return cached_strategy
|
||||
|
||||
# Get from database
|
||||
@@ -128,22 +423,20 @@ class EnhancedStrategyService:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting enhanced strategy: {str(e)}")
|
||||
return None
|
||||
raise
|
||||
|
||||
async def update_enhanced_strategy(self, strategy_id: int, update_data: Dict[str, Any], db: Session) -> Optional[EnhancedContentStrategy]:
|
||||
"""Update enhanced strategy."""
|
||||
"""Update an enhanced strategy."""
|
||||
try:
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
# Get existing strategy
|
||||
strategy = await self.get_enhanced_strategy(strategy_id, db)
|
||||
if not strategy:
|
||||
return None
|
||||
|
||||
# Validate update data
|
||||
validation_result = self.validation_service.validate_strategy_data(update_data)
|
||||
if not validation_result['is_valid']:
|
||||
logger.error(f"Strategy update validation failed: {validation_result['errors']}")
|
||||
logger.error(f"Update validation failed: {validation_result['errors']}")
|
||||
raise ValueError(f"Invalid update data: {'; '.join(validation_result['errors'])}")
|
||||
|
||||
# Update strategy fields
|
||||
@@ -153,18 +446,17 @@ class EnhancedStrategyService:
|
||||
|
||||
strategy.updated_at = datetime.utcnow()
|
||||
|
||||
# Check if AI recommendations should be regenerated
|
||||
if self._should_regenerate_ai_recommendations(update_data):
|
||||
await self.strategy_analyzer.generate_comprehensive_ai_recommendations(strategy, db)
|
||||
|
||||
# Save to database
|
||||
db.commit()
|
||||
db.refresh(strategy)
|
||||
|
||||
# Invalidate cache
|
||||
await self.caching_service.invalidate_cache('strategy_cache', str(strategy_id))
|
||||
# Update cache
|
||||
await self.caching_service.cache_strategy(strategy_id, strategy.to_dict())
|
||||
|
||||
# Regenerate AI recommendations if needed
|
||||
if self._should_regenerate_ai_recommendations(update_data):
|
||||
await self.ai_recommendations_service.generate_comprehensive_recommendations(strategy, db)
|
||||
|
||||
logger.info(f"Enhanced strategy updated successfully: {strategy_id}")
|
||||
return strategy
|
||||
|
||||
except Exception as e:
|
||||
@@ -173,177 +465,105 @@ class EnhancedStrategyService:
|
||||
raise
|
||||
|
||||
async def get_onboarding_data(self, user_id: int, db: Session) -> Dict[str, Any]:
|
||||
"""Get onboarding data for auto-population."""
|
||||
"""Get onboarding data for a user."""
|
||||
try:
|
||||
# Try to get from cache first
|
||||
cached_data = await self.caching_service.get_cached_onboarding_data(user_id)
|
||||
if cached_data:
|
||||
logger.info(f"Retrieved onboarding data for user {user_id} from cache")
|
||||
return cached_data
|
||||
|
||||
# Process onboarding data
|
||||
onboarding_data = await self._process_onboarding_data(user_id, db)
|
||||
|
||||
# Cache the data
|
||||
await self.caching_service.cache_onboarding_data(user_id, onboarding_data)
|
||||
|
||||
return onboarding_data
|
||||
|
||||
return await self.data_processor_service.get_onboarding_data(user_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting onboarding data: {str(e)}")
|
||||
return {}
|
||||
raise
|
||||
|
||||
async def get_ai_analysis(self, strategy_id: int, analysis_type: str, db: Session) -> Optional[Dict[str, Any]]:
|
||||
"""Get AI analysis results."""
|
||||
"""Get AI analysis for a strategy."""
|
||||
try:
|
||||
# Try to get from cache first
|
||||
cached_analysis = await self.caching_service.get_cached_ai_analysis(strategy_id, analysis_type)
|
||||
if cached_analysis:
|
||||
logger.info(f"Retrieved AI analysis for strategy {strategy_id} from cache")
|
||||
return cached_analysis
|
||||
|
||||
# Get from database
|
||||
analysis = db.query(EnhancedAIAnalysisResult).filter(
|
||||
EnhancedAIAnalysisResult.strategy_id == strategy_id,
|
||||
EnhancedAIAnalysisResult.analysis_type == analysis_type
|
||||
).order_by(EnhancedAIAnalysisResult.created_at.desc()).first()
|
||||
|
||||
if analysis:
|
||||
analysis_data = analysis.to_dict()
|
||||
# Cache the analysis
|
||||
await self.caching_service.cache_ai_analysis(strategy_id, analysis_type, analysis_data)
|
||||
return analysis_data
|
||||
|
||||
return None
|
||||
|
||||
return await self.strategy_analyzer.get_latest_ai_analysis(strategy_id, db)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting AI analysis: {str(e)}")
|
||||
return None
|
||||
raise
|
||||
|
||||
async def get_system_health(self, db: Session) -> Dict[str, Any]:
|
||||
"""Get system health status."""
|
||||
try:
|
||||
return await self.health_monitoring_service.check_system_health(
|
||||
db,
|
||||
self.caching_service,
|
||||
self.ai_recommendations_service
|
||||
)
|
||||
return await self.health_monitoring_service.get_system_health(db)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting system health: {str(e)}")
|
||||
return {
|
||||
'overall_status': 'error',
|
||||
'error': str(e),
|
||||
'timestamp': datetime.utcnow().isoformat()
|
||||
}
|
||||
raise
|
||||
|
||||
async def get_performance_report(self) -> Dict[str, Any]:
|
||||
"""Get performance optimization report."""
|
||||
"""Get performance report."""
|
||||
try:
|
||||
return await self.performance_optimization_service.get_performance_report()
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting performance report: {str(e)}")
|
||||
return {
|
||||
'error': str(e),
|
||||
'timestamp': datetime.utcnow().isoformat()
|
||||
}
|
||||
raise
|
||||
|
||||
async def _process_onboarding_data(self, user_id: int, db: Session) -> Dict[str, Any]:
|
||||
"""Process onboarding data for a user."""
|
||||
"""Process onboarding data for strategy creation."""
|
||||
try:
|
||||
# Get integrated onboarding data
|
||||
integrated_data = await self.onboarding_data_service.process_onboarding_data(user_id, db)
|
||||
|
||||
# Assess data quality
|
||||
quality_assessment = self.data_quality_service.assess_onboarding_data_quality(integrated_data)
|
||||
|
||||
# Add quality assessment to integrated data
|
||||
integrated_data['quality_assessment'] = quality_assessment
|
||||
|
||||
return integrated_data
|
||||
|
||||
return await self.data_processor_service.get_onboarding_data(user_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing onboarding data: {str(e)}")
|
||||
return {}
|
||||
raise
|
||||
|
||||
def _merge_strategy_with_onboarding(self, strategy_data: Dict[str, Any], field_transformations: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Merge strategy data with onboarding field transformations."""
|
||||
try:
|
||||
"""Merge strategy data with onboarding data."""
|
||||
merged_data = strategy_data.copy()
|
||||
|
||||
# Add auto-populated fields from onboarding data
|
||||
if 'fields' in field_transformations:
|
||||
for field_name, field_value in field_transformations['fields'].items():
|
||||
if field_name not in merged_data or not merged_data[field_name]:
|
||||
merged_data[field_name] = field_value
|
||||
|
||||
# Add data sources information
|
||||
if 'sources' in field_transformations:
|
||||
merged_data['data_sources'] = field_transformations['sources']
|
||||
for field, transformation in field_transformations.items():
|
||||
if field not in merged_data or merged_data[field] is None:
|
||||
merged_data[field] = transformation.get('value')
|
||||
|
||||
return merged_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error merging strategy with onboarding: {str(e)}")
|
||||
return strategy_data
|
||||
|
||||
def _should_regenerate_ai_recommendations(self, update_data: Dict[str, Any]) -> bool:
|
||||
"""Determine if AI recommendations should be regenerated."""
|
||||
try:
|
||||
# Fields that would trigger AI recommendation regeneration
|
||||
ai_trigger_fields = [
|
||||
'business_objectives', 'target_metrics', 'content_budget',
|
||||
'team_size', 'implementation_timeline', 'market_share',
|
||||
'competitive_position', 'content_preferences', 'audience_pain_points',
|
||||
'top_competitors', 'industry_trends'
|
||||
"""Determine if AI recommendations should be regenerated based on updates."""
|
||||
critical_fields = [
|
||||
'business_objectives', 'target_metrics', 'industry',
|
||||
'content_preferences', 'target_audience', 'competitive_position'
|
||||
]
|
||||
|
||||
return any(field in update_data for field in ai_trigger_fields)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking if AI recommendations should be regenerated: {str(e)}")
|
||||
return False
|
||||
return any(field in update_data for field in critical_fields)
|
||||
|
||||
def get_strategic_input_fields(self) -> List[Dict[str, Any]]:
|
||||
"""Get strategic input field definitions."""
|
||||
"""Get strategic input fields configuration."""
|
||||
return STRATEGIC_INPUT_FIELDS
|
||||
|
||||
def get_service_constants(self) -> Dict[str, Any]:
|
||||
"""Get service configuration constants."""
|
||||
"""Get service constants."""
|
||||
return SERVICE_CONSTANTS
|
||||
|
||||
async def validate_strategy_data(self, strategy_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Validate strategy data using the validation service."""
|
||||
"""Validate strategy data."""
|
||||
try:
|
||||
return self.validation_service.validate_strategy_data(strategy_data)
|
||||
except Exception as e:
|
||||
logger.error(f"Error validating strategy data: {str(e)}")
|
||||
return {
|
||||
'is_valid': False,
|
||||
'errors': [f"Validation error: {str(e)}"],
|
||||
'warnings': [],
|
||||
'field_validations': {},
|
||||
'validation_timestamp': datetime.utcnow().isoformat()
|
||||
}
|
||||
raise
|
||||
|
||||
async def process_data_for_output(self, data: Dict[str, Any], output_format: str = 'json') -> Union[str, Dict[str, Any]]:
|
||||
"""Process data for different output formats."""
|
||||
"""Process data for specific output format."""
|
||||
try:
|
||||
return self.data_processor_service.format_data_for_output(data, output_format)
|
||||
if output_format == 'json':
|
||||
return data
|
||||
elif output_format == 'xml':
|
||||
# Convert to XML format
|
||||
return self._convert_to_xml(data)
|
||||
else:
|
||||
raise ValueError(f"Unsupported output format: {output_format}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing data for output: {str(e)}")
|
||||
return str(data)
|
||||
raise
|
||||
|
||||
async def optimize_strategy_operation(self, operation_name: str, operation_func, *args, **kwargs) -> Dict[str, Any]:
|
||||
"""Optimize strategy operations with performance monitoring."""
|
||||
"""Optimize strategy operation with performance monitoring."""
|
||||
try:
|
||||
return await self.performance_optimization_service.optimize_response_time(
|
||||
return await self.performance_optimization_service.optimize_operation(
|
||||
operation_name, operation_func, *args, **kwargs
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error optimizing strategy operation: {str(e)}")
|
||||
return {
|
||||
'result': None,
|
||||
'response_time': 0.0,
|
||||
'optimization_suggestions': ['Error occurred during optimization'],
|
||||
'performance_status': 'error'
|
||||
}
|
||||
raise
|
||||
|
||||
def _convert_to_xml(self, data: Dict[str, Any]) -> str:
|
||||
"""Convert data to XML format (placeholder implementation)."""
|
||||
# This would be implemented with proper XML conversion
|
||||
return f"<strategy>{str(data)}</strategy>"
|
||||
@@ -3,7 +3,54 @@ Utils Module
|
||||
Data processing and validation utilities.
|
||||
"""
|
||||
|
||||
from .data_processors import DataProcessorService
|
||||
from .data_processors import (
|
||||
DataProcessorService,
|
||||
get_onboarding_data,
|
||||
transform_onboarding_data_to_fields,
|
||||
get_data_sources,
|
||||
get_detailed_input_data_points,
|
||||
get_fallback_onboarding_data,
|
||||
get_website_analysis_data,
|
||||
get_research_preferences_data,
|
||||
get_api_keys_data
|
||||
)
|
||||
from .validators import ValidationService
|
||||
from .strategy_utils import (
|
||||
StrategyUtils,
|
||||
calculate_strategic_scores,
|
||||
extract_market_positioning,
|
||||
extract_competitive_advantages,
|
||||
extract_strategic_risks,
|
||||
extract_opportunity_analysis,
|
||||
initialize_caches,
|
||||
calculate_data_quality_scores,
|
||||
extract_content_preferences_from_style,
|
||||
extract_brand_voice_from_guidelines,
|
||||
extract_editorial_guidelines_from_style,
|
||||
create_field_mappings
|
||||
)
|
||||
|
||||
__all__ = ['DataProcessorService', 'ValidationService']
|
||||
__all__ = [
|
||||
'DataProcessorService',
|
||||
'get_onboarding_data',
|
||||
'transform_onboarding_data_to_fields',
|
||||
'get_data_sources',
|
||||
'get_detailed_input_data_points',
|
||||
'get_fallback_onboarding_data',
|
||||
'get_website_analysis_data',
|
||||
'get_research_preferences_data',
|
||||
'get_api_keys_data',
|
||||
'ValidationService',
|
||||
'StrategyUtils',
|
||||
'calculate_strategic_scores',
|
||||
'extract_market_positioning',
|
||||
'extract_competitive_advantages',
|
||||
'extract_strategic_risks',
|
||||
'extract_opportunity_analysis',
|
||||
'initialize_caches',
|
||||
'calculate_data_quality_scores',
|
||||
'extract_content_preferences_from_style',
|
||||
'extract_brand_voice_from_guidelines',
|
||||
'extract_editorial_guidelines_from_style',
|
||||
'create_field_mappings'
|
||||
]
|
||||
@@ -1,451 +1,539 @@
|
||||
"""
|
||||
Data Processor Service
|
||||
Data processing utilities.
|
||||
Data processing utilities for content strategy operations.
|
||||
Provides functions for transforming onboarding data into strategy fields,
|
||||
managing data sources, and processing various data types.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import json
|
||||
import re
|
||||
from typing import Dict, Any, List, Optional, Union
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Any, Optional, Union
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models.onboarding import OnboardingSession, WebsiteAnalysis, ResearchPreferences, APIKey
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DataProcessorService:
|
||||
"""Service for data processing utilities."""
|
||||
"""Service for processing and transforming data for content strategy operations."""
|
||||
|
||||
def __init__(self):
|
||||
self.cleaning_patterns = {
|
||||
'html_tags': re.compile(r'<[^>]+>'),
|
||||
'extra_whitespace': re.compile(r'\s+'),
|
||||
'special_chars': re.compile(r'[^\w\s\-.,!?;:()]'),
|
||||
'multiple_spaces': re.compile(r'\s{2,}'),
|
||||
'leading_trailing_spaces': re.compile(r'^\s+|\s+$')
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
async def get_onboarding_data(self, user_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Get comprehensive onboarding data for intelligent auto-population via AutoFillService.
|
||||
|
||||
Args:
|
||||
user_id: The user ID to get onboarding data for
|
||||
|
||||
Returns:
|
||||
Dictionary containing comprehensive onboarding data
|
||||
"""
|
||||
try:
|
||||
from services.database import get_db_session
|
||||
from ..autofill import AutoFillService
|
||||
temp_db = get_db_session()
|
||||
try:
|
||||
service = AutoFillService(temp_db)
|
||||
payload = await service.get_autofill(user_id)
|
||||
self.logger.info(f"Retrieved comprehensive onboarding data for user {user_id}")
|
||||
return payload
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting onboarding data: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
temp_db.close()
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting onboarding data: {str(e)}")
|
||||
raise
|
||||
|
||||
def transform_onboarding_data_to_fields(self, processed_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Transform processed onboarding data into field-specific format for frontend.
|
||||
|
||||
Args:
|
||||
processed_data: Dictionary containing processed onboarding data
|
||||
|
||||
Returns:
|
||||
Dictionary with field-specific data for strategy builder
|
||||
"""
|
||||
fields = {}
|
||||
|
||||
website_data = processed_data.get('website_analysis', {})
|
||||
research_data = processed_data.get('research_preferences', {})
|
||||
api_data = processed_data.get('api_keys_data', {})
|
||||
session_data = processed_data.get('onboarding_session', {})
|
||||
|
||||
# Business Context Fields
|
||||
if 'content_goals' in website_data and website_data.get('content_goals'):
|
||||
fields['business_objectives'] = {
|
||||
'value': website_data.get('content_goals'),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
|
||||
# Prefer explicit target_metrics; otherwise derive from performance_metrics
|
||||
if website_data.get('target_metrics'):
|
||||
fields['target_metrics'] = {
|
||||
'value': website_data.get('target_metrics'),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
elif website_data.get('performance_metrics'):
|
||||
fields['target_metrics'] = {
|
||||
'value': website_data.get('performance_metrics'),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
|
||||
# Content budget: website data preferred, else onboarding session budget
|
||||
if website_data.get('content_budget') is not None:
|
||||
fields['content_budget'] = {
|
||||
'value': website_data.get('content_budget'),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
elif isinstance(session_data, dict) and session_data.get('budget') is not None:
|
||||
fields['content_budget'] = {
|
||||
'value': session_data.get('budget'),
|
||||
'source': 'onboarding_session',
|
||||
'confidence': 0.7
|
||||
}
|
||||
|
||||
# Team size: website data preferred, else onboarding session team_size
|
||||
if website_data.get('team_size') is not None:
|
||||
fields['team_size'] = {
|
||||
'value': website_data.get('team_size'),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
elif isinstance(session_data, dict) and session_data.get('team_size') is not None:
|
||||
fields['team_size'] = {
|
||||
'value': session_data.get('team_size'),
|
||||
'source': 'onboarding_session',
|
||||
'confidence': 0.7
|
||||
}
|
||||
|
||||
# Implementation timeline: website data preferred, else onboarding session timeline
|
||||
if website_data.get('implementation_timeline'):
|
||||
fields['implementation_timeline'] = {
|
||||
'value': website_data.get('implementation_timeline'),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
elif isinstance(session_data, dict) and session_data.get('timeline'):
|
||||
fields['implementation_timeline'] = {
|
||||
'value': session_data.get('timeline'),
|
||||
'source': 'onboarding_session',
|
||||
'confidence': 0.7
|
||||
}
|
||||
|
||||
# Market share: explicit if present; otherwise derive rough share from performance metrics if available
|
||||
if website_data.get('market_share'):
|
||||
fields['market_share'] = {
|
||||
'value': website_data.get('market_share'),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
elif website_data.get('performance_metrics'):
|
||||
fields['market_share'] = {
|
||||
'value': website_data.get('performance_metrics').get('estimated_market_share', None),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level')
|
||||
}
|
||||
|
||||
fields['performance_metrics'] = {
|
||||
'value': website_data.get('performance_metrics', {}),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
def transform_data_structure(self, data: Union[Dict, List, str], target_format: str = 'dict') -> Union[Dict, List, str]:
|
||||
"""Transform data between different structures."""
|
||||
try:
|
||||
if target_format == 'dict':
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
elif isinstance(data, list):
|
||||
return {str(i): item for i, item in enumerate(data)}
|
||||
elif isinstance(data, str):
|
||||
try:
|
||||
return json.loads(data)
|
||||
except json.JSONDecodeError:
|
||||
return {'value': data}
|
||||
else:
|
||||
return {'value': str(data)}
|
||||
|
||||
# Audience Intelligence Fields
|
||||
# Extract audience data from research_data structure
|
||||
audience_research = research_data.get('audience_research', {})
|
||||
content_prefs = research_data.get('content_preferences', {})
|
||||
|
||||
fields['content_preferences'] = {
|
||||
'value': content_prefs,
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['consumption_patterns'] = {
|
||||
'value': audience_research.get('consumption_patterns', {}),
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['audience_pain_points'] = {
|
||||
'value': audience_research.get('audience_pain_points', []),
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['buying_journey'] = {
|
||||
'value': audience_research.get('buying_journey', {}),
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['seasonal_trends'] = {
|
||||
'value': ['Q1: Planning', 'Q2: Execution', 'Q3: Optimization', 'Q4: Review'],
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.7)
|
||||
}
|
||||
|
||||
fields['engagement_metrics'] = {
|
||||
'value': {
|
||||
'avg_session_duration': website_data.get('performance_metrics', {}).get('avg_session_duration', 180),
|
||||
'bounce_rate': website_data.get('performance_metrics', {}).get('bounce_rate', 45.5),
|
||||
'pages_per_session': 2.5
|
||||
},
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
# Competitive Intelligence Fields
|
||||
fields['top_competitors'] = {
|
||||
'value': website_data.get('competitors', [
|
||||
'Competitor A - Industry Leader',
|
||||
'Competitor B - Emerging Player',
|
||||
'Competitor C - Niche Specialist'
|
||||
]),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['competitor_content_strategies'] = {
|
||||
'value': ['Educational content', 'Case studies', 'Thought leadership'],
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.7)
|
||||
}
|
||||
|
||||
fields['market_gaps'] = {
|
||||
'value': website_data.get('market_gaps', []),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['industry_trends'] = {
|
||||
'value': ['Digital transformation', 'AI/ML adoption', 'Remote work'],
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['emerging_trends'] = {
|
||||
'value': ['Voice search optimization', 'Video content', 'Interactive content'],
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.7)
|
||||
}
|
||||
|
||||
# Content Strategy Fields
|
||||
fields['preferred_formats'] = {
|
||||
'value': content_prefs.get('preferred_formats', [
|
||||
'Blog posts', 'Whitepapers', 'Webinars', 'Case studies', 'Videos'
|
||||
]),
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['content_mix'] = {
|
||||
'value': {
|
||||
'blog_posts': 40,
|
||||
'whitepapers': 20,
|
||||
'webinars': 15,
|
||||
'case_studies': 15,
|
||||
'videos': 10
|
||||
},
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['content_frequency'] = {
|
||||
'value': 'Weekly',
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['optimal_timing'] = {
|
||||
'value': {
|
||||
'best_days': ['Tuesday', 'Wednesday', 'Thursday'],
|
||||
'best_times': ['9:00 AM', '1:00 PM', '3:00 PM']
|
||||
},
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.7)
|
||||
}
|
||||
|
||||
fields['quality_metrics'] = {
|
||||
'value': {
|
||||
'readability_score': 8.5,
|
||||
'engagement_target': 5.0,
|
||||
'conversion_target': 2.0
|
||||
},
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['editorial_guidelines'] = {
|
||||
'value': {
|
||||
'tone': content_prefs.get('content_style', ['Professional', 'Educational']),
|
||||
'length': content_prefs.get('content_length', 'Medium (1000-2000 words)'),
|
||||
'formatting': ['Use headers', 'Include visuals', 'Add CTAs']
|
||||
},
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['brand_voice'] = {
|
||||
'value': {
|
||||
'tone': 'Professional yet approachable',
|
||||
'style': 'Educational and authoritative',
|
||||
'personality': 'Expert, helpful, trustworthy'
|
||||
},
|
||||
'source': 'research_preferences',
|
||||
'confidence': research_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
# Performance & Analytics Fields
|
||||
fields['traffic_sources'] = {
|
||||
'value': website_data.get('traffic_sources', {}),
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['conversion_rates'] = {
|
||||
'value': {
|
||||
'overall': website_data.get('performance_metrics', {}).get('conversion_rate', 3.2),
|
||||
'blog': 2.5,
|
||||
'landing_pages': 4.0,
|
||||
'email': 5.5
|
||||
},
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
fields['content_roi_targets'] = {
|
||||
'value': {
|
||||
'target_roi': 300,
|
||||
'cost_per_lead': 50,
|
||||
'lifetime_value': 500
|
||||
},
|
||||
'source': 'website_analysis',
|
||||
'confidence': website_data.get('confidence_level', 0.7)
|
||||
}
|
||||
|
||||
fields['ab_testing_capabilities'] = {
|
||||
'value': True,
|
||||
'source': 'api_keys_data',
|
||||
'confidence': api_data.get('confidence_level', 0.8)
|
||||
}
|
||||
|
||||
return fields
|
||||
|
||||
def get_data_sources(self, processed_data: Dict[str, Any]) -> Dict[str, str]:
|
||||
"""
|
||||
Get data sources for each field.
|
||||
|
||||
Args:
|
||||
processed_data: Dictionary containing processed data
|
||||
|
||||
elif target_format == 'list':
|
||||
if isinstance(data, list):
|
||||
return data
|
||||
elif isinstance(data, dict):
|
||||
return list(data.values())
|
||||
elif isinstance(data, str):
|
||||
return [data]
|
||||
else:
|
||||
return [str(data)]
|
||||
Returns:
|
||||
Dictionary mapping field names to their data sources
|
||||
"""
|
||||
sources = {}
|
||||
|
||||
# Map fields to their data sources
|
||||
website_fields = ['business_objectives', 'target_metrics', 'content_budget', 'team_size',
|
||||
'implementation_timeline', 'market_share', 'competitive_position',
|
||||
'performance_metrics', 'engagement_metrics', 'top_competitors',
|
||||
'competitor_content_strategies', 'market_gaps', 'industry_trends',
|
||||
'emerging_trends', 'traffic_sources', 'conversion_rates', 'content_roi_targets']
|
||||
|
||||
research_fields = ['content_preferences', 'consumption_patterns', 'audience_pain_points',
|
||||
'buying_journey', 'seasonal_trends', 'preferred_formats', 'content_mix',
|
||||
'content_frequency', 'optimal_timing', 'quality_metrics', 'editorial_guidelines',
|
||||
'brand_voice']
|
||||
|
||||
api_fields = ['ab_testing_capabilities']
|
||||
|
||||
for field in website_fields:
|
||||
sources[field] = 'website_analysis'
|
||||
|
||||
for field in research_fields:
|
||||
sources[field] = 'research_preferences'
|
||||
|
||||
for field in api_fields:
|
||||
sources[field] = 'api_keys_data'
|
||||
|
||||
return sources
|
||||
|
||||
def get_detailed_input_data_points(self, processed_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Get detailed input data points for transparency.
|
||||
|
||||
Args:
|
||||
processed_data: Dictionary containing processed data
|
||||
|
||||
elif target_format == 'string':
|
||||
if isinstance(data, str):
|
||||
return data
|
||||
elif isinstance(data, (dict, list)):
|
||||
return json.dumps(data, default=str)
|
||||
else:
|
||||
return str(data)
|
||||
|
||||
else:
|
||||
logger.warning(f"Unknown target format: {target_format}")
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error transforming data structure: {str(e)}")
|
||||
return data
|
||||
|
||||
def clean_text_data(self, text: str, cleaning_level: str = 'standard') -> str:
|
||||
"""Clean and normalize text data."""
|
||||
try:
|
||||
if not isinstance(text, str):
|
||||
text = str(text)
|
||||
|
||||
if cleaning_level == 'minimal':
|
||||
# Basic cleaning
|
||||
cleaned = self.cleaning_patterns['leading_trailing_spaces'].sub('', text)
|
||||
cleaned = self.cleaning_patterns['multiple_spaces'].sub(' ', cleaned)
|
||||
return cleaned.strip()
|
||||
|
||||
elif cleaning_level == 'standard':
|
||||
# Standard cleaning
|
||||
cleaned = self.cleaning_patterns['html_tags'].sub('', text)
|
||||
cleaned = self.cleaning_patterns['leading_trailing_spaces'].sub('', cleaned)
|
||||
cleaned = self.cleaning_patterns['multiple_spaces'].sub(' ', cleaned)
|
||||
return cleaned.strip()
|
||||
|
||||
elif cleaning_level == 'aggressive':
|
||||
# Aggressive cleaning
|
||||
cleaned = self.cleaning_patterns['html_tags'].sub('', text)
|
||||
cleaned = self.cleaning_patterns['special_chars'].sub('', cleaned)
|
||||
cleaned = self.cleaning_patterns['leading_trailing_spaces'].sub('', cleaned)
|
||||
cleaned = self.cleaning_patterns['multiple_spaces'].sub(' ', cleaned)
|
||||
return cleaned.strip()
|
||||
|
||||
else:
|
||||
logger.warning(f"Unknown cleaning level: {cleaning_level}")
|
||||
return text.strip()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error cleaning text data: {str(e)}")
|
||||
return str(text)
|
||||
|
||||
def clean_dict_data(self, data: Dict[str, Any], cleaning_level: str = 'standard') -> Dict[str, Any]:
|
||||
"""Clean dictionary data recursively."""
|
||||
try:
|
||||
cleaned_data = {}
|
||||
|
||||
for key, value in data.items():
|
||||
# Clean key
|
||||
cleaned_key = self.clean_text_data(str(key), cleaning_level)
|
||||
|
||||
# Clean value
|
||||
if isinstance(value, str):
|
||||
cleaned_value = self.clean_text_data(value, cleaning_level)
|
||||
elif isinstance(value, dict):
|
||||
cleaned_value = self.clean_dict_data(value, cleaning_level)
|
||||
elif isinstance(value, list):
|
||||
cleaned_value = [self.clean_text_data(str(item), cleaning_level) if isinstance(item, str) else item for item in value]
|
||||
else:
|
||||
cleaned_value = value
|
||||
|
||||
cleaned_data[cleaned_key] = cleaned_value
|
||||
|
||||
return cleaned_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error cleaning dict data: {str(e)}")
|
||||
return data
|
||||
|
||||
def enrich_data_with_metadata(self, data: Dict[str, Any], source: str = 'unknown') -> Dict[str, Any]:
|
||||
"""Enrich data with metadata."""
|
||||
try:
|
||||
enriched_data = data.copy()
|
||||
|
||||
# Add metadata
|
||||
enriched_data['_metadata'] = {
|
||||
'processed_at': datetime.utcnow().isoformat(),
|
||||
'source': source,
|
||||
'data_type': self._determine_data_type(data),
|
||||
'size': len(str(data)),
|
||||
'field_count': len(data) if isinstance(data, dict) else 0
|
||||
Returns:
|
||||
Dictionary with detailed data points
|
||||
"""
|
||||
return {
|
||||
'website_analysis': {
|
||||
'total_fields': len(processed_data.get('website_analysis', {})),
|
||||
'confidence_level': processed_data.get('website_analysis', {}).get('confidence_level', 0.8),
|
||||
'data_freshness': processed_data.get('website_analysis', {}).get('data_freshness', 'recent')
|
||||
},
|
||||
'research_preferences': {
|
||||
'total_fields': len(processed_data.get('research_preferences', {})),
|
||||
'confidence_level': processed_data.get('research_preferences', {}).get('confidence_level', 0.8),
|
||||
'data_freshness': processed_data.get('research_preferences', {}).get('data_freshness', 'recent')
|
||||
},
|
||||
'api_keys_data': {
|
||||
'total_fields': len(processed_data.get('api_keys_data', {})),
|
||||
'confidence_level': processed_data.get('api_keys_data', {}).get('confidence_level', 0.8),
|
||||
'data_freshness': processed_data.get('api_keys_data', {}).get('data_freshness', 'recent')
|
||||
}
|
||||
}
|
||||
|
||||
def get_fallback_onboarding_data(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get fallback onboarding data for compatibility.
|
||||
|
||||
Returns:
|
||||
Dictionary with fallback data (raises error as fallbacks are disabled)
|
||||
"""
|
||||
raise RuntimeError("Fallback onboarding data is disabled. Real data required.")
|
||||
|
||||
async def get_website_analysis_data(self, user_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Get website analysis data from onboarding.
|
||||
|
||||
Args:
|
||||
user_id: The user ID to get data for
|
||||
|
||||
return enriched_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error enriching data with metadata: {str(e)}")
|
||||
return data
|
||||
|
||||
def _determine_data_type(self, data: Any) -> str:
|
||||
"""Determine the type of data."""
|
||||
Returns:
|
||||
Dictionary with website analysis data
|
||||
"""
|
||||
try:
|
||||
if isinstance(data, dict):
|
||||
return 'object'
|
||||
elif isinstance(data, list):
|
||||
return 'array'
|
||||
elif isinstance(data, str):
|
||||
return 'string'
|
||||
elif isinstance(data, (int, float)):
|
||||
return 'number'
|
||||
elif isinstance(data, bool):
|
||||
return 'boolean'
|
||||
else:
|
||||
return 'unknown'
|
||||
|
||||
raise RuntimeError("Website analysis data retrieval not implemented. Real data required.")
|
||||
except Exception as e:
|
||||
logger.error(f"Error determining data type: {str(e)}")
|
||||
return 'unknown'
|
||||
|
||||
def validate_data_completeness(self, data: Dict[str, Any], required_fields: List[str]) -> Dict[str, Any]:
|
||||
"""Validate data completeness against required fields."""
|
||||
self.logger.error(f"Error getting website analysis data: {str(e)}")
|
||||
raise
|
||||
|
||||
async def get_research_preferences_data(self, user_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Get research preferences data from onboarding.
|
||||
|
||||
Args:
|
||||
user_id: The user ID to get data for
|
||||
|
||||
Returns:
|
||||
Dictionary with research preferences data
|
||||
"""
|
||||
try:
|
||||
validation_result = {
|
||||
'is_complete': True,
|
||||
'missing_fields': [],
|
||||
'present_fields': [],
|
||||
'completeness_score': 0.0,
|
||||
'validation_timestamp': datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
present_count = 0
|
||||
for field in required_fields:
|
||||
if field in data and data[field] is not None and data[field] != '':
|
||||
validation_result['present_fields'].append(field)
|
||||
present_count += 1
|
||||
else:
|
||||
validation_result['missing_fields'].append(field)
|
||||
|
||||
# Calculate completeness score
|
||||
if required_fields:
|
||||
validation_result['completeness_score'] = present_count / len(required_fields)
|
||||
validation_result['is_complete'] = validation_result['completeness_score'] >= 0.8
|
||||
|
||||
return validation_result
|
||||
|
||||
raise RuntimeError("Research preferences data retrieval not implemented. Real data required.")
|
||||
except Exception as e:
|
||||
logger.error(f"Error validating data completeness: {str(e)}")
|
||||
return {
|
||||
'is_complete': False,
|
||||
'missing_fields': required_fields,
|
||||
'present_fields': [],
|
||||
'completeness_score': 0.0,
|
||||
'validation_timestamp': datetime.utcnow().isoformat(),
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
def normalize_field_values(self, data: Dict[str, Any], field_mappings: Dict[str, str]) -> Dict[str, Any]:
|
||||
"""Normalize field values based on mappings."""
|
||||
self.logger.error(f"Error getting research preferences data: {str(e)}")
|
||||
raise
|
||||
|
||||
async def get_api_keys_data(self, user_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Get API keys and external data from onboarding.
|
||||
|
||||
Args:
|
||||
user_id: The user ID to get data for
|
||||
|
||||
Returns:
|
||||
Dictionary with API keys data
|
||||
"""
|
||||
try:
|
||||
normalized_data = {}
|
||||
|
||||
for original_field, normalized_field in field_mappings.items():
|
||||
if original_field in data:
|
||||
normalized_data[normalized_field] = data[original_field]
|
||||
|
||||
return normalized_data
|
||||
|
||||
raise RuntimeError("API keys/external data retrieval not implemented. Real data required.")
|
||||
except Exception as e:
|
||||
logger.error(f"Error normalizing field values: {str(e)}")
|
||||
return data
|
||||
self.logger.error(f"Error getting API keys data: {str(e)}")
|
||||
raise
|
||||
|
||||
async def process_website_analysis(self, website_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Process website analysis data (deprecated).
|
||||
|
||||
Args:
|
||||
website_data: Raw website analysis data
|
||||
|
||||
Returns:
|
||||
Processed website analysis data
|
||||
"""
|
||||
raise RuntimeError("Deprecated: use AutoFillService normalizers")
|
||||
|
||||
async def process_research_preferences(self, research_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Process research preferences data (deprecated).
|
||||
|
||||
Args:
|
||||
research_data: Raw research preferences data
|
||||
|
||||
Returns:
|
||||
Processed research preferences data
|
||||
"""
|
||||
raise RuntimeError("Deprecated: use AutoFillService normalizers")
|
||||
|
||||
async def process_api_keys_data(self, api_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Process API keys data (deprecated).
|
||||
|
||||
Args:
|
||||
api_data: Raw API keys data
|
||||
|
||||
Returns:
|
||||
Processed API keys data
|
||||
"""
|
||||
raise RuntimeError("Deprecated: use AutoFillService normalizers")
|
||||
|
||||
def merge_data_sources(self, data_sources: List[Dict[str, Any]], merge_strategy: str = 'prefer_first') -> Dict[str, Any]:
|
||||
"""Merge multiple data sources."""
|
||||
try:
|
||||
if not data_sources:
|
||||
return {}
|
||||
|
||||
if len(data_sources) == 1:
|
||||
return data_sources[0]
|
||||
|
||||
merged_data = {}
|
||||
|
||||
if merge_strategy == 'prefer_first':
|
||||
# Prefer first non-empty value
|
||||
for source in data_sources:
|
||||
for key, value in source.items():
|
||||
if key not in merged_data or merged_data[key] is None or merged_data[key] == '':
|
||||
merged_data[key] = value
|
||||
|
||||
elif merge_strategy == 'prefer_last':
|
||||
# Prefer last non-empty value
|
||||
for source in data_sources:
|
||||
for key, value in source.items():
|
||||
if value is not None and value != '':
|
||||
merged_data[key] = value
|
||||
|
||||
elif merge_strategy == 'combine':
|
||||
# Combine all values
|
||||
for source in data_sources:
|
||||
for key, value in source.items():
|
||||
if key not in merged_data:
|
||||
merged_data[key] = []
|
||||
if isinstance(merged_data[key], list):
|
||||
merged_data[key].append(value)
|
||||
else:
|
||||
merged_data[key] = [merged_data[key], value]
|
||||
|
||||
elif merge_strategy == 'intersection':
|
||||
# Only include fields present in all sources
|
||||
common_keys = set(data_sources[0].keys())
|
||||
for source in data_sources[1:]:
|
||||
common_keys = common_keys.intersection(set(source.keys()))
|
||||
|
||||
for key in common_keys:
|
||||
values = [source[key] for source in data_sources if key in source]
|
||||
merged_data[key] = values[0] if values else None
|
||||
|
||||
return merged_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error merging data sources: {str(e)}")
|
||||
return data_sources[0] if data_sources else {}
|
||||
|
||||
def filter_data_by_criteria(self, data: Dict[str, Any], criteria: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Filter data based on criteria."""
|
||||
try:
|
||||
filtered_data = {}
|
||||
|
||||
for key, value in data.items():
|
||||
include_field = True
|
||||
|
||||
# Check if field should be included based on criteria
|
||||
if 'include_fields' in criteria and key not in criteria['include_fields']:
|
||||
include_field = False
|
||||
|
||||
if 'exclude_fields' in criteria and key in criteria['exclude_fields']:
|
||||
include_field = False
|
||||
|
||||
# Check value-based criteria
|
||||
if 'min_length' in criteria and isinstance(value, str) and len(value) < criteria['min_length']:
|
||||
include_field = False
|
||||
|
||||
if 'max_length' in criteria and isinstance(value, str) and len(value) > criteria['max_length']:
|
||||
include_field = False
|
||||
|
||||
if 'required_values' in criteria and key in criteria['required_values']:
|
||||
if value not in criteria['required_values'][key]:
|
||||
include_field = False
|
||||
|
||||
if include_field:
|
||||
filtered_data[key] = value
|
||||
|
||||
return filtered_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error filtering data by criteria: {str(e)}")
|
||||
return data
|
||||
# Standalone functions for backward compatibility
|
||||
async def get_onboarding_data(user_id: int) -> Dict[str, Any]:
|
||||
"""Get comprehensive onboarding data for intelligent auto-population via AutoFillService."""
|
||||
processor = DataProcessorService()
|
||||
return await processor.get_onboarding_data(user_id)
|
||||
|
||||
def format_data_for_output(self, data: Dict[str, Any], output_format: str = 'json') -> Union[str, Dict[str, Any]]:
|
||||
"""Format data for different output formats."""
|
||||
try:
|
||||
if output_format == 'json':
|
||||
return json.dumps(data, indent=2, default=str)
|
||||
|
||||
elif output_format == 'dict':
|
||||
return data
|
||||
|
||||
elif output_format == 'csv':
|
||||
# Convert to CSV format (simplified)
|
||||
csv_lines = []
|
||||
if data:
|
||||
# Headers
|
||||
headers = list(data.keys())
|
||||
csv_lines.append(','.join(headers))
|
||||
|
||||
# Values
|
||||
values = [str(data.get(header, '')) for header in headers]
|
||||
csv_lines.append(','.join(values))
|
||||
|
||||
return '\n'.join(csv_lines)
|
||||
|
||||
elif output_format == 'xml':
|
||||
# Convert to XML format (simplified)
|
||||
xml_lines = ['<?xml version="1.0" encoding="UTF-8"?>', '<data>']
|
||||
|
||||
for key, value in data.items():
|
||||
xml_lines.append(f' <{key}>{value}</{key}>')
|
||||
|
||||
xml_lines.append('</data>')
|
||||
return '\n'.join(xml_lines)
|
||||
|
||||
else:
|
||||
logger.warning(f"Unknown output format: {output_format}")
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error formatting data for output: {str(e)}")
|
||||
return str(data)
|
||||
|
||||
def validate_data_types(self, data: Dict[str, Any], type_schema: Dict[str, str]) -> Dict[str, Any]:
|
||||
"""Validate data types against a schema."""
|
||||
try:
|
||||
validation_result = {
|
||||
'is_valid': True,
|
||||
'type_errors': [],
|
||||
'validation_timestamp': datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
for field, expected_type in type_schema.items():
|
||||
if field in data:
|
||||
value = data[field]
|
||||
actual_type = self._determine_data_type(value)
|
||||
|
||||
if actual_type != expected_type:
|
||||
validation_result['type_errors'].append({
|
||||
'field': field,
|
||||
'expected_type': expected_type,
|
||||
'actual_type': actual_type,
|
||||
'value': value
|
||||
})
|
||||
validation_result['is_valid'] = False
|
||||
|
||||
return validation_result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error validating data types: {str(e)}")
|
||||
return {
|
||||
'is_valid': False,
|
||||
'type_errors': [{'error': str(e)}],
|
||||
'validation_timestamp': datetime.utcnow().isoformat()
|
||||
}
|
||||
def transform_onboarding_data_to_fields(processed_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Transform processed onboarding data into field-specific format for frontend."""
|
||||
processor = DataProcessorService()
|
||||
return processor.transform_onboarding_data_to_fields(processed_data)
|
||||
|
||||
def sanitize_sensitive_data(self, data: Dict[str, Any], sensitive_fields: List[str]) -> Dict[str, Any]:
|
||||
"""Sanitize sensitive data fields."""
|
||||
try:
|
||||
sanitized_data = data.copy()
|
||||
|
||||
for field in sensitive_fields:
|
||||
if field in sanitized_data:
|
||||
value = sanitized_data[field]
|
||||
if isinstance(value, str) and len(value) > 4:
|
||||
# Replace with asterisks, keeping first and last character
|
||||
sanitized_data[field] = value[0] + '*' * (len(value) - 2) + value[-1]
|
||||
else:
|
||||
sanitized_data[field] = '***'
|
||||
|
||||
return sanitized_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error sanitizing sensitive data: {str(e)}")
|
||||
return data
|
||||
|
||||
def calculate_data_statistics(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Calculate statistics about the data."""
|
||||
try:
|
||||
stats = {
|
||||
'total_fields': len(data),
|
||||
'string_fields': 0,
|
||||
'numeric_fields': 0,
|
||||
'boolean_fields': 0,
|
||||
'object_fields': 0,
|
||||
'array_fields': 0,
|
||||
'null_fields': 0,
|
||||
'empty_fields': 0,
|
||||
'average_field_length': 0.0
|
||||
}
|
||||
|
||||
total_length = 0
|
||||
field_count = 0
|
||||
|
||||
for key, value in data.items():
|
||||
if value is None:
|
||||
stats['null_fields'] += 1
|
||||
elif value == '':
|
||||
stats['empty_fields'] += 1
|
||||
else:
|
||||
data_type = self._determine_data_type(value)
|
||||
if data_type == 'string':
|
||||
stats['string_fields'] += 1
|
||||
total_length += len(str(value))
|
||||
field_count += 1
|
||||
elif data_type == 'number':
|
||||
stats['numeric_fields'] += 1
|
||||
elif data_type == 'boolean':
|
||||
stats['boolean_fields'] += 1
|
||||
elif data_type == 'object':
|
||||
stats['object_fields'] += 1
|
||||
elif data_type == 'array':
|
||||
stats['array_fields'] += 1
|
||||
|
||||
if field_count > 0:
|
||||
stats['average_field_length'] = total_length / field_count
|
||||
|
||||
return stats
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating data statistics: {str(e)}")
|
||||
return {
|
||||
'error': str(e),
|
||||
'total_fields': 0
|
||||
}
|
||||
def get_data_sources(processed_data: Dict[str, Any]) -> Dict[str, str]:
|
||||
"""Get data sources for each field."""
|
||||
processor = DataProcessorService()
|
||||
return processor.get_data_sources(processed_data)
|
||||
|
||||
|
||||
def get_detailed_input_data_points(processed_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Get detailed input data points for transparency."""
|
||||
processor = DataProcessorService()
|
||||
return processor.get_detailed_input_data_points(processed_data)
|
||||
|
||||
|
||||
def get_fallback_onboarding_data() -> Dict[str, Any]:
|
||||
"""Get fallback onboarding data for compatibility."""
|
||||
processor = DataProcessorService()
|
||||
return processor.get_fallback_onboarding_data()
|
||||
|
||||
|
||||
async def get_website_analysis_data(user_id: int) -> Dict[str, Any]:
|
||||
"""Get website analysis data from onboarding."""
|
||||
processor = DataProcessorService()
|
||||
return await processor.get_website_analysis_data(user_id)
|
||||
|
||||
|
||||
async def get_research_preferences_data(user_id: int) -> Dict[str, Any]:
|
||||
"""Get research preferences data from onboarding."""
|
||||
processor = DataProcessorService()
|
||||
return await processor.get_research_preferences_data(user_id)
|
||||
|
||||
|
||||
async def get_api_keys_data(user_id: int) -> Dict[str, Any]:
|
||||
"""Get API keys and external data from onboarding."""
|
||||
processor = DataProcessorService()
|
||||
return await processor.get_api_keys_data(user_id)
|
||||
@@ -0,0 +1,355 @@
|
||||
"""
|
||||
Strategy utility functions for analysis, scoring, and data processing.
|
||||
Provides utility functions for content strategy operations including strategic scoring,
|
||||
market positioning analysis, competitive advantages, risk assessment, and opportunity analysis.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Any, Optional, Union
|
||||
from datetime import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def calculate_strategic_scores(ai_recommendations: Dict[str, Any]) -> Dict[str, float]:
|
||||
"""
|
||||
Calculate strategic performance scores from AI recommendations.
|
||||
|
||||
Args:
|
||||
ai_recommendations: Dictionary containing AI analysis results
|
||||
|
||||
Returns:
|
||||
Dictionary with calculated strategic scores
|
||||
"""
|
||||
scores = {
|
||||
'overall_score': 0.0,
|
||||
'content_quality_score': 0.0,
|
||||
'engagement_score': 0.0,
|
||||
'conversion_score': 0.0,
|
||||
'innovation_score': 0.0
|
||||
}
|
||||
|
||||
# Calculate scores based on AI recommendations
|
||||
total_confidence = 0
|
||||
total_score = 0
|
||||
|
||||
for analysis_type, recommendations in ai_recommendations.items():
|
||||
if isinstance(recommendations, dict) and 'metrics' in recommendations:
|
||||
metrics = recommendations['metrics']
|
||||
score = metrics.get('score', 50)
|
||||
confidence = metrics.get('confidence', 0.5)
|
||||
|
||||
total_score += score * confidence
|
||||
total_confidence += confidence
|
||||
|
||||
if total_confidence > 0:
|
||||
scores['overall_score'] = total_score / total_confidence
|
||||
|
||||
# Set other scores based on overall score
|
||||
scores['content_quality_score'] = scores['overall_score'] * 1.1
|
||||
scores['engagement_score'] = scores['overall_score'] * 0.9
|
||||
scores['conversion_score'] = scores['overall_score'] * 0.95
|
||||
scores['innovation_score'] = scores['overall_score'] * 1.05
|
||||
|
||||
return scores
|
||||
|
||||
|
||||
def extract_market_positioning(ai_recommendations: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Extract market positioning insights from AI recommendations.
|
||||
|
||||
Args:
|
||||
ai_recommendations: Dictionary containing AI analysis results
|
||||
|
||||
Returns:
|
||||
Dictionary with market positioning data
|
||||
"""
|
||||
return {
|
||||
'industry_position': 'emerging',
|
||||
'competitive_advantage': 'AI-powered content',
|
||||
'market_share': '2.5%',
|
||||
'positioning_score': 4
|
||||
}
|
||||
|
||||
|
||||
def extract_competitive_advantages(ai_recommendations: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Extract competitive advantages from AI recommendations.
|
||||
|
||||
Args:
|
||||
ai_recommendations: Dictionary containing AI analysis results
|
||||
|
||||
Returns:
|
||||
List of competitive advantages with impact and implementation status
|
||||
"""
|
||||
return [
|
||||
{
|
||||
'advantage': 'AI-powered content creation',
|
||||
'impact': 'High',
|
||||
'implementation': 'In Progress'
|
||||
},
|
||||
{
|
||||
'advantage': 'Data-driven strategy',
|
||||
'impact': 'Medium',
|
||||
'implementation': 'Complete'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def extract_strategic_risks(ai_recommendations: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Extract strategic risks from AI recommendations.
|
||||
|
||||
Args:
|
||||
ai_recommendations: Dictionary containing AI analysis results
|
||||
|
||||
Returns:
|
||||
List of strategic risks with probability and impact assessment
|
||||
"""
|
||||
return [
|
||||
{
|
||||
'risk': 'Content saturation in market',
|
||||
'probability': 'Medium',
|
||||
'impact': 'High'
|
||||
},
|
||||
{
|
||||
'risk': 'Algorithm changes affecting reach',
|
||||
'probability': 'High',
|
||||
'impact': 'Medium'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def extract_opportunity_analysis(ai_recommendations: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Extract opportunity analysis from AI recommendations.
|
||||
|
||||
Args:
|
||||
ai_recommendations: Dictionary containing AI analysis results
|
||||
|
||||
Returns:
|
||||
List of opportunities with potential impact and implementation ease
|
||||
"""
|
||||
return [
|
||||
{
|
||||
'opportunity': 'Video content expansion',
|
||||
'potential_impact': 'High',
|
||||
'implementation_ease': 'Medium'
|
||||
},
|
||||
{
|
||||
'opportunity': 'Social media engagement',
|
||||
'potential_impact': 'Medium',
|
||||
'implementation_ease': 'High'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def initialize_caches() -> Dict[str, Any]:
|
||||
"""
|
||||
Initialize in-memory caches for strategy operations.
|
||||
|
||||
Returns:
|
||||
Dictionary with initialized cache structures
|
||||
"""
|
||||
return {
|
||||
'performance_metrics': {
|
||||
'response_times': [],
|
||||
'cache_hit_rates': {},
|
||||
'error_rates': {},
|
||||
'throughput_metrics': {}
|
||||
},
|
||||
'strategy_cache': {},
|
||||
'ai_analysis_cache': {},
|
||||
'onboarding_cache': {}
|
||||
}
|
||||
|
||||
|
||||
def calculate_data_quality_scores(data_sources: Dict[str, Any]) -> Dict[str, float]:
|
||||
"""
|
||||
Calculate data quality scores for different data sources.
|
||||
|
||||
Args:
|
||||
data_sources: Dictionary containing data source information
|
||||
|
||||
Returns:
|
||||
Dictionary with quality scores for each data source
|
||||
"""
|
||||
quality_scores = {}
|
||||
|
||||
for source_name, source_data in data_sources.items():
|
||||
if isinstance(source_data, dict):
|
||||
# Calculate quality based on data completeness and freshness
|
||||
completeness = source_data.get('completeness', 0.5)
|
||||
freshness = source_data.get('freshness', 0.5)
|
||||
confidence = source_data.get('confidence', 0.5)
|
||||
|
||||
# Weighted average of quality factors
|
||||
quality_score = (completeness * 0.4 + freshness * 0.3 + confidence * 0.3)
|
||||
quality_scores[source_name] = round(quality_score, 2)
|
||||
else:
|
||||
quality_scores[source_name] = 0.5 # Default score
|
||||
|
||||
return quality_scores
|
||||
|
||||
|
||||
def extract_content_preferences_from_style(writing_style: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Extract content preferences from writing style analysis.
|
||||
|
||||
Args:
|
||||
writing_style: Dictionary containing writing style analysis
|
||||
|
||||
Returns:
|
||||
Dictionary with extracted content preferences
|
||||
"""
|
||||
preferences = {
|
||||
'tone': writing_style.get('tone', 'professional'),
|
||||
'complexity': writing_style.get('complexity', 'intermediate'),
|
||||
'engagement_level': writing_style.get('engagement_level', 'medium'),
|
||||
'content_type': writing_style.get('content_type', 'blog')
|
||||
}
|
||||
|
||||
return preferences
|
||||
|
||||
|
||||
def extract_brand_voice_from_guidelines(style_guidelines: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Extract brand voice from style guidelines.
|
||||
|
||||
Args:
|
||||
style_guidelines: Dictionary containing style guidelines
|
||||
|
||||
Returns:
|
||||
Dictionary with extracted brand voice information
|
||||
"""
|
||||
brand_voice = {
|
||||
'tone': style_guidelines.get('tone', 'professional'),
|
||||
'personality': style_guidelines.get('personality', 'authoritative'),
|
||||
'style': style_guidelines.get('style', 'formal'),
|
||||
'voice_characteristics': style_guidelines.get('voice_characteristics', [])
|
||||
}
|
||||
|
||||
return brand_voice
|
||||
|
||||
|
||||
def extract_editorial_guidelines_from_style(writing_style: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Extract editorial guidelines from writing style analysis.
|
||||
|
||||
Args:
|
||||
writing_style: Dictionary containing writing style analysis
|
||||
|
||||
Returns:
|
||||
Dictionary with extracted editorial guidelines
|
||||
"""
|
||||
guidelines = {
|
||||
'sentence_structure': writing_style.get('sentence_structure', 'clear'),
|
||||
'vocabulary_level': writing_style.get('vocabulary_level', 'intermediate'),
|
||||
'paragraph_organization': writing_style.get('paragraph_organization', 'logical'),
|
||||
'style_rules': writing_style.get('style_rules', [])
|
||||
}
|
||||
|
||||
return guidelines
|
||||
|
||||
|
||||
def create_field_mappings() -> Dict[str, str]:
|
||||
"""
|
||||
Create field mappings for strategy data transformation.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping field names to their corresponding data sources
|
||||
"""
|
||||
return {
|
||||
'business_objectives': 'website_analysis',
|
||||
'target_metrics': 'research_preferences',
|
||||
'content_budget': 'onboarding_session',
|
||||
'team_size': 'onboarding_session',
|
||||
'implementation_timeline': 'onboarding_session',
|
||||
'market_share': 'website_analysis',
|
||||
'competitive_position': 'website_analysis',
|
||||
'performance_metrics': 'website_analysis',
|
||||
'content_preferences': 'website_analysis',
|
||||
'consumption_patterns': 'research_preferences',
|
||||
'audience_pain_points': 'website_analysis',
|
||||
'buying_journey': 'website_analysis',
|
||||
'seasonal_trends': 'research_preferences',
|
||||
'engagement_metrics': 'website_analysis',
|
||||
'top_competitors': 'website_analysis',
|
||||
'competitor_content_strategies': 'website_analysis',
|
||||
'market_gaps': 'website_analysis',
|
||||
'industry_trends': 'website_analysis',
|
||||
'emerging_trends': 'website_analysis',
|
||||
'preferred_formats': 'website_analysis',
|
||||
'content_mix': 'research_preferences',
|
||||
'content_frequency': 'research_preferences',
|
||||
'optimal_timing': 'research_preferences',
|
||||
'quality_metrics': 'website_analysis',
|
||||
'editorial_guidelines': 'website_analysis',
|
||||
'brand_voice': 'website_analysis',
|
||||
'traffic_sources': 'website_analysis',
|
||||
'conversion_rates': 'website_analysis',
|
||||
'content_roi_targets': 'website_analysis',
|
||||
'ab_testing_capabilities': 'onboarding_session'
|
||||
}
|
||||
|
||||
|
||||
class StrategyUtils:
|
||||
"""
|
||||
Utility class for strategy-related operations.
|
||||
Provides static methods for strategy analysis and data processing.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def calculate_strategic_scores(ai_recommendations: Dict[str, Any]) -> Dict[str, float]:
|
||||
"""Calculate strategic performance scores from AI recommendations."""
|
||||
return calculate_strategic_scores(ai_recommendations)
|
||||
|
||||
@staticmethod
|
||||
def extract_market_positioning(ai_recommendations: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Extract market positioning insights from AI recommendations."""
|
||||
return extract_market_positioning(ai_recommendations)
|
||||
|
||||
@staticmethod
|
||||
def extract_competitive_advantages(ai_recommendations: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""Extract competitive advantages from AI recommendations."""
|
||||
return extract_competitive_advantages(ai_recommendations)
|
||||
|
||||
@staticmethod
|
||||
def extract_strategic_risks(ai_recommendations: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""Extract strategic risks from AI recommendations."""
|
||||
return extract_strategic_risks(ai_recommendations)
|
||||
|
||||
@staticmethod
|
||||
def extract_opportunity_analysis(ai_recommendations: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""Extract opportunity analysis from AI recommendations."""
|
||||
return extract_opportunity_analysis(ai_recommendations)
|
||||
|
||||
@staticmethod
|
||||
def initialize_caches() -> Dict[str, Any]:
|
||||
"""Initialize in-memory caches for strategy operations."""
|
||||
return initialize_caches()
|
||||
|
||||
@staticmethod
|
||||
def calculate_data_quality_scores(data_sources: Dict[str, Any]) -> Dict[str, float]:
|
||||
"""Calculate data quality scores for different data sources."""
|
||||
return calculate_data_quality_scores(data_sources)
|
||||
|
||||
@staticmethod
|
||||
def extract_content_preferences_from_style(writing_style: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Extract content preferences from writing style analysis."""
|
||||
return extract_content_preferences_from_style(writing_style)
|
||||
|
||||
@staticmethod
|
||||
def extract_brand_voice_from_guidelines(style_guidelines: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Extract brand voice from style guidelines."""
|
||||
return extract_brand_voice_from_guidelines(style_guidelines)
|
||||
|
||||
@staticmethod
|
||||
def extract_editorial_guidelines_from_style(writing_style: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Extract editorial guidelines from writing style analysis."""
|
||||
return extract_editorial_guidelines_from_style(writing_style)
|
||||
|
||||
@staticmethod
|
||||
def create_field_mappings() -> Dict[str, str]:
|
||||
"""Create field mappings for strategy data transformation."""
|
||||
return create_field_mappings()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user