Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts

This commit is contained in:
ajaysi
2026-02-08 13:56:57 +05:30
parent 1db10ccd0f
commit e404a86502
333 changed files with 42223 additions and 10875 deletions

View File

@@ -14,9 +14,16 @@ from services.onboarding.api_key_manager import APIKeyManager
from services.validation import check_all_api_keys
from services.seo_analyzer import ComprehensiveSEOAnalyzer, SEOAnalysisResult, SEOAnalysisService
from services.user_data_service import UserDataService
from services.database import get_db_session
from services.database import get_db_session, get_session_for_user
from services.seo import SEODashboardService
from middleware.auth_middleware import get_current_user
from services.llm_providers.main_text_generation import llm_text_gen
from api.content_planning.services.content_strategy.onboarding import OnboardingDataIntegrationService
from models.onboarding import SEOPageAudit, WebsiteAnalysis, OnboardingSession
from sqlalchemy.orm.attributes import flag_modified
# Phase 2B: Import semantic monitoring
from services.intelligence.monitoring.semantic_dashboard import RealTimeSemanticMonitor, SemanticHealthMetric
# Initialize the SEO analyzer
seo_analyzer = ComprehensiveSEOAnalyzer()
@@ -64,6 +71,9 @@ class SEOAnalysisRequest(BaseModel):
url: str
target_keywords: Optional[List[str]] = None
class AnalyzeURLsRequest(BaseModel):
urls: List[str]
class SEOAnalysisResponse(BaseModel):
url: str
timestamp: datetime
@@ -239,12 +249,105 @@ def generate_ai_insights(metrics: Dict[str, Any], platforms: Dict[str, Any]) ->
return insights
from services.seo.deep_competitor_analysis_service import DeepCompetitorAnalysisService
# API Endpoints
async def run_strategic_insights(
current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
"""
Manually trigger AI-Powered Competitive Insights (Weekly Strategy Brief).
"""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session(user_id)
if not db_session:
raise HTTPException(status_code=500, detail="Database connection unavailable")
try:
# 1. Get Website Analysis (with fallback)
website_analysis_data = None
analysis_id = None
# Try SSOT first
integration_service = OnboardingDataIntegrationService()
integrated_data = integration_service.get_integrated_data_sync(user_id, db_session)
if integrated_data and integrated_data.get("website_analysis"):
website_analysis_data = integrated_data.get("website_analysis")
analysis_id = website_analysis_data.get("id")
# Fallback: Find latest WebsiteAnalysis across sessions
if not website_analysis_data:
latest_analysis = db_session.query(WebsiteAnalysis).join(
OnboardingSession, WebsiteAnalysis.session_id == OnboardingSession.id
).filter(
OnboardingSession.user_id == user_id
).order_by(WebsiteAnalysis.updated_at.desc()).first()
if latest_analysis:
# Convert to dict
from fastapi.encoders import jsonable_encoder
website_analysis_data = jsonable_encoder(latest_analysis)
analysis_id = latest_analysis.id
if not website_analysis_data:
raise HTTPException(status_code=400, detail="No website analysis found. Please complete Onboarding Step 2.")
# 2. Get Competitors
competitors = []
if integrated_data:
competitors = integrated_data.get("competitor_analysis", [])
if not competitors:
# Fallback to research preferences
research_prefs = integrated_data.get("research_preferences", {})
competitors = research_prefs.get("competitors", [])
if not competitors:
raise HTTPException(status_code=400, detail="No competitors found. Please complete Onboarding Step 3.")
# 3. Run Analysis
service = DeepCompetitorAnalysisService()
report = await service.generate_weekly_strategy_brief(
user_id=user_id,
website_analysis=website_analysis_data,
competitors=competitors
)
# 4. Persist to History
if analysis_id:
wa = db_session.query(WebsiteAnalysis).filter(WebsiteAnalysis.id == analysis_id).first()
if wa:
history = wa.strategic_insights_history or []
# Ensure history is a list
if not isinstance(history, list):
history = []
# Prepend new report
history.insert(0, report)
# Keep last 52 weeks
wa.strategic_insights_history = history[:52]
flag_modified(wa, "strategic_insights_history")
db_session.commit()
return report
finally:
db_session.close()
except HTTPException as he:
raise he
except Exception as e:
logger.error(f"Error running strategic insights: {e}")
raise HTTPException(status_code=500, detail=f"Failed to run analysis: {str(e)}")
async def get_seo_dashboard_data(current_user: dict = Depends(get_current_user)) -> SEODashboardData:
"""Get comprehensive SEO dashboard data."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
logger.error("No database session available")
@@ -278,7 +381,7 @@ async def get_seo_health_score(current_user: dict = Depends(get_current_user)) -
"""Get current SEO health score."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
raise HTTPException(status_code=500, detail="Database connection unavailable")
@@ -299,7 +402,7 @@ async def get_seo_metrics(current_user: dict = Depends(get_current_user)) -> Dic
"""Get SEO metrics."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
raise HTTPException(status_code=500, detail="Database connection unavailable")
@@ -322,7 +425,7 @@ async def get_platform_status(
"""Get platform connection status."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
logger.error("No database session available")
@@ -347,7 +450,7 @@ async def get_ai_insights(current_user: dict = Depends(get_current_user)) -> Lis
"""Get AI-generated insights."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
raise HTTPException(status_code=500, detail="Database connection unavailable")
@@ -368,6 +471,59 @@ async def seo_dashboard_health_check():
"""Health check for SEO dashboard."""
return {"status": "healthy", "service": "SEO Dashboard API"}
# Phase 2B: Semantic health monitoring endpoint
async def get_semantic_health(current_user: dict = Depends(get_current_user)) -> SemanticHealthMetric:
"""
Get real-time semantic health metrics for the user's content and competitors.
This endpoint provides Phase 2B semantic intelligence monitoring data.
Returns:
SemanticHealthMetric with current health status, score, and recommendations
"""
try:
user_id = str(current_user.get('id'))
# Initialize semantic monitor for this user
semantic_monitor = RealTimeSemanticMonitor(user_id)
# Get current semantic health (will use cache if available)
semantic_health = await semantic_monitor.check_semantic_health(user_id)
logger.info(f"[Semantic Health API] Retrieved health data for user {user_id}: {semantic_health.status} (score: {semantic_health.value:.2f})")
return semantic_health
except Exception as e:
logger.error(f"[Semantic Health API] Error retrieving semantic health for user: {e}")
# Return a default healthy state with warning message
return SemanticHealthMetric(
metric_name="semantic_health",
value=0.5,
threshold=0.6,
status="warning",
timestamp=datetime.utcnow().isoformat(),
description="Semantic monitoring temporarily unavailable",
recommendations=["Please try again later", "Check system status"]
)
async def get_semantic_cache_stats(current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
"""
Get statistics for the semantic cache.
"""
try:
user_id = str(current_user.get('id'))
# Initialize semantic monitor to access its cache manager
semantic_monitor = RealTimeSemanticMonitor(user_id)
return await semantic_monitor.get_cache_stats()
except Exception as e:
logger.error(f"[Semantic Cache API] Error retrieving cache stats: {e}")
return {
"error": "Failed to retrieve cache statistics",
"hit_rate": 0.0,
"memory_usage_mb": 0.0
}
# New comprehensive SEO analysis endpoints
async def analyze_seo_comprehensive(request: SEOAnalysisRequest) -> SEOAnalysisResponse:
"""
@@ -650,6 +806,107 @@ async def batch_analyze_urls(urls: List[str]) -> Dict[str, Any]:
detail=f"Error in batch analysis: {str(e)}"
)
async def analyze_urls_ai(request: AnalyzeURLsRequest, current_user: dict) -> Dict[str, Any]:
"""Run AI analysis on selected URLs."""
user_id = str(current_user.get('id'))
db_session = get_db_session()
results = []
try:
for url in request.urls:
# Check if audit exists
audit = db_session.query(SEOPageAudit).filter(
SEOPageAudit.user_id == user_id,
SEOPageAudit.page_url == url
).first()
if not audit:
results.append({"url": url, "status": "skipped", "reason": "No audit found"})
continue
# Prepare Prompt
# We use the existing audit data (algorithmic) to feed the AI
audit_summary = {
"score": audit.overall_score,
"issues": audit.issues,
"warnings": audit.warnings
}
prompt = f"""
As an expert SEO consultant, analyze these technical audit results for the page: {url}
AUDIT DATA:
{json.dumps(audit_summary, default=str)[:3000]}
TASK:
Provide 3 specific, high-impact AI recommendations to improve this page's SEO.
Focus on content relevance, user intent, and semantic SEO, which the algorithmic audit might miss.
OUTPUT JSON format:
[
{{ "category": "Content|Technical|UX", "recommendation": "...", "impact": "High|Medium", "effort": "Low|Medium" }}
]
"""
try:
ai_response = llm_text_gen(prompt, user_id=user_id)
# Parse JSON
import re
cleaned = ai_response.strip().replace("```json", "").replace("```", "")
# Simple regex to find the JSON array if extra text exists
match = re.search(r'\[.*\]', cleaned, re.DOTALL)
if match:
cleaned = match.group(0)
recommendations = json.loads(cleaned)
# Update audit
current_recs = audit.recommendations or []
if isinstance(current_recs, list):
# Tag new ones
for r in recommendations:
r['source'] = 'ai_on_demand'
current_recs.extend(recommendations)
audit.recommendations = current_recs
audit.last_analyzed_at = datetime.utcnow()
results.append({"url": url, "status": "success"})
except Exception as e:
logger.error(f"AI Analysis failed for {url}: {e}")
results.append({"url": url, "status": "failed", "error": str(e)})
db_session.commit()
return {"results": results}
finally:
db_session.close()
async def get_analyzed_pages(current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
"""Get list of pages that have been analyzed by AI."""
user_id = str(current_user.get('id'))
db_session = get_db_session()
try:
audits = db_session.query(SEOPageAudit).filter(
SEOPageAudit.user_id == user_id
).all()
results = []
for audit in audits:
if audit.recommendations:
results.append({
"url": audit.page_url,
"analyzed_at": audit.last_analyzed_at,
"score": audit.overall_score,
"recommendations_count": len(audit.recommendations)
})
return {"results": results}
finally:
db_session.close()
# New SEO Dashboard Endpoints with Real Data
async def get_seo_dashboard_overview(
@@ -659,7 +916,7 @@ async def get_seo_dashboard_overview(
"""Get comprehensive SEO dashboard overview with real GSC/Bing data."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_session_for_user(user_id)
if not db_session:
logger.error("No database session available")
@@ -715,7 +972,7 @@ async def get_bing_raw_data(
"""Get raw Bing data for the specified site."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
logger.error("No database session available")
@@ -743,7 +1000,7 @@ async def get_competitive_insights(
"""Get competitive insights from onboarding step 3 data."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
logger.error("No database session available")
@@ -764,6 +1021,153 @@ async def get_competitive_insights(
logger.error(f"Error getting competitive insights: {e}")
raise HTTPException(status_code=500, detail="Failed to get competitive insights")
async def get_deep_competitor_analysis(
current_user: dict = Depends(get_current_user),
site_url: Optional[str] = None
) -> Dict[str, Any]:
try:
user_id = str(current_user.get('id'))
db_session = get_session_for_user(user_id)
if not db_session:
logger.error("No database session available")
raise HTTPException(status_code=500, detail="Database connection failed")
try:
integration_service = OnboardingDataIntegrationService()
integrated = integration_service.get_integrated_data_sync(user_id, db_session)
deep = integrated.get("deep_competitor_analysis") if isinstance(integrated, dict) else None
return deep or {
"status": "not_available",
"last_run": None,
"report": None
}
finally:
db_session.close()
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting deep competitor analysis: {e}")
raise HTTPException(status_code=500, detail="Failed to get deep competitor analysis")
async def run_strategic_insights(
current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
"""Run AI-powered strategic insights analysis manually."""
try:
user_id = str(current_user.get('id'))
db_session = get_session_for_user(user_id)
if not db_session:
raise HTTPException(status_code=500, detail="Database connection failed")
try:
integration_service = OnboardingDataIntegrationService()
integrated = integration_service.get_integrated_data_sync(user_id, db_session)
website_analysis_data = integrated.get("website_analysis")
logger.info(f"Integrated data for user {user_id}: website_analysis found? {bool(website_analysis_data)}")
# Fallback: If not found in integrated data (e.g. strict session mismatch), find latest analysis for user
if not website_analysis_data:
logger.info(f"Attempting fallback for user {user_id}")
# Find latest WebsiteAnalysis for this user across all sessions
latest_analysis = db_session.query(WebsiteAnalysis).join(
OnboardingSession, WebsiteAnalysis.session_id == OnboardingSession.id
).filter(
OnboardingSession.user_id == user_id
).order_by(WebsiteAnalysis.updated_at.desc()).first()
if latest_analysis:
logger.info(f"Found fallback WebsiteAnalysis {latest_analysis.id} for user {user_id}")
website_analysis_data = latest_analysis.to_dict()
# Ensure ID is present for updates
website_analysis_data['id'] = latest_analysis.id
else:
logger.warning(f"Fallback failed for user {user_id}. No WebsiteAnalysis found.")
if not website_analysis_data:
raise HTTPException(status_code=400, detail="Website analysis (Step 2) not found. Please complete onboarding.")
research_prefs = integrated.get("research_preferences")
competitors = (research_prefs.get("competitors") if isinstance(research_prefs, dict) else None)
if not competitors:
# Try competitor_analysis as fallback
competitors = integrated.get("competitor_analysis") or []
if not competitors:
raise HTTPException(status_code=400, detail="No competitors found. Please add competitors in Step 3.")
from services.seo.deep_competitor_analysis_service import DeepCompetitorAnalysisService
analysis_service = DeepCompetitorAnalysisService()
logger.info(f"Running manual strategic insights for user {user_id}")
report = await analysis_service.generate_weekly_strategy_brief(
user_id=user_id,
website_analysis=website_analysis_data if isinstance(website_analysis_data, dict) else {},
competitors=competitors if isinstance(competitors, list) else []
)
# Find the WebsiteAnalysis record to persist history
analysis_id = website_analysis_data.get('id') if isinstance(website_analysis_data, dict) else None
if analysis_id:
website_analysis = db_session.query(WebsiteAnalysis).filter(WebsiteAnalysis.id == analysis_id).first()
if website_analysis:
history = website_analysis.strategic_insights_history or []
if not isinstance(history, list):
history = []
# Append new report at the beginning (latest first)
history.insert(0, report)
# Keep last 52 weeks (1 year)
website_analysis.strategic_insights_history = history[:52]
flag_modified(website_analysis, "strategic_insights_history")
db_session.commit()
logger.info(f"Persisted strategic insight for user {user_id} to history")
return {"success": True, "report": report}
finally:
db_session.close()
except HTTPException:
raise
except Exception as e:
logger.error(f"Error running strategic insights: {e}")
raise HTTPException(status_code=500, detail=f"Failed to run strategic insights: {str(e)}")
async def get_strategic_insights_history(
current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
"""Fetch the history of strategic insights for the user."""
try:
user_id = str(current_user.get('id'))
db_session = get_session_for_user(user_id)
if not db_session:
raise HTTPException(status_code=500, detail="Database connection failed")
try:
integration_service = OnboardingDataIntegrationService()
integrated = integration_service.get_integrated_data_sync(user_id, db_session)
website_analysis = integrated.get("website_analysis")
if not website_analysis or not isinstance(website_analysis, dict):
return {"history": []}
history = website_analysis.get("strategic_insights_history") or []
return {"history": history}
finally:
db_session.close()
except Exception as e:
logger.error(f"Error fetching strategic insights history: {e}")
raise HTTPException(status_code=500, detail="Failed to fetch strategic insights history")
async def refresh_analytics_data(
current_user: dict = Depends(get_current_user),
site_url: Optional[str] = None
@@ -771,7 +1175,7 @@ async def refresh_analytics_data(
"""Refresh analytics data by invalidating cache and fetching fresh data."""
try:
user_id = str(current_user.get('id'))
db_session = get_db_session()
db_session = get_db_session(user_id)
if not db_session:
logger.error("No database session available")
@@ -849,4 +1253,4 @@ def _convert_platforms(platform_data: Dict[str, Any]) -> Dict[str, PlatformStatu
}
except Exception as e:
logger.error(f"Error converting platforms: {e}")
return {}
return {}