Files
ALwrity/docs/content_planning_monolithic_backup.py
2025-08-09 23:14:16 +05:30

2201 lines
90 KiB
Python

"""Content Planning API endpoints for ALwrity."""
from fastapi import APIRouter, HTTPException, Depends, status, Query
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from typing import Dict, Any, List, Optional
from datetime import datetime
import json
import asyncio
from loguru import logger
from sqlalchemy.orm import Session
import time
# Import existing services
from services.api_key_manager import APIKeyManager
from services.validation import check_all_api_keys
from services.content_planning_service import ContentPlanningService
from services.user_data_service import UserDataService
from services.database import get_db_session, get_db
# Import database service
from services.content_planning_db import ContentPlanningDBService
# Import migrated content gap analysis services
from services.content_gap_analyzer.content_gap_analyzer import ContentGapAnalyzer
from services.content_gap_analyzer.competitor_analyzer import CompetitorAnalyzer
from services.content_gap_analyzer.keyword_researcher import KeywordResearcher
from services.content_gap_analyzer.ai_engine_service import AIEngineService
from services.content_gap_analyzer.website_analyzer import WebsiteAnalyzer
# Import new AI analytics service
from services.ai_analytics_service import AIAnalyticsService
from services.onboarding_data_service import OnboardingDataService
# Import new AI analysis database service
from services.ai_analysis_db_service import AIAnalysisDBService
# Import new calendar generator service
from services.calendar_generator_service import CalendarGeneratorService
# Initialize AI analysis database service
ai_analysis_db_service = AIAnalysisDBService()
# Initialize the content planning service
content_planning_service = ContentPlanningService()
# Initialize new calendar generator service
calendar_generator_service = CalendarGeneratorService()
# Initialize migrated services
content_gap_analyzer = ContentGapAnalyzer()
competitor_analyzer = CompetitorAnalyzer()
keyword_researcher = KeywordResearcher()
ai_engine_service = AIEngineService()
website_analyzer = WebsiteAnalyzer()
# Initialize new AI analytics service
ai_analytics_service = AIAnalyticsService()
# Pydantic models for Content Planning
class ContentStrategyRequest(BaseModel):
industry: str
target_audience: Dict[str, Any]
business_goals: List[str]
content_preferences: Dict[str, Any]
competitor_urls: Optional[List[str]] = None
# New Pydantic models for database operations
class ContentStrategyCreate(BaseModel):
user_id: int
name: str
industry: str
target_audience: Dict[str, Any]
content_pillars: Optional[List[Dict[str, Any]]] = None
ai_recommendations: Optional[Dict[str, Any]] = None
class ContentStrategyResponse(BaseModel):
id: int
name: str
industry: str
target_audience: Dict[str, Any]
content_pillars: List[Dict[str, Any]]
ai_recommendations: Dict[str, Any]
created_at: datetime
updated_at: datetime
class CalendarEventCreate(BaseModel):
strategy_id: int
title: str
description: str
content_type: str
platform: str
scheduled_date: datetime
ai_recommendations: Optional[Dict[str, Any]] = None
class CalendarEventResponse(BaseModel):
id: int
strategy_id: int
title: str
description: str
content_type: str
platform: str
scheduled_date: datetime
status: str
ai_recommendations: Optional[Dict[str, Any]] = None
created_at: datetime
updated_at: datetime
class ContentGapAnalysisCreate(BaseModel):
user_id: int
website_url: str
competitor_urls: List[str]
target_keywords: Optional[List[str]] = None
industry: Optional[str] = None
analysis_results: Optional[Dict[str, Any]] = None
recommendations: Optional[Dict[str, Any]] = None
opportunities: Optional[Dict[str, Any]] = None
class ContentGapAnalysisResponse(BaseModel):
id: int
user_id: int
website_url: str
competitor_urls: List[str]
target_keywords: Optional[List[str]] = None
industry: Optional[str] = None
analysis_results: Optional[Dict[str, Any]] = None
recommendations: Optional[Dict[str, Any]] = None
opportunities: Optional[Dict[str, Any]] = None
created_at: datetime
updated_at: datetime
class ContentGapAnalysisRequest(BaseModel):
website_url: str
competitor_urls: List[str]
target_keywords: Optional[List[str]] = None
industry: Optional[str] = None
class ContentGapAnalysisFullResponse(BaseModel):
website_analysis: Dict[str, Any]
competitor_analysis: Dict[str, Any]
gap_analysis: Dict[str, Any]
recommendations: List[Dict[str, Any]]
opportunities: List[Dict[str, Any]]
created_at: datetime
# New Pydantic models for AI Analytics
class ContentEvolutionRequest(BaseModel):
strategy_id: int
time_period: str = "30d" # 7d, 30d, 90d, 1y
class PerformanceTrendsRequest(BaseModel):
strategy_id: int
metrics: Optional[List[str]] = None
class ContentPerformancePredictionRequest(BaseModel):
strategy_id: int
content_data: Dict[str, Any]
class StrategicIntelligenceRequest(BaseModel):
strategy_id: int
market_data: Optional[Dict[str, Any]] = None
class AIAnalyticsResponse(BaseModel):
analysis_type: str
strategy_id: int
results: Dict[str, Any]
recommendations: List[Dict[str, Any]]
analysis_date: datetime
# New Pydantic models for Calendar Generation
class CalendarGenerationRequest(BaseModel):
user_id: int
strategy_id: Optional[int] = None
calendar_type: str = Field("monthly", description="Type of calendar: monthly, weekly, custom")
industry: Optional[str] = None
business_size: str = Field("sme", description="Business size: startup, sme, enterprise")
force_refresh: bool = Field(False, description="Force refresh calendar generation")
class CalendarGenerationResponse(BaseModel):
user_id: int
strategy_id: Optional[int]
calendar_type: str
industry: str
business_size: str
generated_at: datetime
content_pillars: List[str]
platform_strategies: Dict[str, Any]
content_mix: Dict[str, float]
daily_schedule: List[Dict[str, Any]]
weekly_themes: List[Dict[str, Any]]
content_recommendations: List[Dict[str, Any]]
optimal_timing: Dict[str, Any]
performance_predictions: Dict[str, Any]
trending_topics: List[Dict[str, Any]]
repurposing_opportunities: List[Dict[str, Any]]
ai_insights: List[Dict[str, Any]]
competitor_analysis: Dict[str, Any]
gap_analysis_insights: Dict[str, Any]
strategy_insights: Dict[str, Any]
onboarding_insights: Dict[str, Any]
processing_time: float
ai_confidence: float
class ContentOptimizationRequest(BaseModel):
user_id: int
event_id: Optional[int] = None
title: str
description: str
content_type: str
target_platform: str
original_content: Optional[Dict[str, Any]] = None
class ContentOptimizationResponse(BaseModel):
user_id: int
event_id: Optional[int]
original_content: Dict[str, Any]
optimized_content: Dict[str, Any]
platform_adaptations: List[str]
visual_recommendations: List[str]
hashtag_suggestions: List[str]
keyword_optimization: Dict[str, Any]
tone_adjustments: Dict[str, Any]
length_optimization: Dict[str, Any]
performance_prediction: Dict[str, Any]
optimization_score: float
created_at: datetime
class PerformancePredictionRequest(BaseModel):
user_id: int
strategy_id: Optional[int] = None
content_type: str
platform: str
content_data: Dict[str, Any]
class PerformancePredictionResponse(BaseModel):
user_id: int
strategy_id: Optional[int]
content_type: str
platform: str
predicted_engagement_rate: float
predicted_reach: int
predicted_conversions: int
predicted_roi: float
confidence_score: float
recommendations: List[str]
created_at: datetime
class ContentRepurposingRequest(BaseModel):
user_id: int
strategy_id: Optional[int] = None
original_content: Dict[str, Any]
target_platforms: List[str]
class ContentRepurposingResponse(BaseModel):
user_id: int
strategy_id: Optional[int]
original_content: Dict[str, Any]
platform_adaptations: List[Dict[str, Any]]
transformations: List[Dict[str, Any]]
implementation_tips: List[str]
gap_addresses: List[str]
created_at: datetime
class TrendingTopicsRequest(BaseModel):
user_id: int
industry: str
limit: int = Field(10, description="Number of trending topics to return")
class TrendingTopicsResponse(BaseModel):
user_id: int
industry: str
trending_topics: List[Dict[str, Any]]
gap_relevance_scores: Dict[str, float]
audience_alignment_scores: Dict[str, float]
created_at: datetime
# Create router
router = APIRouter(prefix="/api/content-planning", tags=["content-planning"])
# Database-integrated endpoints
@router.post("/strategies/", response_model=ContentStrategyResponse)
async def create_content_strategy(
strategy: ContentStrategyCreate,
db: Session = Depends(get_db)
):
"""Create a new content strategy."""
try:
logger.info(f"Creating content strategy: {strategy.name}")
db_service = ContentPlanningDBService(db)
strategy_data = strategy.dict()
created_strategy = await db_service.create_content_strategy(strategy_data)
if created_strategy:
logger.info(f"Content strategy created successfully: {created_strategy.id}")
return ContentStrategyResponse(**created_strategy.to_dict())
else:
raise HTTPException(status_code=500, detail="Failed to create content strategy")
except Exception as e:
logger.error(f"Error creating content strategy: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/strategies/", response_model=Dict[str, Any])
async def get_content_strategies(
user_id: Optional[int] = Query(None, description="User ID"),
strategy_id: Optional[int] = Query(None, description="Strategy ID")
):
"""
Get content strategies with comprehensive logging for debugging.
"""
try:
logger.info(f"🚀 Starting content strategy analysis for user: {user_id}, strategy: {strategy_id}")
# Check for existing strategy analysis in database
logger.info(f"🔍 Checking database for existing strategy analysis for user {user_id or 'None'}")
# Get latest AI analysis for strategic intelligence
latest_analysis = await ai_analysis_db_service.get_latest_ai_analysis(
user_id=user_id or 1,
analysis_type="strategic_intelligence"
)
if latest_analysis:
logger.info(f"✅ Found existing strategy analysis in database: {latest_analysis.get('id', 'unknown')}")
# Extract the actual strategic intelligence data from personalized_data_used
# The strategic intelligence data is stored in personalized_data_used, not in results
personalized_data = latest_analysis.get("personalized_data_used", {})
# Generate strategic intelligence from the personalized data
ai_analytics = AIAnalyticsService()
strategic_intelligence = await ai_analytics.generate_strategic_intelligence(
strategy_id=strategy_id or 1
)
logger.info("📊 CONTENT STRATEGY DATA BREAKDOWN:")
logger.info(f" - Strategy ID: {latest_analysis.get('id', 'N/A')}")
logger.info(f" - Analysis Date: {latest_analysis.get('analysis_date', 'N/A')}")
logger.info(f" - User ID: {latest_analysis.get('user_id', 'N/A')}")
# Log strategic insights from the generated intelligence
strategic_insights = strategic_intelligence.get("strategic_insights", [])
logger.info(f" - Strategic Insights Count: {len(strategic_insights)}")
for i, insight in enumerate(strategic_insights[:3]): # Log first 3 insights
logger.info(f" Insight {i+1}: {insight.get('insight', 'N/A')}")
logger.info(f" Type: {insight.get('type', 'N/A')}")
logger.info(f" Priority: {insight.get('priority', 'N/A')}")
logger.info(f" Impact: {insight.get('estimated_impact', 'N/A')}")
# Log market positioning data
market_positioning = strategic_intelligence.get("market_positioning", {})
logger.info(f" - Market Positioning: {market_positioning.get('industry_position', 'N/A')}")
logger.info(f" - Competitive Advantage: {market_positioning.get('competitive_advantage', 'N/A')}")
logger.info(f" - Market Share: {market_positioning.get('market_share', 'N/A')}")
# Log strategic scores
strategic_scores = strategic_intelligence.get("strategic_scores", {})
logger.info(f" - Strategic Scores:")
for score_name, score_value in strategic_scores.items():
logger.info(f" {score_name}: {score_value}")
# Log risk assessment
risk_assessment = strategic_intelligence.get("risk_assessment", [])
logger.info(f" - Risk Assessment Count: {len(risk_assessment)}")
for i, risk in enumerate(risk_assessment[:2]): # Log first 2 risks
logger.info(f" Risk {i+1}: {risk.get('type', 'N/A')} - {risk.get('severity', 'N/A')}")
logger.info(f" Description: {risk.get('description', 'N/A')}")
# Log opportunity analysis
opportunity_analysis = strategic_intelligence.get("opportunity_analysis", [])
logger.info(f" - Opportunity Analysis Count: {len(opportunity_analysis)}")
for i, opportunity in enumerate(opportunity_analysis[:2]): # Log first 2 opportunities
logger.info(f" Opportunity {i+1}: {opportunity.get('title', 'N/A')}")
logger.info(f" Impact: {opportunity.get('estimated_impact', 'N/A')}")
# Log recommendations
recommendations = strategic_intelligence.get("recommendations", [])
logger.info(f" - Recommendations Count: {len(recommendations)}")
for i, rec in enumerate(recommendations[:3]): # Log first 3 recommendations
logger.info(f" Recommendation {i+1}: {rec.get('title', 'N/A')}")
logger.info(f" Priority: {rec.get('priority', 'N/A')}")
logger.info(f" Impact: {rec.get('estimated_impact', 'N/A')}")
# Log the full strategy data structure
logger.info("🔍 FULL STRATEGY DATA STRUCTURE:")
logger.info(f" - Strategic Intelligence Keys: {list(strategic_intelligence.keys())}")
logger.info(f" - Personalized Data Keys: {list(personalized_data.keys())}")
return {
"status": "success",
"message": "Content strategy retrieved successfully",
"data": {
"strategies": [strategic_intelligence],
"total_count": 1,
"user_id": user_id,
"analysis_date": latest_analysis.get("analysis_date"),
"strategic_insights": strategic_insights,
"market_positioning": market_positioning,
"strategic_scores": strategic_scores,
"risk_assessment": risk_assessment,
"opportunity_analysis": opportunity_analysis,
"recommendations": recommendations,
"personalized_data": personalized_data
}
}
else:
logger.warning("⚠️ No existing strategy analysis found in database")
return {
"status": "not_found",
"message": "No content strategy found",
"data": {
"strategies": [],
"total_count": 0,
"user_id": user_id
}
}
except Exception as e:
logger.error(f"❌ Error retrieving content strategies: {str(e)}")
logger.error(f"Exception type: {type(e)}")
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
raise HTTPException(
status_code=500,
detail=f"Error retrieving content strategies: {str(e)}"
)
@router.get("/strategies/{strategy_id}", response_model=ContentStrategyResponse)
async def get_content_strategy(
strategy_id: int,
db: Session = Depends(get_db)
):
"""Get a specific content strategy by ID."""
try:
logger.info(f"Fetching content strategy: {strategy_id}")
db_service = ContentPlanningDBService(db)
strategy = await db_service.get_content_strategy(strategy_id)
if strategy:
return ContentStrategyResponse(**strategy.to_dict())
else:
raise HTTPException(status_code=404, detail="Content strategy not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting content strategy: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.put("/strategies/{strategy_id}", response_model=ContentStrategyResponse)
async def update_content_strategy(
strategy_id: int,
update_data: Dict[str, Any],
db: Session = Depends(get_db)
):
"""Update a content strategy."""
try:
logger.info(f"Updating content strategy: {strategy_id}")
db_service = ContentPlanningDBService(db)
updated_strategy = await db_service.update_content_strategy(strategy_id, update_data)
if updated_strategy:
return ContentStrategyResponse(**updated_strategy.to_dict())
else:
raise HTTPException(status_code=404, detail="Content strategy not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error updating content strategy: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.delete("/strategies/{strategy_id}")
async def delete_content_strategy(
strategy_id: int,
db: Session = Depends(get_db)
):
"""Delete a content strategy."""
try:
logger.info(f"Deleting content strategy: {strategy_id}")
db_service = ContentPlanningDBService(db)
deleted = await db_service.delete_content_strategy(strategy_id)
if deleted:
return {"message": f"Content strategy {strategy_id} deleted successfully"}
else:
raise HTTPException(status_code=404, detail="Content strategy not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error deleting content strategy: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/calendar-events/", response_model=CalendarEventResponse)
async def create_calendar_event(
event: CalendarEventCreate,
db: Session = Depends(get_db)
):
"""Create a new calendar event."""
try:
logger.info(f"Creating calendar event: {event.title}")
db_service = ContentPlanningDBService(db)
event_data = event.dict()
created_event = await db_service.create_calendar_event(event_data)
if created_event:
logger.info(f"Calendar event created successfully: {created_event.id}")
return CalendarEventResponse(**created_event.to_dict())
else:
raise HTTPException(status_code=500, detail="Failed to create calendar event")
except Exception as e:
logger.error(f"Error creating calendar event: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/calendar-events/", response_model=List[CalendarEventResponse])
async def get_calendar_events(
strategy_id: Optional[int] = Query(None, description="Filter by strategy ID"),
db: Session = Depends(get_db)
):
"""Get calendar events, optionally filtered by strategy."""
try:
logger.info("Fetching calendar events")
db_service = ContentPlanningDBService(db)
if strategy_id:
events = await db_service.get_strategy_calendar_events(strategy_id)
else:
# TODO: Implement get_all_calendar_events method
events = []
return [CalendarEventResponse(**event.to_dict()) for event in events]
except Exception as e:
logger.error(f"Error getting calendar events: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/calendar-events/{event_id}", response_model=CalendarEventResponse)
async def get_calendar_event(
event_id: int,
db: Session = Depends(get_db)
):
"""Get a specific calendar event by ID."""
try:
logger.info(f"Fetching calendar event: {event_id}")
db_service = ContentPlanningDBService(db)
event = await db_service.get_calendar_event(event_id)
if event:
return CalendarEventResponse(**event.to_dict())
else:
raise HTTPException(status_code=404, detail="Calendar event not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting calendar event: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.put("/calendar-events/{event_id}", response_model=CalendarEventResponse)
async def update_calendar_event(
event_id: int,
update_data: Dict[str, Any],
db: Session = Depends(get_db)
):
"""Update a calendar event."""
try:
logger.info(f"Updating calendar event: {event_id}")
db_service = ContentPlanningDBService(db)
updated_event = await db_service.update_calendar_event(event_id, update_data)
if updated_event:
return CalendarEventResponse(**updated_event.to_dict())
else:
raise HTTPException(status_code=404, detail="Calendar event not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error updating calendar event: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.delete("/calendar-events/{event_id}")
async def delete_calendar_event(
event_id: int,
db: Session = Depends(get_db)
):
"""Delete a calendar event."""
try:
logger.info(f"Deleting calendar event: {event_id}")
db_service = ContentPlanningDBService(db)
deleted = await db_service.delete_calendar_event(event_id)
if deleted:
return {"message": f"Calendar event {event_id} deleted successfully"}
else:
raise HTTPException(status_code=404, detail="Calendar event not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error deleting calendar event: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/gap-analysis/", response_model=ContentGapAnalysisResponse)
async def create_content_gap_analysis(
analysis: ContentGapAnalysisCreate,
db: Session = Depends(get_db)
):
"""Create a new content gap analysis."""
try:
logger.info(f"Creating content gap analysis for: {analysis.website_url}")
db_service = ContentPlanningDBService(db)
analysis_data = analysis.dict()
created_analysis = await db_service.create_content_gap_analysis(analysis_data)
if created_analysis:
logger.info(f"Content gap analysis created successfully: {created_analysis.id}")
return ContentGapAnalysisResponse(**created_analysis.to_dict())
else:
raise HTTPException(status_code=500, detail="Failed to create content gap analysis")
except Exception as e:
logger.error(f"Error creating content gap analysis: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/gap-analysis/", response_model=Dict[str, Any])
async def get_content_gap_analyses(
user_id: Optional[int] = Query(None, description="User ID"),
strategy_id: Optional[int] = Query(None, description="Strategy ID"),
force_refresh: bool = Query(False, description="Force refresh gap analysis")
):
"""Get content gap analysis with real AI insights - Database first approach."""
try:
logger.info(f"🚀 Starting content gap analysis for user: {user_id}, strategy: {strategy_id}, force_refresh: {force_refresh}")
# Use user_id or default to 1
current_user_id = user_id or 1
# Skip database check if force_refresh is True
if not force_refresh:
# First, try to get existing gap analysis from database
logger.info(f"🔍 Checking database for existing gap analysis for user {current_user_id}")
existing_analysis = await ai_analysis_db_service.get_latest_ai_analysis(
user_id=current_user_id,
analysis_type="gap_analysis",
strategy_id=strategy_id,
max_age_hours=24 # Use cached results up to 24 hours old
)
if existing_analysis:
logger.info(f"✅ Found existing gap analysis in database: {existing_analysis.id}")
# Return cached results
return {
"gap_analyses": [{"recommendations": existing_analysis.recommendations or []}],
"total_gaps": len(existing_analysis.recommendations or []),
"generated_at": existing_analysis.created_at.isoformat(),
"ai_service_status": existing_analysis.ai_service_status,
"personalized_data_used": True if existing_analysis.personalized_data_used else False,
"data_source": "database_cache",
"cache_age_hours": (datetime.utcnow() - existing_analysis.created_at).total_seconds() / 3600
}
# No recent analysis found or force refresh requested, run new AI analysis
logger.info(f"🔄 Running new gap analysis for user {current_user_id} (force_refresh: {force_refresh})")
# Get personalized inputs from onboarding data
onboarding_service = OnboardingDataService()
personalized_inputs = onboarding_service.get_personalized_ai_inputs(current_user_id)
logger.info(f"📊 Using personalized inputs: {json.dumps(personalized_inputs, indent=2)}")
# Generate real AI-powered gap analysis
ai_engine = AIEngineService()
gap_analysis = await ai_engine.generate_content_recommendations(personalized_inputs)
logger.info(f"✅ AI gap analysis completed: {len(gap_analysis)} recommendations")
# Store results in database
try:
await ai_analysis_db_service.store_ai_analysis_result(
user_id=current_user_id,
analysis_type="gap_analysis",
insights=[],
recommendations=gap_analysis,
personalized_data=personalized_inputs,
strategy_id=strategy_id,
ai_service_status="operational"
)
logger.info(f"💾 Gap analysis results stored in database for user {current_user_id}")
except Exception as e:
logger.error(f"❌ Failed to store gap analysis in database: {str(e)}")
return {
"gap_analyses": [{"recommendations": gap_analysis}],
"total_gaps": len(gap_analysis),
"generated_at": datetime.utcnow().isoformat(),
"ai_service_status": "operational",
"personalized_data_used": True,
"data_source": "ai_analysis"
}
except Exception as e:
logger.error(f"❌ Error generating content gap analysis: {str(e)}")
raise HTTPException(status_code=500, detail=f"Error generating content gap analysis: {str(e)}")
@router.get("/gap-analysis/{analysis_id}", response_model=ContentGapAnalysisResponse)
async def get_content_gap_analysis(
analysis_id: int,
db: Session = Depends(get_db)
):
"""Get a specific content gap analysis by ID."""
try:
logger.info(f"Fetching content gap analysis: {analysis_id}")
db_service = ContentPlanningDBService(db)
analysis = await db_service.get_content_gap_analysis(analysis_id)
if analysis:
return ContentGapAnalysisResponse(**analysis.to_dict())
else:
raise HTTPException(status_code=404, detail="Content gap analysis not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting content gap analysis: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/gap-analysis/analyze", response_model=ContentGapAnalysisFullResponse)
async def analyze_content_gaps(request: ContentGapAnalysisRequest):
"""
Analyze content gaps between your website and competitors.
"""
try:
logger.info(f"Starting content gap analysis for: {request.website_url}")
# Use migrated services for actual analysis
analysis_results = {}
# 1. Website Analysis
logger.info("Performing website analysis...")
website_analysis = await website_analyzer.analyze_website_content(request.website_url)
analysis_results['website_analysis'] = website_analysis
# 2. Competitor Analysis
logger.info("Performing competitor analysis...")
competitor_analysis = await competitor_analyzer.analyze_competitors(request.competitor_urls)
analysis_results['competitor_analysis'] = competitor_analysis
# 3. Keyword Research
logger.info("Performing keyword research...")
keyword_analysis = await keyword_researcher.research_keywords(
industry=request.industry,
target_keywords=request.target_keywords
)
analysis_results['keyword_analysis'] = keyword_analysis
# 4. Content Gap Analysis
logger.info("Performing content gap analysis...")
gap_analysis = await content_gap_analyzer.identify_content_gaps(
website_url=request.website_url,
competitor_urls=request.competitor_urls,
keyword_data=keyword_analysis
)
analysis_results['gap_analysis'] = gap_analysis
# 5. AI-Powered Recommendations
logger.info("Generating AI recommendations...")
recommendations = await ai_engine_service.generate_recommendations(
website_analysis=website_analysis,
competitor_analysis=competitor_analysis,
gap_analysis=gap_analysis,
keyword_analysis=keyword_analysis
)
analysis_results['recommendations'] = recommendations
# 6. Strategic Opportunities
logger.info("Identifying strategic opportunities...")
opportunities = await ai_engine_service.identify_strategic_opportunities(
gap_analysis=gap_analysis,
competitor_analysis=competitor_analysis,
keyword_analysis=keyword_analysis
)
analysis_results['opportunities'] = opportunities
# Prepare response
response_data = {
'website_analysis': analysis_results['website_analysis'],
'competitor_analysis': analysis_results['competitor_analysis'],
'gap_analysis': analysis_results['gap_analysis'],
'recommendations': analysis_results['recommendations'],
'opportunities': analysis_results['opportunities'],
'created_at': datetime.utcnow()
}
logger.info(f"Content gap analysis completed successfully")
return ContentGapAnalysisFullResponse(**response_data)
except Exception as e:
logger.error(f"Error analyzing content gaps: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Error analyzing content gaps: {str(e)}"
)
@router.get("/health")
async def content_planning_health_check():
"""
Health check for content planning services.
"""
try:
# Check all migrated services
service_status = {}
# Test content gap analyzer
try:
await content_gap_analyzer.health_check()
service_status['content_gap_analyzer'] = 'operational'
except Exception as e:
service_status['content_gap_analyzer'] = f'error: {str(e)}'
# Test competitor analyzer
try:
await competitor_analyzer.health_check()
service_status['competitor_analyzer'] = 'operational'
except Exception as e:
service_status['competitor_analyzer'] = f'error: {str(e)}'
# Test keyword researcher
try:
await keyword_researcher.health_check()
service_status['keyword_researcher'] = 'operational'
except Exception as e:
service_status['keyword_researcher'] = f'error: {str(e)}'
# Test AI engine service
try:
await ai_engine_service.health_check()
service_status['ai_engine_service'] = 'operational'
except Exception as e:
service_status['ai_engine_service'] = f'error: {str(e)}'
# Test website analyzer
try:
await website_analyzer.health_check()
service_status['website_analyzer'] = 'operational'
except Exception as e:
service_status['website_analyzer'] = f'error: {str(e)}'
# Determine overall status
operational_services = sum(1 for status in service_status.values() if status == 'operational')
total_services = len(service_status)
overall_status = 'healthy' if operational_services == total_services else 'degraded'
health_status = {
'status': overall_status,
'services': service_status,
'operational_services': operational_services,
'total_services': total_services,
'timestamp': datetime.utcnow().isoformat()
}
return health_status
except Exception as e:
logger.error(f"Health check failed: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Health check failed: {str(e)}"
)
@router.get("/database/health")
async def database_health_check(db: Session = Depends(get_db)):
"""
Health check for database operations.
"""
try:
logger.info("Performing database health check")
db_service = ContentPlanningDBService(db)
health_status = await db_service.health_check()
logger.info(f"Database health check completed: {health_status['status']}")
return health_status
except Exception as e:
logger.error(f"Database health check failed: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Database health check failed: {str(e)}"
)
# New AI Analytics Endpoints
@router.post("/ai-analytics/content-evolution", response_model=AIAnalyticsResponse)
async def analyze_content_evolution(request: ContentEvolutionRequest):
"""
Analyze content evolution over time for a specific strategy.
"""
try:
logger.info(f"Starting content evolution analysis for strategy {request.strategy_id}")
# Perform content evolution analysis
evolution_analysis = await ai_analytics_service.analyze_content_evolution(
strategy_id=request.strategy_id,
time_period=request.time_period
)
# Prepare response
response_data = {
'analysis_type': 'content_evolution',
'strategy_id': request.strategy_id,
'results': evolution_analysis,
'recommendations': evolution_analysis.get('recommendations', []),
'analysis_date': datetime.utcnow()
}
logger.info(f"Content evolution analysis completed for strategy {request.strategy_id}")
return AIAnalyticsResponse(**response_data)
except Exception as e:
logger.error(f"Error analyzing content evolution: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Error analyzing content evolution: {str(e)}"
)
@router.post("/ai-analytics/performance-trends", response_model=AIAnalyticsResponse)
async def analyze_performance_trends(request: PerformanceTrendsRequest):
"""
Analyze performance trends for content strategy.
"""
try:
logger.info(f"Starting performance trends analysis for strategy {request.strategy_id}")
# Perform performance trends analysis
trends_analysis = await ai_analytics_service.analyze_performance_trends(
strategy_id=request.strategy_id,
metrics=request.metrics
)
# Prepare response
response_data = {
'analysis_type': 'performance_trends',
'strategy_id': request.strategy_id,
'results': trends_analysis,
'recommendations': trends_analysis.get('recommendations', []),
'analysis_date': datetime.utcnow()
}
logger.info(f"Performance trends analysis completed for strategy {request.strategy_id}")
return AIAnalyticsResponse(**response_data)
except Exception as e:
logger.error(f"Error analyzing performance trends: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Error analyzing performance trends: {str(e)}"
)
@router.post("/ai-analytics/predict-performance", response_model=AIAnalyticsResponse)
async def predict_content_performance(request: ContentPerformancePredictionRequest):
"""
Predict content performance using AI models.
"""
try:
logger.info(f"Starting content performance prediction for strategy {request.strategy_id}")
# Perform content performance prediction
prediction_results = await ai_analytics_service.predict_content_performance(
content_data=request.content_data,
strategy_id=request.strategy_id
)
# Prepare response
response_data = {
'analysis_type': 'content_performance_prediction',
'strategy_id': request.strategy_id,
'results': prediction_results,
'recommendations': prediction_results.get('optimization_recommendations', []),
'analysis_date': datetime.utcnow()
}
logger.info(f"Content performance prediction completed for strategy {request.strategy_id}")
return AIAnalyticsResponse(**response_data)
except Exception as e:
logger.error(f"Error predicting content performance: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Error predicting content performance: {str(e)}"
)
@router.post("/ai-analytics/strategic-intelligence", response_model=AIAnalyticsResponse)
async def generate_strategic_intelligence(request: StrategicIntelligenceRequest):
"""
Generate strategic intelligence for content planning.
"""
try:
logger.info(f"Starting strategic intelligence generation for strategy {request.strategy_id}")
# Generate strategic intelligence
intelligence_results = await ai_analytics_service.generate_strategic_intelligence(
strategy_id=request.strategy_id,
market_data=request.market_data
)
# Prepare response
response_data = {
'analysis_type': 'strategic_intelligence',
'strategy_id': request.strategy_id,
'results': intelligence_results,
'recommendations': [], # Strategic intelligence includes its own recommendations
'analysis_date': datetime.utcnow()
}
logger.info(f"Strategic intelligence generation completed for strategy {request.strategy_id}")
return AIAnalyticsResponse(**response_data)
except Exception as e:
logger.error(f"Error generating strategic intelligence: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Error generating strategic intelligence: {str(e)}"
)
@router.get("/ai-analytics/health")
async def ai_analytics_health_check():
"""
Health check for AI analytics services.
"""
try:
# Check AI analytics service
service_status = {}
# Test AI analytics service
try:
# Test with a simple operation that doesn't require data
# Just check if the service can be instantiated
test_service = AIAnalyticsService()
service_status['ai_analytics_service'] = 'operational'
except Exception as e:
service_status['ai_analytics_service'] = f'error: {str(e)}'
# Determine overall status
operational_services = sum(1 for status in service_status.values() if status == 'operational')
total_services = len(service_status)
overall_status = 'healthy' if operational_services == total_services else 'degraded'
health_status = {
'status': overall_status,
'services': service_status,
'operational_services': operational_services,
'total_services': total_services,
'timestamp': datetime.utcnow().isoformat()
}
return health_status
except Exception as e:
logger.error(f"AI analytics health check failed: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"AI analytics health check failed: {str(e)}"
)
# Additional database-integrated endpoints
@router.get("/ai-analytics/", response_model=Dict[str, Any])
async def get_ai_analytics(
user_id: Optional[int] = Query(None, description="User ID"),
strategy_id: Optional[int] = Query(None, description="Strategy ID"),
force_refresh: bool = Query(False, description="Force refresh AI analysis")
):
"""Get AI analytics with real personalized insights - Database first approach."""
try:
logger.info(f"🚀 Starting AI analytics for user: {user_id}, strategy: {strategy_id}, force_refresh: {force_refresh}")
start_time = time.time()
# Use user_id or default to 1
current_user_id = user_id or 1
# Skip database check if force_refresh is True
if not force_refresh:
# First, try to get existing AI analysis from database
logger.info(f"🔍 Checking database for existing AI analysis for user {current_user_id}")
existing_analysis = await ai_analysis_db_service.get_latest_ai_analysis(
user_id=current_user_id,
analysis_type="comprehensive_analysis",
strategy_id=strategy_id,
max_age_hours=24 # Use cached results up to 24 hours old
)
if existing_analysis:
logger.info(f"✅ Found existing AI analysis in database: {existing_analysis.id}")
# Return cached results
return {
"insights": existing_analysis.insights or [],
"recommendations": existing_analysis.recommendations or [],
"total_insights": len(existing_analysis.insights or []),
"total_recommendations": len(existing_analysis.recommendations or []),
"generated_at": existing_analysis.created_at.isoformat(),
"ai_service_status": existing_analysis.ai_service_status,
"processing_time": f"{existing_analysis.processing_time:.2f}s" if existing_analysis.processing_time else "cached",
"personalized_data_used": True if existing_analysis.personalized_data_used else False,
"data_source": "database_cache",
"cache_age_hours": (datetime.utcnow() - existing_analysis.created_at).total_seconds() / 3600,
"user_profile": existing_analysis.personalized_data_used or {}
}
# No recent analysis found or force refresh requested, run new AI analysis
logger.info(f"🔄 Running new AI analysis for user {current_user_id} (force_refresh: {force_refresh})")
# Get personalized inputs from onboarding data
onboarding_service = OnboardingDataService()
personalized_inputs = onboarding_service.get_personalized_ai_inputs(current_user_id)
logger.info(f"📊 Using personalized inputs: {json.dumps(personalized_inputs, indent=2)}")
# Initialize AI services
ai_analytics = AIAnalyticsService()
ai_engine = AIEngineService()
# Generate real AI insights using personalized data
logger.info("🔍 Generating performance analysis...")
performance_analysis = await ai_analytics.analyze_performance_trends(
strategy_id=strategy_id or 1
)
logger.info("🧠 Generating strategic intelligence...")
strategic_intelligence = await ai_analytics.generate_strategic_intelligence(
strategy_id=strategy_id or 1
)
logger.info("📈 Analyzing content evolution...")
evolution_analysis = await ai_analytics.analyze_content_evolution(
strategy_id=strategy_id or 1
)
logger.info("🎯 Generating content recommendations...")
content_recommendations = await ai_engine.generate_content_recommendations(
personalized_inputs
)
# Combine all insights
insights = []
recommendations = []
if performance_analysis:
insights.extend(performance_analysis.get('insights', []))
if strategic_intelligence:
insights.extend(strategic_intelligence.get('insights', []))
if evolution_analysis:
insights.extend(evolution_analysis.get('insights', []))
if content_recommendations:
recommendations.extend(content_recommendations) # content_recommendations is already a list
total_time = time.time() - start_time
logger.info(f"🎉 AI analytics completed in {total_time:.2f}s: {len(insights)} insights, {len(recommendations)} recommendations")
# Store results in database
try:
await ai_analysis_db_service.store_ai_analysis_result(
user_id=current_user_id,
analysis_type="comprehensive_analysis",
insights=insights,
recommendations=recommendations,
performance_metrics=performance_analysis,
personalized_data=personalized_inputs,
processing_time=total_time,
strategy_id=strategy_id,
ai_service_status="operational" if len(insights) > 0 else "fallback"
)
logger.info(f"💾 AI analysis results stored in database for user {current_user_id}")
except Exception as e:
logger.error(f"❌ Failed to store AI analysis in database: {str(e)}")
return {
"insights": insights,
"recommendations": recommendations,
"total_insights": len(insights),
"total_recommendations": len(recommendations),
"generated_at": datetime.utcnow().isoformat(),
"ai_service_status": "operational" if len(insights) > 0 else "fallback",
"processing_time": f"{total_time:.2f}s",
"personalized_data_used": True,
"data_source": "ai_analysis",
"user_profile": {
"website_url": personalized_inputs.get('website_analysis', {}).get('website_url', ''),
"content_types": personalized_inputs.get('website_analysis', {}).get('content_types', []),
"target_audience": personalized_inputs.get('website_analysis', {}).get('target_audience', []),
"industry_focus": personalized_inputs.get('website_analysis', {}).get('industry_focus', 'general')
}
}
except Exception as e:
logger.error(f"❌ Error generating AI analytics: {str(e)}")
raise HTTPException(status_code=500, detail=f"Error generating AI analytics: {str(e)}")
@router.get("/strategies/{strategy_id}/analytics")
async def get_strategy_analytics(
strategy_id: int,
db: Session = Depends(get_db)
):
"""Get analytics for a specific strategy."""
try:
logger.info(f"Fetching analytics for strategy: {strategy_id}")
db_service = ContentPlanningDBService(db)
analytics = await db_service.get_strategy_analytics(strategy_id)
return {
'strategy_id': strategy_id,
'analytics_count': len(analytics),
'analytics': [analytic.to_dict() for analytic in analytics]
}
except Exception as e:
logger.error(f"Error getting strategy analytics: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/strategies/{strategy_id}/events")
async def get_strategy_events(
strategy_id: int,
status: Optional[str] = Query(None, description="Filter by event status"),
db: Session = Depends(get_db)
):
"""Get calendar events for a specific strategy."""
try:
logger.info(f"Fetching events for strategy: {strategy_id}")
db_service = ContentPlanningDBService(db)
if status:
events = await db_service.get_events_by_status(strategy_id, status)
else:
events = await db_service.get_strategy_calendar_events(strategy_id)
return {
'strategy_id': strategy_id,
'events_count': len(events),
'events': [event.to_dict() for event in events]
}
except Exception as e:
logger.error(f"Error getting strategy events: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/users/{user_id}/recommendations")
async def get_user_recommendations(
user_id: int,
priority: Optional[str] = Query(None, description="Filter by priority"),
db: Session = Depends(get_db)
):
"""Get content recommendations for a user."""
try:
logger.info(f"Fetching recommendations for user: {user_id}")
db_service = ContentPlanningDBService(db)
if priority:
recommendations = await db_service.get_recommendations_by_priority(user_id, priority)
else:
recommendations = await db_service.get_user_content_recommendations(user_id)
return {
'user_id': user_id,
'recommendations_count': len(recommendations),
'recommendations': [rec.to_dict() for rec in recommendations]
}
except Exception as e:
logger.error(f"Error getting user recommendations: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/strategies/{strategy_id}/summary")
async def get_strategy_summary(
strategy_id: int,
db: Session = Depends(get_db)
):
"""Get a comprehensive summary of a strategy with analytics."""
try:
logger.info(f"Fetching strategy summary: {strategy_id}")
db_service = ContentPlanningDBService(db)
# Get strategy with analytics
strategies_with_analytics = await db_service.get_strategies_with_analytics(strategy_id)
if strategies_with_analytics:
return strategies_with_analytics[0]
else:
raise HTTPException(status_code=404, detail="Strategy not found")
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting strategy summary: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/health/backend", response_model=Dict[str, Any])
async def check_backend_health():
"""
Check core backend health (independent of AI services)
"""
try:
# Check basic backend functionality
health_status = {
"status": "healthy",
"timestamp": datetime.utcnow().isoformat(),
"services": {
"api_server": True,
"database_connection": False, # Will be updated below
"file_system": True,
"memory_usage": "normal"
},
"version": "1.0.0"
}
# Test database connection
try:
from sqlalchemy import text
db_session = get_db_session()
result = db_session.execute(text("SELECT 1"))
result.fetchone()
health_status["services"]["database_connection"] = True
except Exception as e:
logger.warning(f"Database health check failed: {str(e)}")
health_status["services"]["database_connection"] = False
# Determine overall status
all_services_healthy = all(health_status["services"].values())
health_status["status"] = "healthy" if all_services_healthy else "degraded"
return health_status
except Exception as e:
logger.error(f"Backend health check failed: {e}")
return {
"status": "unhealthy",
"timestamp": datetime.utcnow().isoformat(),
"error": str(e),
"services": {
"api_server": False,
"database_connection": False,
"file_system": False,
"memory_usage": "unknown"
}
}
@router.get("/health/ai", response_model=Dict[str, Any])
async def check_ai_services_health():
"""
Check AI services health separately
"""
try:
health_status = {
"status": "healthy",
"timestamp": datetime.utcnow().isoformat(),
"services": {
"gemini_provider": False,
"ai_analytics_service": False,
"ai_engine_service": False
}
}
# Test Gemini provider
try:
from llm_providers.gemini_provider import get_gemini_api_key
api_key = get_gemini_api_key()
if api_key:
health_status["services"]["gemini_provider"] = True
except Exception as e:
logger.warning(f"Gemini provider health check failed: {e}")
# Test AI Analytics Service
try:
from services.ai_analytics_service import AIAnalyticsService
ai_service = AIAnalyticsService()
health_status["services"]["ai_analytics_service"] = True
except Exception as e:
logger.warning(f"AI Analytics Service health check failed: {e}")
# Test AI Engine Service
try:
from services.content_gap_analyzer.ai_engine_service import AIEngineService
ai_engine = AIEngineService()
health_status["services"]["ai_engine_service"] = True
except Exception as e:
logger.warning(f"AI Engine Service health check failed: {e}")
# Determine overall AI status
ai_services_healthy = any(health_status["services"].values())
health_status["status"] = "healthy" if ai_services_healthy else "unhealthy"
return health_status
except Exception as e:
logger.error(f"AI services health check failed: {e}")
return {
"status": "unhealthy",
"timestamp": datetime.utcnow().isoformat(),
"error": str(e),
"services": {
"gemini_provider": False,
"ai_analytics_service": False,
"ai_engine_service": False
}
}
@router.get("/ai-analytics/stream")
async def stream_ai_analytics_progress(
user_id: Optional[int] = Query(None, description="User ID"),
strategy_id: Optional[int] = Query(None, description="Strategy ID")
):
"""
Stream AI analytics progress in real-time using Server-Sent Events
"""
async def generate_progress():
try:
# Send initial connection message
yield f"data: {json.dumps({'type': 'connected', 'message': 'Starting AI analysis...'})}\n\n"
ai_service = AIAnalyticsService()
current_strategy_id = strategy_id or 1
# Step 1: Performance Analysis
yield f"data: {json.dumps({'type': 'progress', 'step': 'performance', 'message': 'Analyzing performance trends...', 'progress': 20})}\n\n"
performance_start = time.time()
performance_analysis = await ai_service.analyze_performance_trends(
strategy_id=current_strategy_id,
metrics=['engagement_rate', 'reach', 'conversion_rate']
)
performance_time = time.time() - performance_start
yield f"data: {json.dumps({'type': 'progress', 'step': 'performance', 'message': f'Performance analysis completed in {performance_time:.2f}s', 'progress': 40})}\n\n"
# Step 2: Strategic Intelligence
yield f"data: {json.dumps({'type': 'progress', 'step': 'strategic', 'message': 'Generating strategic intelligence...', 'progress': 60})}\n\n"
strategic_start = time.time()
strategic_intelligence = await ai_service.generate_strategic_intelligence(strategy_id=current_strategy_id)
strategic_time = time.time() - strategic_start
yield f"data: {json.dumps({'type': 'progress', 'step': 'strategic', 'message': f'Strategic intelligence completed in {strategic_time:.2f}s', 'progress': 80})}\n\n"
# Step 3: Content Evolution
yield f"data: {json.dumps({'type': 'progress', 'step': 'evolution', 'message': 'Analyzing content evolution...', 'progress': 90})}\n\n"
evolution_start = time.time()
evolution_analysis = await ai_service.analyze_content_evolution(strategy_id=current_strategy_id, time_period="30d")
evolution_time = time.time() - evolution_start
yield f"data: {json.dumps({'type': 'progress', 'step': 'evolution', 'message': f'Content evolution analysis completed in {evolution_time:.2f}s', 'progress': 95})}\n\n"
# Step 4: AI Engine Recommendations
yield f"data: {json.dumps({'type': 'progress', 'step': 'ai_engine', 'message': 'Generating AI recommendations...', 'progress': 98})}\n\n"
engine_start = time.time()
from services.content_gap_analyzer.ai_engine_service import AIEngineService
ai_engine = AIEngineService()
analysis_data = {
"website_analysis": {"content_types": ["blog", "video", "social"]},
"competitor_analysis": {"top_performers": ["competitor1.com", "competitor2.com"]},
"gap_analysis": {"content_gaps": ["AI content", "Video tutorials", "Case studies"]},
"keyword_analysis": {"high_value_keywords": ["AI marketing", "Content automation", "Digital strategy"]}
}
ai_recommendations = await ai_engine.generate_content_recommendations(analysis_data)
engine_time = time.time() - engine_start
yield f"data: {json.dumps({'type': 'progress', 'step': 'ai_engine', 'message': f'AI recommendations completed in {engine_time:.2f}s', 'progress': 100})}\n\n"
# Compile final results
insights = []
recommendations = []
# Extract insights from performance analysis
if 'trend_analysis' in performance_analysis:
for metric, trend_data in performance_analysis['trend_analysis'].items():
if isinstance(trend_data, dict):
trend_direction = trend_data.get('trend_direction', 'stable')
change_percentage = trend_data.get('change_percentage', 0)
if trend_direction == 'increasing':
insights.append({
"id": f"perf_{metric}",
"type": "performance",
"title": f"{metric.replace('_', ' ').title()} Improvement",
"description": f"Your {metric.replace('_', ' ')} is showing positive trends with {change_percentage}% improvement.",
"priority": "high" if change_percentage > 20 else "medium",
"created_at": datetime.utcnow().isoformat()
})
# Extract insights from strategic intelligence
if 'market_positioning' in strategic_intelligence:
market_data = strategic_intelligence['market_positioning']
positioning_score = market_data.get('positioning_score', 0)
if positioning_score < 5:
insights.append({
"id": "market_position",
"type": "warning",
"title": "Market Positioning Needs Improvement",
"description": f"Your market positioning score is {positioning_score}/10. Consider strategic adjustments.",
"priority": "high",
"created_at": datetime.utcnow().isoformat()
})
# Add AI-generated insights
if ai_recommendations and len(ai_recommendations) > 0:
for i, rec in enumerate(ai_recommendations[:2]):
insights.append({
"id": f"ai_gen_{i+1}",
"type": "ai_generated",
"title": rec.get('title', 'AI-Generated Insight'),
"description": rec.get('description', 'AI-powered content recommendation'),
"priority": "high",
"created_at": datetime.utcnow().isoformat()
})
recommendations.append({
"id": f"ai_rec_{i+1}",
"type": "ai_recommendation",
"title": rec.get('title', 'AI Recommendation'),
"description": rec.get('description', ''),
"confidence": rec.get('confidence', 90),
"reasoning": "Generated by AI analysis of content gaps and opportunities",
"action_items": rec.get('action_items', []),
"status": "pending"
})
# Send final results
total_time = time.time() - performance_start
final_results = {
"type": "complete",
"insights": insights,
"recommendations": recommendations,
"total_insights": len(insights),
"total_recommendations": len(recommendations),
"processing_time": f"{total_time:.2f}s",
"ai_service_status": "operational" if len(insights) > 0 else "fallback"
}
yield f"data: {json.dumps(final_results)}\n\n"
except Exception as e:
error_message = {
"type": "error",
"message": f"AI analysis failed: {str(e)}",
"error": str(e)
}
yield f"data: {json.dumps(error_message)}\n\n"
return StreamingResponse(
generate_progress(),
media_type="text/plain",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Type": "text/event-stream"
}
)
@router.get("/ai-analytics/results/{user_id}")
async def get_user_ai_analysis_results(
user_id: int,
analysis_type: Optional[str] = Query(None, description="Filter by analysis type"),
limit: int = Query(10, description="Number of results to return")
):
"""Get AI analysis results for a specific user."""
try:
logger.info(f"Fetching AI analysis results for user {user_id}")
analysis_types = [analysis_type] if analysis_type else None
results = await ai_analysis_db_service.get_user_ai_analyses(
user_id=user_id,
analysis_types=analysis_types,
limit=limit
)
return {
"user_id": user_id,
"results": [result.to_dict() for result in results],
"total_results": len(results)
}
except Exception as e:
logger.error(f"Error fetching AI analysis results: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/ai-analytics/refresh/{user_id}")
async def refresh_ai_analysis(
user_id: int,
analysis_type: str = Query(..., description="Type of analysis to refresh"),
strategy_id: Optional[int] = Query(None, description="Strategy ID")
):
"""Force refresh of AI analysis for a user."""
try:
logger.info(f"Force refreshing AI analysis for user {user_id}, type: {analysis_type}")
# Delete existing analysis to force refresh
await ai_analysis_db_service.delete_old_ai_analyses(days_old=0)
# Run new analysis based on type
if analysis_type == "comprehensive_analysis":
# This will trigger a new comprehensive analysis
return {"message": f"AI analysis refresh initiated for user {user_id}"}
elif analysis_type == "gap_analysis":
# This will trigger a new gap analysis
return {"message": f"Gap analysis refresh initiated for user {user_id}"}
elif analysis_type == "strategic_intelligence":
# This will trigger a new strategic intelligence analysis
return {"message": f"Strategic intelligence refresh initiated for user {user_id}"}
else:
raise HTTPException(status_code=400, detail=f"Unknown analysis type: {analysis_type}")
except Exception as e:
logger.error(f"Error refreshing AI analysis: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.delete("/ai-analytics/cache/{user_id}")
async def clear_ai_analysis_cache(
user_id: int,
analysis_type: Optional[str] = Query(None, description="Specific analysis type to clear")
):
"""Clear AI analysis cache for a user."""
try:
logger.info(f"Clearing AI analysis cache for user {user_id}")
if analysis_type:
# Clear specific analysis type
deleted_count = await ai_analysis_db_service.delete_old_ai_analyses(days_old=0)
return {"message": f"Cleared {deleted_count} cached results for user {user_id}"}
else:
# Clear all cached results
deleted_count = await ai_analysis_db_service.delete_old_ai_analyses(days_old=0)
return {"message": f"Cleared {deleted_count} cached results for user {user_id}"}
except Exception as e:
logger.error(f"Error clearing AI analysis cache: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/ai-analytics/statistics")
async def get_ai_analysis_statistics(
user_id: Optional[int] = Query(None, description="User ID for user-specific stats")
):
"""Get AI analysis statistics."""
try:
logger.info(f"📊 Getting AI analysis statistics for user: {user_id}")
if user_id:
# Get user-specific statistics
user_stats = await ai_analysis_db_service.get_analysis_statistics(user_id)
return {
"user_id": user_id,
"statistics": user_stats,
"message": "User-specific AI analysis statistics retrieved successfully"
}
else:
# Get global statistics
global_stats = await ai_analysis_db_service.get_analysis_statistics()
return {
"statistics": global_stats,
"message": "Global AI analysis statistics retrieved successfully"
}
except Exception as e:
logger.error(f"❌ Error getting AI analysis statistics: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to get AI analysis statistics: {str(e)}"
)
# ============================================================================
# NEW CALENDAR GENERATION ENDPOINTS
# ============================================================================
@router.post("/generate-calendar", response_model=CalendarGenerationResponse)
async def generate_comprehensive_calendar(request: CalendarGenerationRequest):
"""
Generate a comprehensive AI-powered content calendar using database insights.
This endpoint uses advanced AI analysis and comprehensive user data.
"""
try:
logger.info(f"🎯 Generating comprehensive calendar for user {request.user_id}")
start_time = time.time()
# Generate calendar using advanced AI-powered method
calendar_data = await calendar_generator_service.generate_ai_powered_calendar(
user_id=request.user_id,
strategy_id=request.strategy_id,
calendar_type=request.calendar_type,
industry=request.industry,
business_size=request.business_size
)
processing_time = time.time() - start_time
logger.info(f"✅ Calendar generated successfully in {processing_time:.2f}s")
return CalendarGenerationResponse(
user_id=calendar_data["user_id"],
strategy_id=calendar_data["strategy_id"],
calendar_type=calendar_data["calendar_type"],
industry=calendar_data["industry"],
business_size=calendar_data["business_size"],
generated_at=datetime.fromisoformat(calendar_data["generated_at"]),
content_pillars=calendar_data["content_pillars"],
platform_strategies=calendar_data["platform_strategies"],
content_mix=calendar_data["content_mix"],
daily_schedule=calendar_data["daily_schedule"],
weekly_themes=calendar_data["weekly_themes"],
content_recommendations=calendar_data["content_recommendations"],
optimal_timing=calendar_data["optimal_timing"],
performance_predictions=calendar_data["performance_predictions"],
trending_topics=calendar_data["trending_topics"],
repurposing_opportunities=calendar_data["repurposing_opportunities"],
ai_insights=calendar_data["ai_insights"],
competitor_analysis=calendar_data["competitor_analysis"],
gap_analysis_insights=calendar_data["gap_analysis_insights"],
strategy_insights=calendar_data["strategy_insights"],
onboarding_insights=calendar_data["onboarding_insights"],
processing_time=calendar_data["processing_time"],
ai_confidence=calendar_data["ai_confidence"]
)
except Exception as e:
logger.error(f"❌ Error generating comprehensive calendar: {str(e)}")
logger.error(f"Exception type: {type(e)}")
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
raise HTTPException(
status_code=500,
detail=f"Error generating comprehensive calendar: {str(e)}"
)
@router.post("/optimize-content", response_model=ContentOptimizationResponse)
async def optimize_content_for_platform(request: ContentOptimizationRequest):
"""
Optimize content for specific platforms using database insights.
This endpoint optimizes content based on:
- Historical performance data for the platform
- Audience preferences from onboarding data
- Gap analysis insights for content improvement
- Competitor analysis for differentiation
"""
try:
logger.info(f"🔧 Starting content optimization for user {request.user_id}")
# Validate API keys
api_key_status = await check_all_api_keys()
if not api_key_status["all_keys_present"]:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="AI services are not properly configured"
)
# Get user data for optimization
user_data = await calendar_generator_service._get_comprehensive_user_data(
request.user_id,
None # No strategy_id for content optimization
)
# Create optimization request for AI
optimization_prompt = f"""
Optimize the following content for {request.target_platform}:
Original Content:
- Title: {request.title}
- Description: {request.description}
- Content Type: {request.content_type}
- Platform: {request.target_platform}
User Context:
- Industry: {user_data.get('industry', 'technology')}
- Target Audience: {user_data.get('target_audience', {})}
- Performance Data: {user_data.get('performance_data', {})}
- Gap Analysis: {user_data.get('gap_analysis', {})}
Provide comprehensive optimization including:
1. Platform-specific adaptations
2. Visual recommendations
3. Hashtag suggestions
4. Keyword optimization
5. Tone adjustments
6. Length optimization
7. Performance predictions
"""
# Generate optimization using AI
optimization_result = await calendar_generator_service.ai_engine.generate_structured_response(
prompt=optimization_prompt,
schema={
"type": "object",
"properties": {
"optimized_content": {"type": "object"},
"platform_adaptations": {"type": "array", "items": {"type": "string"}},
"visual_recommendations": {"type": "array", "items": {"type": "string"}},
"hashtag_suggestions": {"type": "array", "items": {"type": "string"}},
"keyword_optimization": {"type": "object"},
"tone_adjustments": {"type": "object"},
"length_optimization": {"type": "object"},
"performance_prediction": {"type": "object"},
"optimization_score": {"type": "number"}
}
}
)
# Prepare response
response_data = {
"user_id": request.user_id,
"event_id": request.event_id,
"original_content": {
"title": request.title,
"description": request.description,
"content_type": request.content_type,
"target_platform": request.target_platform
},
"optimized_content": optimization_result.get("optimized_content", {}),
"platform_adaptations": optimization_result.get("platform_adaptations", []),
"visual_recommendations": optimization_result.get("visual_recommendations", []),
"hashtag_suggestions": optimization_result.get("hashtag_suggestions", []),
"keyword_optimization": optimization_result.get("keyword_optimization", {}),
"tone_adjustments": optimization_result.get("tone_adjustments", {}),
"length_optimization": optimization_result.get("length_optimization", {}),
"performance_prediction": optimization_result.get("performance_prediction", {}),
"optimization_score": optimization_result.get("optimization_score", 0.8),
"created_at": datetime.utcnow()
}
logger.info(f"✅ Content optimization completed for user {request.user_id}")
return ContentOptimizationResponse(**response_data)
except HTTPException:
raise
except Exception as e:
logger.error(f"❌ Error optimizing content: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to optimize content: {str(e)}"
)
@router.post("/performance-predictions", response_model=PerformancePredictionResponse)
async def predict_content_performance(request: PerformancePredictionRequest):
"""
Predict content performance using database insights.
This endpoint predicts performance based on:
- Historical performance data
- Audience demographics and preferences
- Content type and platform patterns
- Gap analysis opportunities
"""
try:
logger.info(f"📊 Starting performance prediction for user {request.user_id}")
# Get user data for prediction
user_data = await calendar_generator_service._get_comprehensive_user_data(
request.user_id,
request.strategy_id
)
# Generate performance prediction
prediction_prompt = f"""
Predict performance for the following content:
Content Data:
- Content Type: {request.content_type}
- Platform: {request.platform}
- Content Data: {request.content_data}
User Context:
- Industry: {user_data.get('industry', 'technology')}
- Performance Data: {user_data.get('performance_data', {})}
- Gap Analysis: {user_data.get('gap_analysis', {})}
- Audience Insights: {user_data.get('onboarding_data', {}).get('target_audience', {})}
Provide performance predictions including:
1. Engagement rate
2. Reach estimates
3. Conversion predictions
4. ROI estimates
5. Confidence score
6. Recommendations
"""
# Generate prediction using AI
prediction_result = await calendar_generator_service.ai_engine.generate_structured_response(
prompt=prediction_prompt,
schema={
"type": "object",
"properties": {
"predicted_engagement_rate": {"type": "number"},
"predicted_reach": {"type": "integer"},
"predicted_conversions": {"type": "integer"},
"predicted_roi": {"type": "number"},
"confidence_score": {"type": "number"},
"recommendations": {"type": "array", "items": {"type": "string"}}
}
}
)
# Prepare response
response_data = {
"user_id": request.user_id,
"strategy_id": request.strategy_id,
"content_type": request.content_type,
"platform": request.platform,
"predicted_engagement_rate": prediction_result.get("predicted_engagement_rate", 0.05),
"predicted_reach": prediction_result.get("predicted_reach", 1000),
"predicted_conversions": prediction_result.get("predicted_conversions", 10),
"predicted_roi": prediction_result.get("predicted_roi", 2.5),
"confidence_score": prediction_result.get("confidence_score", 0.75),
"recommendations": prediction_result.get("recommendations", []),
"created_at": datetime.utcnow()
}
logger.info(f"✅ Performance prediction completed for user {request.user_id}")
return PerformancePredictionResponse(**response_data)
except Exception as e:
logger.error(f"❌ Error predicting content performance: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to predict content performance: {str(e)}"
)
@router.post("/repurpose-content", response_model=ContentRepurposingResponse)
async def repurpose_content_across_platforms(request: ContentRepurposingRequest):
"""
Repurpose content across different platforms using database insights.
This endpoint suggests content repurposing based on:
- Existing content and strategy data
- Gap analysis opportunities
- Platform-specific requirements
- Audience preferences
"""
try:
logger.info(f"🔄 Starting content repurposing for user {request.user_id}")
# Get user data for repurposing
user_data = await calendar_generator_service._get_comprehensive_user_data(
request.user_id,
request.strategy_id
)
# Generate repurposing suggestions
repurposing_prompt = f"""
Repurpose the following content for multiple platforms:
Original Content:
{request.original_content}
Target Platforms:
{request.target_platforms}
User Context:
- Gap Analysis: {user_data.get('gap_analysis', {})}
- Strategy Data: {user_data.get('strategy_data', {})}
- Recommendations: {user_data.get('recommendations_data', [])}
Provide repurposing suggestions including:
1. Platform-specific adaptations
2. Content transformations
3. Implementation tips
4. Gap addressing opportunities
"""
# Generate repurposing suggestions using AI
repurposing_result = await calendar_generator_service.ai_engine.generate_structured_response(
prompt=repurposing_prompt,
schema={
"type": "object",
"properties": {
"platform_adaptations": {"type": "array", "items": {"type": "object"}},
"transformations": {"type": "array", "items": {"type": "object"}},
"implementation_tips": {"type": "array", "items": {"type": "string"}},
"gap_addresses": {"type": "array", "items": {"type": "string"}}
}
}
)
# Prepare response
response_data = {
"user_id": request.user_id,
"strategy_id": request.strategy_id,
"original_content": request.original_content,
"platform_adaptations": repurposing_result.get("platform_adaptations", []),
"transformations": repurposing_result.get("transformations", []),
"implementation_tips": repurposing_result.get("implementation_tips", []),
"gap_addresses": repurposing_result.get("gap_addresses", []),
"created_at": datetime.utcnow()
}
logger.info(f"✅ Content repurposing completed for user {request.user_id}")
return ContentRepurposingResponse(**response_data)
except Exception as e:
logger.error(f"❌ Error repurposing content: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to repurpose content: {str(e)}"
)
@router.get("/trending-topics", response_model=TrendingTopicsResponse)
async def get_trending_topics(
user_id: int = Query(..., description="User ID"),
industry: str = Query(..., description="Industry for trending topics"),
limit: int = Query(10, description="Number of trending topics to return")
):
"""
Get trending topics relevant to the user's industry and content gaps.
This endpoint provides trending topics based on:
- Industry-specific trends
- Gap analysis keyword opportunities
- Audience alignment assessment
- Competitor analysis insights
"""
try:
logger.info(f"📈 Getting trending topics for user {user_id} in {industry}")
# Get user data for trending topics
user_data = await calendar_generator_service._get_comprehensive_user_data(user_id, None)
# Get trending topics with database insights
trending_topics = await calendar_generator_service._get_trending_topics_from_db(industry, user_data)
# Limit results
limited_topics = trending_topics[:limit]
# Calculate relevance scores
gap_relevance_scores = {}
audience_alignment_scores = {}
for topic in limited_topics:
topic_key = topic.get("keyword", "")
gap_relevance_scores[topic_key] = calendar_generator_service._assess_gap_relevance(topic, user_data.get("gap_analysis", {}))
audience_alignment_scores[topic_key] = calendar_generator_service._assess_audience_alignment(topic, user_data.get("onboarding_data", {}))
# Prepare response
response_data = {
"user_id": user_id,
"industry": industry,
"trending_topics": limited_topics,
"gap_relevance_scores": gap_relevance_scores,
"audience_alignment_scores": audience_alignment_scores,
"created_at": datetime.utcnow()
}
logger.info(f"✅ Trending topics retrieved for user {user_id}")
return TrendingTopicsResponse(**response_data)
except Exception as e:
logger.error(f"❌ Error getting trending topics: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to get trending topics: {str(e)}"
)
@router.get("/comprehensive-user-data")
async def get_comprehensive_user_data(
user_id: int = Query(..., description="User ID"),
db: Session = Depends(get_db)
) -> Dict[str, Any]:
"""
Get comprehensive user data for calendar generation.
This endpoint aggregates all data points needed for the calendar wizard.
"""
try:
logger.info(f"Getting comprehensive user data for user_id: {user_id}")
# Get comprehensive data using the calendar generator service
logger.info("Calling calendar generator service...")
comprehensive_data = await calendar_generator_service._get_comprehensive_user_data(user_id, None)
logger.info(f"Calendar generator service returned: {type(comprehensive_data)}")
logger.info(f"Successfully retrieved comprehensive user data for user_id: {user_id}")
return {
"status": "success",
"data": comprehensive_data,
"message": "Comprehensive user data retrieved successfully",
"timestamp": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"Error getting comprehensive user data for user_id {user_id}: {str(e)}")
logger.error(f"Exception type: {type(e)}")
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
raise HTTPException(
status_code=500,
detail=f"Error retrieving comprehensive user data: {str(e)}"
)
@router.get("/calendar-generation/health")
async def calendar_generation_health_check():
"""
Health check for calendar generation services.
"""
try:
logger.info("🏥 Performing calendar generation health check")
# Check AI services
api_key_status = await check_all_api_keys()
# Check database connectivity
db_status = "healthy"
try:
# Test database connection - only if calendar generator service is properly initialized
if hasattr(calendar_generator_service, 'content_planning_db_service') and calendar_generator_service.content_planning_db_service is not None:
await calendar_generator_service.content_planning_db_service.get_user_content_gap_analyses(1)
else:
db_status = "not_initialized"
except Exception as e:
db_status = f"error: {str(e)}"
health_status = {
"service": "calendar_generation",
"status": "healthy" if api_key_status["all_keys_present"] and db_status == "healthy" else "unhealthy",
"timestamp": datetime.utcnow().isoformat(),
"components": {
"ai_services": "healthy" if api_key_status["all_keys_present"] else "unhealthy",
"database": db_status,
"calendar_generator": "healthy"
},
"api_keys": api_key_status
}
logger.info("✅ Calendar generation health check completed")
return health_status
except Exception as e:
logger.error(f"❌ Calendar generation health check failed: {str(e)}")
return {
"service": "calendar_generation",
"status": "unhealthy",
"timestamp": datetime.utcnow().isoformat(),
"error": str(e)
}
@router.get("/debug/strategies/{user_id}")
async def debug_content_strategies(user_id: int):
"""
Debug endpoint to print content strategy data directly.
"""
try:
logger.info(f"🔍 DEBUG: Getting content strategy data for user {user_id}")
# Get latest AI analysis
latest_analysis = await ai_analysis_db_service.get_latest_ai_analysis(
user_id=user_id,
analysis_type="strategic_intelligence"
)
if latest_analysis:
logger.info("📊 DEBUG: Content Strategy Data Found")
logger.info("=" * 50)
logger.info("FULL CONTENT STRATEGY DATA:")
logger.info("=" * 50)
# Print the entire data structure
import json
logger.info(json.dumps(latest_analysis, indent=2, default=str))
return {
"status": "success",
"message": "Content strategy data printed to logs",
"data": latest_analysis
}
else:
logger.warning("⚠️ DEBUG: No content strategy data found")
return {
"status": "not_found",
"message": "No content strategy data found",
"data": None
}
except Exception as e:
logger.error(f"❌ DEBUG: Error getting content strategy data: {str(e)}")
import traceback
logger.error(f"DEBUG Traceback: {traceback.format_exc()}")
raise HTTPException(
status_code=500,
detail=f"Debug error: {str(e)}"
)