AI Analysis and Content Strategy fixes. Enhanced Strategy Routes refactoring.

This commit is contained in:
ajaysi
2026-01-10 19:32:50 +05:30
parent 0b63ae7fc1
commit 8193cdba67
298 changed files with 45678 additions and 10952 deletions

View File

@@ -726,9 +726,10 @@ async def get_latest_generated_strategy(
# Fallback: Check in-memory task status
if not hasattr(generate_comprehensive_strategy_polling, '_task_status'):
logger.warning("⚠️ No task status storage found")
return ResponseBuilder.create_not_found_response(
return ResponseBuilder.create_success_response(
data={"user_id": user_id, "strategy": None},
message="No strategy generation tasks found",
data={"user_id": user_id, "strategy": None}
status_code=200
)
# Debug: Log all task statuses
@@ -768,9 +769,10 @@ async def get_latest_generated_strategy(
)
else:
logger.info(f"⚠️ No completed strategies found for user: {user_id}")
return ResponseBuilder.create_not_found_response(
return ResponseBuilder.create_success_response(
data={"user_id": user_id, "strategy": None},
message="No completed strategy generation found",
data={"user_id": user_id, "strategy": None}
status_code=200
)
except Exception as e:

View File

@@ -39,51 +39,34 @@ async def get_enhanced_strategy_analytics(
strategy_id: int,
db: Session = Depends(get_db)
) -> Dict[str, Any]:
"""Get analytics data for an enhanced strategy."""
"""Get comprehensive analytics for an enhanced strategy."""
try:
logger.info(f"Getting analytics for strategy: {strategy_id}")
logger.info(f"🚀 Getting analytics for enhanced strategy: {strategy_id}")
# Check if strategy exists
strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
db_service = EnhancedStrategyDBService(db)
if not strategy:
raise HTTPException(
status_code=404,
detail=f"Enhanced strategy with ID {strategy_id} not found"
)
# Get strategy with analytics
strategies_with_analytics = await db_service.get_enhanced_strategies_with_analytics(
strategy_id=strategy_id
)
# Calculate completion statistics
strategy.calculate_completion_percentage()
if not strategies_with_analytics:
raise ContentPlanningErrorHandler.handle_not_found_error("Enhanced strategy", strategy_id)
# Get AI analysis results
ai_analyses = db.query(EnhancedAIAnalysisResult).filter(
EnhancedAIAnalysisResult.strategy_id == strategy_id
).order_by(EnhancedAIAnalysisResult.created_at.desc()).all()
strategy_analytics = strategies_with_analytics[0]
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"✅ Enhanced strategy analytics retrieved successfully: {strategy_id}")
logger.info(f"Retrieved analytics for strategy: {strategy_id}")
return ResponseBuilder.success_response(
message=SUCCESS_MESSAGES['analytics_retrieved'],
data=analytics_data
return ResponseBuilder.create_success_response(
message="Enhanced strategy analytics retrieved successfully",
data=strategy_analytics
)
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")
logger.error(f"Error getting enhanced strategy analytics: {str(e)}")
raise ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_analytics")
@router.get("/{strategy_id}/ai-analyses")
async def get_enhanced_strategy_ai_analysis(
@@ -91,43 +74,36 @@ async def get_enhanced_strategy_ai_analysis(
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."""
"""Get AI analysis history for an enhanced strategy."""
try:
logger.info(f"Getting AI analyses for strategy: {strategy_id}, limit: {limit}")
logger.info(f"🚀 Getting AI analysis for enhanced strategy: {strategy_id}")
# Check if strategy exists
strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
db_service = EnhancedStrategyDBService(db)
# Verify strategy exists
strategy = await db_service.get_enhanced_strategy(strategy_id)
if not strategy:
raise HTTPException(
status_code=404,
detail=f"Enhanced strategy with ID {strategy_id} not found"
)
raise ContentPlanningErrorHandler.handle_not_found_error("Enhanced strategy", strategy_id)
# Get AI analysis results
ai_analyses = db.query(EnhancedAIAnalysisResult).filter(
EnhancedAIAnalysisResult.strategy_id == strategy_id
).order_by(EnhancedAIAnalysisResult.created_at.desc()).limit(limit).all()
# Get AI analysis history
ai_analysis_history = await db_service.get_ai_analysis_history(strategy_id, limit)
analyses_data = [analysis.to_dict() for analysis in ai_analyses]
logger.info(f"✅ AI analysis history retrieved successfully: {strategy_id}")
logger.info(f"Retrieved {len(analyses_data)} AI analyses for strategy: {strategy_id}")
return ResponseBuilder.success_response(
message=SUCCESS_MESSAGES['ai_analyses_retrieved'],
return ResponseBuilder.create_success_response(
message="Enhanced strategy AI analysis retrieved successfully",
data={
"strategy_id": strategy_id,
"analyses": analyses_data,
"total_count": len(analyses_data)
"ai_analysis_history": ai_analysis_history,
"total_analyses": len(ai_analysis_history)
}
)
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")
logger.error(f"Error getting enhanced strategy AI analysis: {str(e)}")
raise ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_ai_analysis")
@router.get("/{strategy_id}/completion")
async def get_enhanced_strategy_completion_stats(
@@ -136,99 +112,67 @@ async def get_enhanced_strategy_completion_stats(
) -> Dict[str, Any]:
"""Get completion statistics for an enhanced strategy."""
try:
logger.info(f"Getting completion stats for strategy: {strategy_id}")
logger.info(f"🚀 Getting completion stats for enhanced strategy: {strategy_id}")
# Check if strategy exists
strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
db_service = EnhancedStrategyDBService(db)
# Get strategy
strategy = await db_service.get_enhanced_strategy(strategy_id)
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)
raise ContentPlanningErrorHandler.handle_not_found_error("Enhanced strategy", strategy_id)
# Calculate completion stats
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,
"total_fields": 30, # 30+ strategic inputs
"filled_fields": len([f for f in strategy.__dict__.keys() if getattr(strategy, f) is not None]),
"missing_fields": 30 - len([f for f in strategy.__dict__.keys() if getattr(strategy, f) is not None]),
"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'],
logger.info(f"✅ Completion stats retrieved successfully: {strategy_id}")
return ResponseBuilder.create_success_response(
message="Enhanced strategy completion stats retrieved successfully",
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")
logger.error(f"Error getting enhanced strategy completion stats: {str(e)}")
raise 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."""
"""Get onboarding data integration for an enhanced strategy."""
try:
logger.info(f"Getting onboarding integration for strategy: {strategy_id}")
logger.info(f"🚀 Getting onboarding integration for enhanced strategy: {strategy_id}")
# Check if strategy exists
strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
db_service = EnhancedStrategyDBService(db)
onboarding_integration = await db_service.get_onboarding_integration(strategy_id)
if not strategy:
raise HTTPException(
status_code=404,
detail=f"Enhanced strategy with ID {strategy_id} not found"
if not onboarding_integration:
return ResponseBuilder.create_success_response(
data={"strategy_id": strategy_id, "onboarding_integration": None},
message="No onboarding integration found for this strategy",
status_code=200
)
# Get onboarding integration data
onboarding_data = strategy.onboarding_data_used if hasattr(strategy, 'onboarding_data_used') else {}
logger.info(f"✅ Onboarding integration retrieved successfully: {strategy_id}")
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
return ResponseBuilder.create_success_response(
message="Enhanced strategy onboarding integration retrieved successfully",
data=onboarding_integration
)
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")
logger.error(f"Error getting onboarding integration: {str(e)}")
raise ContentPlanningErrorHandler.handle_general_error(e, "get_enhanced_strategy_onboarding_integration")
@router.post("/{strategy_id}/ai-recommendations")
async def generate_enhanced_ai_recommendations(
@@ -237,50 +181,36 @@ async def generate_enhanced_ai_recommendations(
) -> Dict[str, Any]:
"""Generate AI recommendations for an enhanced strategy."""
try:
logger.info(f"Generating AI recommendations for strategy: {strategy_id}")
logger.info(f"🚀 Generating AI recommendations for enhanced strategy: {strategy_id}")
# Check if strategy exists
strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
# Get strategy
db_service = EnhancedStrategyDBService(db)
strategy = await db_service.get_enhanced_strategy(strategy_id)
if not strategy:
raise HTTPException(
status_code=404,
detail=f"Enhanced strategy with ID {strategy_id} not found"
)
raise ContentPlanningErrorHandler.handle_not_found_error("Enhanced strategy", strategy_id)
# Generate AI recommendations
db_service = EnhancedStrategyDBService(db)
enhanced_service = EnhancedStrategyService(db_service)
# Pass user_id for subscription checks
user_id = str(strategy.user_id) if hasattr(strategy, 'user_id') else None
await enhanced_service._generate_comprehensive_ai_recommendations(strategy, db, user_id=user_id)
# 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()
}
# Get updated strategy data
updated_strategy = await db_service.get_enhanced_strategy(strategy_id)
logger.info(f"Generated AI recommendations for strategy: {strategy_id}")
return ResponseBuilder.success_response(
message=SUCCESS_MESSAGES['ai_recommendations_generated'],
data=recommendations
logger.info(f" AI recommendations generated successfully: {strategy_id}")
return ResponseBuilder.create_success_response(
message="Enhanced strategy AI recommendations generated successfully",
data=updated_strategy.to_dict()
)
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")
logger.error(f"Error generating AI recommendations: {str(e)}")
raise ContentPlanningErrorHandler.handle_general_error(e, "generate_enhanced_ai_recommendations")
@router.post("/{strategy_id}/ai-analysis/regenerate")
async def regenerate_enhanced_strategy_ai_analysis(
@@ -290,44 +220,33 @@ async def regenerate_enhanced_strategy_ai_analysis(
) -> Dict[str, Any]:
"""Regenerate AI analysis for an enhanced strategy."""
try:
logger.info(f"Regenerating AI analysis for strategy: {strategy_id}, type: {analysis_type}")
logger.info(f"🚀 Regenerating AI analysis for enhanced strategy: {strategy_id}, type: {analysis_type}")
# Check if strategy exists
strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
# Get strategy
db_service = EnhancedStrategyDBService(db)
strategy = await db_service.get_enhanced_strategy(strategy_id)
if not strategy:
raise HTTPException(
status_code=404,
detail=f"Enhanced strategy with ID {strategy_id} not found"
)
raise ContentPlanningErrorHandler.handle_not_found_error("Enhanced strategy", strategy_id)
# Regenerate AI analysis
db_service = EnhancedStrategyDBService(db)
enhanced_service = EnhancedStrategyService(db_service)
# Pass user_id for subscription checks
user_id = str(strategy.user_id) if hasattr(strategy, 'user_id') else None
await enhanced_service._generate_specialized_recommendations(strategy, analysis_type, db, user_id=user_id)
# 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"]
}
}
# Get updated strategy data
updated_strategy = await db_service.get_enhanced_strategy(strategy_id)
logger.info(f"Regenerated AI analysis for strategy: {strategy_id}")
return ResponseBuilder.success_response(
message=SUCCESS_MESSAGES['ai_analysis_regenerated'],
data=analysis_result
logger.info(f" AI analysis regenerated successfully: {strategy_id}")
return ResponseBuilder.create_success_response(
message="Enhanced strategy AI analysis regenerated successfully",
data=updated_strategy.to_dict()
)
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")
logger.error(f"Error regenerating AI analysis: {str(e)}")
raise ContentPlanningErrorHandler.handle_general_error(e, "regenerate_enhanced_strategy_ai_analysis")

View File

@@ -13,6 +13,9 @@ from datetime import datetime
# Import database
from services.database import get_db_session
# Import authentication middleware
from middleware.auth_middleware import get_current_user
# Import services
from ....services.enhanced_strategy_service import EnhancedStrategyService
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
@@ -24,6 +27,7 @@ from models.enhanced_strategy_models import EnhancedContentStrategy
from ....utils.error_handlers import ContentPlanningErrorHandler
from ....utils.response_builders import ResponseBuilder
from ....utils.constants import ERROR_MESSAGES, SUCCESS_MESSAGES
from ....utils.data_parsers import parse_strategy_data
router = APIRouter(tags=["Strategy CRUD"])
@@ -38,14 +42,26 @@ def get_db():
@router.post("/create")
async def create_enhanced_strategy(
strategy_data: Dict[str, Any],
current_user: Dict[str, Any] = Depends(get_current_user),
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')}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID in authentication token"
)
logger.info(f"Creating enhanced strategy: {strategy_data.get('name', 'Unknown')} for user: {clerk_user_id}")
# Override user_id from request body with authenticated user_id (security)
strategy_data['user_id'] = clerk_user_id
# Validate required fields
required_fields = ['user_id', 'name']
required_fields = ['name']
for field in required_fields:
if field not in strategy_data or not strategy_data[field]:
raise HTTPException(
@@ -53,85 +69,33 @@ async def create_enhanced_strategy(
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
# Parse and validate strategy data using shared utilities
cleaned_data, warnings = parse_strategy_data(strategy_data)
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])
# Log warnings if any
if warnings:
logger.warning(f" Strategy create warnings: {warnings}")
# Create strategy
db_service = EnhancedStrategyDBService(db)
enhanced_service = EnhancedStrategyService(db_service)
result = await enhanced_service.create_enhanced_strategy(strategy_data, db)
# Pass authenticated user_id for AI calls with subscription checks
result = await enhanced_service.create_enhanced_strategy(cleaned_data, db)
logger.info(f"Enhanced strategy created successfully: {result.get('strategy_id')}")
return ResponseBuilder.success_response(
message=SUCCESS_MESSAGES['strategy_created'],
data=result
logger.info(f"Enhanced strategy created successfully: {result.get('strategy_id') if isinstance(result, dict) else getattr(result, 'id', None)}")
response = ResponseBuilder.create_success_response(
data=result,
message=SUCCESS_MESSAGES['strategy_created']
)
# Include warnings if any
if warnings:
response['warnings'] = warnings
return response
except HTTPException:
raise
except Exception as e:
@@ -140,23 +104,36 @@ async def create_enhanced_strategy(
@router.get("/")
async def get_enhanced_strategies(
user_id: Optional[int] = Query(None, description="User ID to filter strategies"),
user_id: Optional[int] = Query(None, description="User ID to filter strategies (deprecated - use authenticated user)"),
strategy_id: Optional[int] = Query(None, description="Specific strategy ID"),
current_user: Dict[str, Any] = Depends(get_current_user),
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}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID in authentication token"
)
# Use authenticated user_id (override query parameter for security)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
logger.info(f"Getting enhanced strategies for authenticated user: {authenticated_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)
# Use authenticated user_id to ensure users can only see their own strategies
strategies_data = await enhanced_service.get_enhanced_strategies(authenticated_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
return ResponseBuilder.create_success_response(
data=strategies_data,
message=SUCCESS_MESSAGES['strategies_retrieved']
)
except Exception as e:
@@ -166,29 +143,47 @@ async def get_enhanced_strategies(
@router.get("/{strategy_id}")
async def get_enhanced_strategy_by_id(
strategy_id: int,
current_user: Dict[str, Any] = Depends(get_current_user),
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}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID in authentication token"
)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
logger.info(f"Getting enhanced strategy by ID: {strategy_id} for authenticated user: {authenticated_user_id}")
db_service = EnhancedStrategyDBService(db)
enhanced_service = EnhancedStrategyService(db_service)
strategies_data = await enhanced_service.get_enhanced_strategies(strategy_id=strategy_id, db=db)
strategies_data = await enhanced_service.get_enhanced_strategies(user_id=authenticated_user_id, 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"
detail=f"Enhanced strategy with ID {strategy_id} not found or you don't have access to it"
)
strategy = strategies_data["strategies"][0]
# Verify ownership
if strategy.get('user_id') != authenticated_user_id:
raise HTTPException(
status_code=403,
detail="You don't have permission to access this strategy"
)
logger.info(f"Retrieved strategy: {strategy.get('name')}")
return ResponseBuilder.success_response(
message=SUCCESS_MESSAGES['strategy_retrieved'],
data=strategy
return ResponseBuilder.create_success_response(
data=strategy,
message=SUCCESS_MESSAGES['strategy_retrieved']
)
except HTTPException:
@@ -201,13 +196,24 @@ async def get_enhanced_strategy_by_id(
async def update_enhanced_strategy(
strategy_id: int,
update_data: Dict[str, Any],
current_user: Dict[str, Any] = Depends(get_current_user),
db: Session = Depends(get_db)
) -> Dict[str, Any]:
"""Update an enhanced strategy."""
try:
logger.info(f"Updating enhanced strategy: {strategy_id}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID in authentication token"
)
# Check if strategy exists
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
logger.info(f"Updating enhanced strategy: {strategy_id} for authenticated user: {authenticated_user_id}")
# Check if strategy exists and verify ownership
existing_strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
@@ -218,6 +224,13 @@ async def update_enhanced_strategy(
detail=f"Enhanced strategy with ID {strategy_id} not found"
)
# Verify ownership
if existing_strategy.user_id != authenticated_user_id:
raise HTTPException(
status_code=403,
detail="You don't have permission to update this strategy"
)
# Update strategy fields
for field, value in update_data.items():
if hasattr(existing_strategy, field):
@@ -230,9 +243,9 @@ async def update_enhanced_strategy(
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()
return ResponseBuilder.create_success_response(
data=existing_strategy.to_dict(),
message=SUCCESS_MESSAGES['strategy_updated']
)
except HTTPException:
@@ -244,13 +257,24 @@ async def update_enhanced_strategy(
@router.delete("/{strategy_id}")
async def delete_enhanced_strategy(
strategy_id: int,
current_user: Dict[str, Any] = Depends(get_current_user),
db: Session = Depends(get_db)
) -> Dict[str, Any]:
"""Delete an enhanced strategy."""
try:
logger.info(f"Deleting enhanced strategy: {strategy_id}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID in authentication token"
)
# Check if strategy exists
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
logger.info(f"Deleting enhanced strategy: {strategy_id} for authenticated user: {authenticated_user_id}")
# Check if strategy exists and verify ownership
strategy = db.query(EnhancedContentStrategy).filter(
EnhancedContentStrategy.id == strategy_id
).first()
@@ -261,14 +285,21 @@ async def delete_enhanced_strategy(
detail=f"Enhanced strategy with ID {strategy_id} not found"
)
# Verify ownership
if strategy.user_id != authenticated_user_id:
raise HTTPException(
status_code=403,
detail="You don't have permission to delete this strategy"
)
# 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}
return ResponseBuilder.create_success_response(
data={"strategy_id": strategy_id},
message=SUCCESS_MESSAGES['strategy_deleted']
)
except HTTPException:

View File

@@ -6,6 +6,7 @@ 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 starlette.requests import Request
from sqlalchemy.orm import Session
from loguru import logger
import json
@@ -17,6 +18,9 @@ import time
# Import database
from services.database import get_db_session
# Import authentication middleware
from middleware.auth_middleware import get_current_user, get_current_user_with_query_token
# Import services
from ....services.enhanced_strategy_service import EnhancedStrategyService
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
@@ -66,15 +70,26 @@ async def stream_data(data_generator):
@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"),
current_user: Dict[str, Any] = Depends(get_current_user),
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}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
yield {"type": "error", "message": "Invalid user ID in authentication token", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
yield {"type": "error", "message": "Invalid user ID format", "timestamp": datetime.utcnow().isoformat()}
return
logger.info(f"🚀 Starting strategy stream for authenticated user: {authenticated_user_id}, strategy: {strategy_id}")
# Send initial status
yield {"type": "status", "message": "Starting strategy retrieval...", "timestamp": datetime.utcnow().isoformat()}
@@ -85,7 +100,8 @@ async def stream_enhanced_strategies(
# Send progress update
yield {"type": "progress", "message": "Querying database...", "progress": 25}
strategies_data = await enhanced_service.get_enhanced_strategies(user_id, strategy_id, db)
# Use authenticated user_id to ensure users can only see their own strategies
strategies_data = await enhanced_service.get_enhanced_strategies(authenticated_user_id, strategy_id, db)
# Send progress update
yield {"type": "progress", "message": "Processing strategies...", "progress": 50}
@@ -100,7 +116,7 @@ async def stream_enhanced_strategies(
# Send final result
yield {"type": "result", "status": "success", "data": strategies_data, "progress": 100}
logger.info(f"✅ Strategy stream completed for user: {user_id}")
logger.info(f"✅ Strategy stream completed for user: {authenticated_user_id}")
except Exception as e:
logger.error(f"❌ Error in strategy stream: {str(e)}")
@@ -121,20 +137,32 @@ async def stream_enhanced_strategies(
@router.get("/stream/strategic-intelligence")
async def stream_strategic_intelligence(
user_id: Optional[int] = Query(None, description="User ID"),
request: Request,
current_user: Dict[str, Any] = Depends(get_current_user_with_query_token),
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}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
yield {"type": "error", "message": "Invalid user ID in authentication token", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
yield {"type": "error", "message": "Invalid user ID format", "timestamp": datetime.utcnow().isoformat()}
return
logger.info(f"🚀 Starting strategic intelligence stream for authenticated user: {authenticated_user_id}")
# Check cache first
cache_key = f"strategic_intelligence_{user_id}"
cache_key = f"strategic_intelligence_{authenticated_user_id}"
cached_data = get_cached_data(cache_key)
if cached_data:
logger.info(f"✅ Returning cached strategic intelligence data for user: {user_id}")
logger.info(f"✅ Returning cached strategic intelligence data for user: {authenticated_user_id}")
yield {"type": "result", "status": "success", "data": cached_data, "progress": 100}
return
@@ -147,7 +175,8 @@ async def stream_strategic_intelligence(
# Send progress update
yield {"type": "progress", "message": "Retrieving strategies...", "progress": 20}
strategies_data = await enhanced_service.get_enhanced_strategies(user_id, None, db)
# Use authenticated user_id to ensure users can only see their own strategies
strategies_data = await enhanced_service.get_enhanced_strategies(authenticated_user_id, None, db)
# Send progress update
yield {"type": "progress", "message": "Analyzing market positioning...", "progress": 40}
@@ -228,7 +257,7 @@ async def stream_strategic_intelligence(
# Send final result
yield {"type": "result", "status": "success", "data": strategic_intelligence, "progress": 100}
logger.info(f"✅ Strategic intelligence stream completed for user: {user_id}")
logger.info(f"✅ Strategic intelligence stream completed for user: {authenticated_user_id}")
except Exception as e:
logger.error(f"❌ Error in strategic intelligence stream: {str(e)}")
@@ -249,20 +278,32 @@ async def stream_strategic_intelligence(
@router.get("/stream/keyword-research")
async def stream_keyword_research(
user_id: Optional[int] = Query(None, description="User ID"),
request: Request,
current_user: Dict[str, Any] = Depends(get_current_user_with_query_token),
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}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
yield {"type": "error", "message": "Invalid user ID in authentication token", "timestamp": datetime.utcnow().isoformat()}
return
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
yield {"type": "error", "message": "Invalid user ID format", "timestamp": datetime.utcnow().isoformat()}
return
logger.info(f"🚀 Starting keyword research stream for authenticated user: {authenticated_user_id}")
# Check cache first
cache_key = f"keyword_research_{user_id}"
cache_key = f"keyword_research_{authenticated_user_id}"
cached_data = get_cached_data(cache_key)
if cached_data:
logger.info(f"✅ Returning cached keyword research data for user: {user_id}")
logger.info(f"✅ Returning cached keyword research data for user: {authenticated_user_id}")
yield {"type": "result", "status": "success", "data": cached_data, "progress": 100}
return
@@ -276,7 +317,8 @@ async def stream_keyword_research(
yield {"type": "progress", "message": "Retrieving gap analyses...", "progress": 20}
gap_service = GapAnalysisService()
gap_analyses = await gap_service.get_gap_analyses(user_id)
# Use authenticated user_id to ensure users can only see their own data
gap_analyses = await gap_service.get_gap_analyses(authenticated_user_id)
# Send progress update
yield {"type": "progress", "message": "Analyzing keyword opportunities...", "progress": 40}
@@ -337,7 +379,7 @@ async def stream_keyword_research(
# Send final result
yield {"type": "result", "status": "success", "data": keyword_data, "progress": 100}
logger.info(f"✅ Keyword research stream completed for user: {user_id}")
logger.info(f"✅ Keyword research stream completed for user: {authenticated_user_id}")
except Exception as e:
logger.error(f"❌ Error in keyword research stream: {str(e)}")

View File

@@ -15,6 +15,9 @@ from services.database import get_db_session
from ....services.enhanced_strategy_service import EnhancedStrategyService
from ....services.enhanced_strategy_db_service import EnhancedStrategyDBService
# Import authentication
from middleware.auth_middleware import get_current_user
# Import utilities
from ....utils.error_handlers import ContentPlanningErrorHandler
from ....utils.response_builders import ResponseBuilder
@@ -32,36 +35,60 @@ def get_db():
@router.get("/onboarding-data")
async def get_onboarding_data(
user_id: Optional[int] = Query(None, description="User ID to get onboarding data for"),
current_user: Dict[str, Any] = Depends(get_current_user),
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}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID in authentication token"
)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID format in authentication token"
)
logger.info(f"🚀 Getting onboarding data for authenticated user: {authenticated_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)
onboarding_data = await enhanced_service._get_onboarding_data(authenticated_user_id)
logger.info(f"✅ Onboarding data retrieved successfully for user: {actual_user_id}")
logger.info(f"✅ Onboarding data retrieved successfully for user: {authenticated_user_id}")
return ResponseBuilder.create_success_response(
message="Onboarding data retrieved successfully",
data=onboarding_data
)
except HTTPException:
raise
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]:
async def get_enhanced_strategy_tooltips(
current_user: Dict[str, Any] = Depends(get_current_user)
) -> Dict[str, Any]:
"""Get tooltip data for enhanced strategy fields."""
try:
logger.info("🚀 Getting enhanced strategy tooltips")
# Verify authentication (user_id not needed for static data, but auth is required)
if not current_user or not current_user.get('id'):
raise HTTPException(
status_code=401,
detail="Authentication required"
)
logger.info(f"🚀 Getting enhanced strategy tooltips for authenticated user: {current_user.get('id')}")
# Mock tooltip data - in real implementation, this would come from a database
tooltip_data = {
@@ -122,15 +149,26 @@ async def get_enhanced_strategy_tooltips() -> Dict[str, Any]:
data=tooltip_data
)
except HTTPException:
raise
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]:
async def get_enhanced_strategy_disclosure_steps(
current_user: Dict[str, Any] = Depends(get_current_user)
) -> Dict[str, Any]:
"""Get progressive disclosure steps for enhanced strategy."""
try:
logger.info("🚀 Getting enhanced strategy disclosure steps")
# Verify authentication (user_id not needed for static data, but auth is required)
if not current_user or not current_user.get('id'):
raise HTTPException(
status_code=401,
detail="Authentication required"
)
logger.info(f"🚀 Getting enhanced strategy disclosure steps for authenticated user: {current_user.get('id')}")
# Progressive disclosure steps configuration
disclosure_steps = [
@@ -197,41 +235,55 @@ async def get_enhanced_strategy_disclosure_steps() -> Dict[str, Any]:
data=disclosure_steps
)
except HTTPException:
raise
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")
current_user: Dict[str, Any] = Depends(get_current_user)
):
"""Clear streaming cache for a specific user or all users."""
"""Clear streaming cache for the authenticated user."""
try:
logger.info(f"🚀 Clearing streaming cache for user: {user_id}")
# Extract authenticated user_id from Clerk
clerk_user_id = str(current_user.get('id', ''))
if not clerk_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID in authentication token"
)
authenticated_user_id = int(clerk_user_id) if clerk_user_id.isdigit() else None
if not authenticated_user_id:
raise HTTPException(
status_code=401,
detail="Invalid user ID format in authentication token"
)
logger.info(f"🚀 Clearing streaming cache for authenticated user: {authenticated_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")
# Clear cache for authenticated user only (security: users can only clear their own cache)
cache_keys_to_remove = [
f"strategic_intelligence_{authenticated_user_id}",
f"keyword_research_{authenticated_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}")
return ResponseBuilder.create_success_response(
message="Streaming cache cleared successfully",
data={"cleared_for_user": user_id}
data={"cleared_for_user": authenticated_user_id}
)
except HTTPException:
raise
except Exception as e:
logger.error(f"❌ Error clearing streaming cache: {str(e)}")
raise ContentPlanningErrorHandler.handle_general_error(e, "clear_streaming_cache")

View File

@@ -14,12 +14,19 @@ 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"])
# Using /enhanced-strategies prefix for backward compatibility with frontend
router = APIRouter(prefix="/enhanced-strategies", tags=["Content Strategy"])
# Include all endpoint routers
router.include_router(crud_router, prefix="/strategies")
# CRUD endpoints directly under /enhanced-strategies (backward compatibility)
router.include_router(crud_router, prefix="")
# Analytics endpoints under /enhanced-strategies/strategies/{id}/...
router.include_router(analytics_router, prefix="/strategies")
# Utility endpoints directly under /enhanced-strategies
router.include_router(utility_router, prefix="")
# Streaming endpoints directly under /enhanced-strategies
router.include_router(streaming_router, prefix="")
# Autofill endpoints under /enhanced-strategies/strategies/{id}/...
router.include_router(autofill_router, prefix="/strategies")
# AI generation endpoints under /enhanced-strategies/ai-generation
router.include_router(ai_generation_router, prefix="/ai-generation")

File diff suppressed because it is too large Load Diff

View File

@@ -11,10 +11,7 @@ from loguru import logger
# Import route modules
from .routes import strategies, calendar_events, gap_analysis, ai_analytics, calendar_generation, health_monitoring, monitoring
# Import enhanced strategy routes
from .enhanced_strategy_routes import router as enhanced_strategy_router
# Import content strategy routes
# Import content strategy routes (modular endpoints)
from .content_strategy.routes import router as content_strategy_router
# Import quality analysis routes
@@ -35,10 +32,7 @@ router.include_router(calendar_generation.router)
router.include_router(health_monitoring.router)
router.include_router(monitoring.router)
# Include enhanced strategy routes with correct prefix
router.include_router(enhanced_strategy_router, prefix="/enhanced-strategies")
# Include content strategy routes
# Include content strategy routes (modular endpoints)
router.include_router(content_strategy_router)
# Include quality analysis routes

View File

@@ -62,18 +62,24 @@ async def get_cache_statistics(db = None) -> Dict[str, Any]:
@router.get("/health")
async def get_system_health() -> Dict[str, Any]:
"""Get overall system health status."""
"""Get overall system health status.
Optimized to fail fast - cache stats are optional and won't block the response.
"""
try:
# Get lightweight API stats
# Get lightweight API stats (this is the critical path)
api_stats = await get_lightweight_stats()
# Get cache stats if available
# Get cache stats if available (non-blocking - don't fail if unavailable)
cache_stats = {}
try:
db = next(get_db())
cache_service = ComprehensiveUserDataCacheService(db)
cache_stats = cache_service.get_cache_stats()
except:
db.close()
except Exception as cache_err:
# Cache stats are optional - log at debug level, don't fail
logger.debug(f"Cache stats unavailable: {cache_err}")
cache_stats = {"error": "Cache service unavailable"}
# Determine overall health
@@ -97,7 +103,7 @@ async def get_system_health() -> Dict[str, Any]:
"message": f"System health: {system_health}"
}
except Exception as e:
logger.error(f"Error getting system health: {str(e)}")
logger.error(f"Error getting system health: {str(e)}", exc_info=True)
return {
"status": "error",
"data": {