ALwrity version 0.5.5
This commit is contained in:
@@ -17,6 +17,12 @@ from .enhanced_strategy_routes import router as enhanced_strategy_router
|
||||
# Import content strategy routes
|
||||
from .content_strategy.routes import router as content_strategy_router
|
||||
|
||||
# Import monitoring routes
|
||||
from ..monitoring_routes import router as monitoring_router
|
||||
|
||||
# Import quality analysis routes
|
||||
from ..quality_analysis_routes import router as quality_analysis_router
|
||||
|
||||
# Create main router
|
||||
router = APIRouter(prefix="/api/content-planning", tags=["content-planning"])
|
||||
|
||||
@@ -34,6 +40,12 @@ router.include_router(enhanced_strategy_router, prefix="/enhanced-strategies")
|
||||
# Include content strategy routes
|
||||
router.include_router(content_strategy_router)
|
||||
|
||||
# Include monitoring routes
|
||||
router.include_router(monitoring_router)
|
||||
|
||||
# Include quality analysis routes
|
||||
router.include_router(quality_analysis_router)
|
||||
|
||||
# Add health check endpoint
|
||||
@router.get("/health")
|
||||
async def content_planning_health_check():
|
||||
|
||||
@@ -94,7 +94,7 @@ async def check_ai_services_health():
|
||||
|
||||
# Test Gemini provider
|
||||
try:
|
||||
from llm_providers.gemini_provider import get_gemini_api_key
|
||||
from services.llm_providers.gemini_provider import get_gemini_api_key
|
||||
api_key = get_gemini_api_key()
|
||||
if api_key:
|
||||
health_status["services"]["gemini_provider"] = True
|
||||
|
||||
695
backend/api/content_planning/monitoring_routes.py
Normal file
695
backend/api/content_planning/monitoring_routes.py
Normal file
@@ -0,0 +1,695 @@
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||
from typing import Dict, Any
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import and_, desc
|
||||
import json
|
||||
|
||||
from services.monitoring_plan_generator import MonitoringPlanGenerator
|
||||
from services.strategy_service import StrategyService
|
||||
from services.database import get_db
|
||||
from models.monitoring_models import (
|
||||
StrategyMonitoringPlan, MonitoringTask, TaskExecutionLog,
|
||||
StrategyPerformanceMetrics, StrategyActivationStatus
|
||||
)
|
||||
from models.enhanced_strategy_models import EnhancedContentStrategy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/strategy", tags=["strategy-monitoring"])
|
||||
|
||||
@router.post("/{strategy_id}/generate-monitoring-plan")
|
||||
async def generate_monitoring_plan(strategy_id: int):
|
||||
"""Generate monitoring plan for a strategy"""
|
||||
try:
|
||||
generator = MonitoringPlanGenerator()
|
||||
plan = await generator.generate_monitoring_plan(strategy_id)
|
||||
|
||||
logger.info(f"Successfully generated monitoring plan for strategy {strategy_id}")
|
||||
return {
|
||||
"success": True,
|
||||
"data": plan,
|
||||
"message": "Monitoring plan generated successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating monitoring plan for strategy {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to generate monitoring plan: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/{strategy_id}/activate-with-monitoring")
|
||||
async def activate_strategy_with_monitoring(
|
||||
strategy_id: int,
|
||||
monitoring_plan: Dict[str, Any]
|
||||
):
|
||||
"""Activate strategy with monitoring plan"""
|
||||
try:
|
||||
strategy_service = StrategyService()
|
||||
|
||||
# Activate strategy
|
||||
activation_success = await strategy_service.activate_strategy(strategy_id)
|
||||
if not activation_success:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Failed to activate strategy {strategy_id}"
|
||||
)
|
||||
|
||||
# Save monitoring plan
|
||||
plan_success = await strategy_service.save_monitoring_plan(strategy_id, monitoring_plan)
|
||||
if not plan_success:
|
||||
logger.warning(f"Failed to save monitoring plan for strategy {strategy_id}")
|
||||
|
||||
logger.info(f"Successfully activated strategy {strategy_id} with monitoring")
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Strategy activated with monitoring successfully",
|
||||
"strategy_id": strategy_id
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error activating strategy {strategy_id} with monitoring: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to activate strategy with monitoring: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{strategy_id}/monitoring-plan")
|
||||
async def get_monitoring_plan(strategy_id: int):
|
||||
"""Get monitoring plan for a strategy"""
|
||||
try:
|
||||
strategy_service = StrategyService()
|
||||
monitoring_plan = await strategy_service.get_monitoring_plan(strategy_id)
|
||||
|
||||
if monitoring_plan:
|
||||
return {
|
||||
"success": True,
|
||||
"data": monitoring_plan
|
||||
}
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Monitoring plan not found for strategy {strategy_id}"
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting monitoring plan for strategy {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get monitoring plan: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{strategy_id}/performance-history")
|
||||
async def get_strategy_performance_history(strategy_id: int, days: int = 30):
|
||||
"""Get performance history for a strategy"""
|
||||
try:
|
||||
strategy_service = StrategyService()
|
||||
performance_history = await strategy_service.get_strategy_performance_history(strategy_id, days)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"strategy_id": strategy_id,
|
||||
"performance_history": performance_history,
|
||||
"days": days
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting performance history for strategy {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get performance history: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/{strategy_id}/deactivate")
|
||||
async def deactivate_strategy(strategy_id: int, user_id: int = 1):
|
||||
"""Deactivate a strategy"""
|
||||
try:
|
||||
strategy_service = StrategyService()
|
||||
success = await strategy_service.deactivate_strategy(strategy_id, user_id)
|
||||
|
||||
if success:
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Strategy {strategy_id} deactivated successfully"
|
||||
}
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Failed to deactivate strategy {strategy_id}"
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error deactivating strategy {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to deactivate strategy: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/{strategy_id}/pause")
|
||||
async def pause_strategy(strategy_id: int, user_id: int = 1):
|
||||
"""Pause a strategy"""
|
||||
try:
|
||||
strategy_service = StrategyService()
|
||||
success = await strategy_service.pause_strategy(strategy_id, user_id)
|
||||
|
||||
if success:
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Strategy {strategy_id} paused successfully"
|
||||
}
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Failed to pause strategy {strategy_id}"
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error pausing strategy {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to pause strategy: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/{strategy_id}/resume")
|
||||
async def resume_strategy(strategy_id: int, user_id: int = 1):
|
||||
"""Resume a paused strategy"""
|
||||
try:
|
||||
strategy_service = StrategyService()
|
||||
success = await strategy_service.resume_strategy(strategy_id, user_id)
|
||||
|
||||
if success:
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Strategy {strategy_id} resumed successfully"
|
||||
}
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Failed to resume strategy {strategy_id}"
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error resuming strategy {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to resume strategy: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{strategy_id}/performance-metrics")
|
||||
async def get_performance_metrics(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Get performance metrics for a strategy
|
||||
"""
|
||||
try:
|
||||
# For now, return mock data - in real implementation, this would query the database
|
||||
mock_metrics = {
|
||||
"traffic_growth_percentage": 15.7,
|
||||
"engagement_rate_percentage": 8.3,
|
||||
"conversion_rate_percentage": 2.1,
|
||||
"roi_ratio": 3.2,
|
||||
"strategy_adoption_rate": 85,
|
||||
"content_quality_score": 92,
|
||||
"competitive_position_rank": 3,
|
||||
"audience_growth_percentage": 12.5,
|
||||
"confidence_score": 88,
|
||||
"last_updated": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": mock_metrics,
|
||||
"message": "Performance metrics retrieved successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting performance metrics: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/{strategy_id}/trend-data")
|
||||
async def get_trend_data(
|
||||
strategy_id: int,
|
||||
time_range: str = Query("30d", description="Time range: 7d, 30d, 90d, 1y"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Get trend data for a strategy over time
|
||||
"""
|
||||
try:
|
||||
# Mock trend data - in real implementation, this would query the database
|
||||
mock_trend_data = [
|
||||
{"date": "2024-01-01", "traffic_growth": 5.2, "engagement_rate": 6.1, "conversion_rate": 1.8, "content_quality_score": 85, "strategy_adoption_rate": 70},
|
||||
{"date": "2024-01-08", "traffic_growth": 7.8, "engagement_rate": 7.2, "conversion_rate": 2.0, "content_quality_score": 87, "strategy_adoption_rate": 75},
|
||||
{"date": "2024-01-15", "traffic_growth": 9.1, "engagement_rate": 7.8, "conversion_rate": 2.1, "content_quality_score": 89, "strategy_adoption_rate": 78},
|
||||
{"date": "2024-01-22", "traffic_growth": 11.3, "engagement_rate": 8.1, "conversion_rate": 2.0, "content_quality_score": 90, "strategy_adoption_rate": 82},
|
||||
{"date": "2024-01-29", "traffic_growth": 12.7, "engagement_rate": 8.3, "conversion_rate": 2.1, "content_quality_score": 91, "strategy_adoption_rate": 85},
|
||||
{"date": "2024-02-05", "traffic_growth": 14.2, "engagement_rate": 8.5, "conversion_rate": 2.2, "content_quality_score": 92, "strategy_adoption_rate": 87},
|
||||
{"date": "2024-02-12", "traffic_growth": 15.7, "engagement_rate": 8.3, "conversion_rate": 2.1, "content_quality_score": 92, "strategy_adoption_rate": 85}
|
||||
]
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": mock_trend_data,
|
||||
"message": "Trend data retrieved successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting trend data: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/{strategy_id}/test-transparency")
|
||||
async def test_transparency_endpoint(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Simple test endpoint to check if transparency data endpoint works
|
||||
"""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
return {
|
||||
"success": False,
|
||||
"data": None,
|
||||
"message": f"Strategy with ID {strategy_id} not found"
|
||||
}
|
||||
|
||||
# Get monitoring plan
|
||||
monitoring_plan = db.query(StrategyMonitoringPlan).filter(
|
||||
StrategyMonitoringPlan.strategy_id == strategy_id
|
||||
).first()
|
||||
|
||||
# Get monitoring tasks count
|
||||
tasks_count = db.query(MonitoringTask).filter(
|
||||
MonitoringTask.strategy_id == strategy_id
|
||||
).count()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"strategy_id": strategy_id,
|
||||
"strategy_name": strategy.strategy_name if hasattr(strategy, 'strategy_name') else "Unknown",
|
||||
"monitoring_plan_exists": monitoring_plan is not None,
|
||||
"tasks_count": tasks_count
|
||||
},
|
||||
"message": "Test endpoint working"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in test endpoint: {str(e)}")
|
||||
return {
|
||||
"success": False,
|
||||
"data": None,
|
||||
"message": f"Error: {str(e)}"
|
||||
}
|
||||
|
||||
@router.get("/{strategy_id}/monitoring-tasks")
|
||||
async def get_monitoring_tasks(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Get all monitoring tasks for a strategy with their execution status
|
||||
"""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(status_code=404, detail="Strategy not found")
|
||||
|
||||
# Get monitoring tasks with execution logs
|
||||
tasks = db.query(MonitoringTask).filter(
|
||||
MonitoringTask.strategy_id == strategy_id
|
||||
).all()
|
||||
|
||||
tasks_data = []
|
||||
for task in tasks:
|
||||
# Get latest execution log
|
||||
latest_log = db.query(TaskExecutionLog).filter(
|
||||
TaskExecutionLog.task_id == task.id
|
||||
).order_by(desc(TaskExecutionLog.execution_date)).first()
|
||||
|
||||
task_data = {
|
||||
"id": task.id,
|
||||
"title": task.task_title,
|
||||
"description": task.task_description,
|
||||
"assignee": task.assignee,
|
||||
"frequency": task.frequency,
|
||||
"metric": task.metric,
|
||||
"measurementMethod": task.measurement_method,
|
||||
"successCriteria": task.success_criteria,
|
||||
"alertThreshold": task.alert_threshold,
|
||||
"actionableInsights": getattr(task, 'actionable_insights', None),
|
||||
"status": "active", # This would be determined by task execution status
|
||||
"lastExecuted": latest_log.execution_date.isoformat() if latest_log else None,
|
||||
"executionCount": db.query(TaskExecutionLog).filter(
|
||||
TaskExecutionLog.task_id == task.id
|
||||
).count()
|
||||
}
|
||||
tasks_data.append(task_data)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": tasks_data,
|
||||
"message": "Monitoring tasks retrieved successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error retrieving monitoring tasks: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
|
||||
@router.get("/{strategy_id}/data-freshness")
|
||||
async def get_data_freshness(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Get data freshness information for all metrics
|
||||
"""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(status_code=404, detail="Strategy not found")
|
||||
|
||||
# Get latest task execution logs
|
||||
latest_logs = db.query(TaskExecutionLog).join(MonitoringTask).filter(
|
||||
MonitoringTask.strategy_id == strategy_id
|
||||
).order_by(desc(TaskExecutionLog.execution_date)).limit(10).all()
|
||||
|
||||
# Get performance metrics
|
||||
performance_metrics = db.query(StrategyPerformanceMetrics).filter(
|
||||
StrategyPerformanceMetrics.strategy_id == strategy_id
|
||||
).order_by(desc(StrategyPerformanceMetrics.created_at)).first()
|
||||
|
||||
freshness_data = {
|
||||
"lastUpdated": latest_logs[0].execution_date.isoformat() if latest_logs else datetime.now().isoformat(),
|
||||
"updateFrequency": "Every 4 hours",
|
||||
"dataSource": "Multiple Analytics APIs + AI Analysis",
|
||||
"confidence": 90,
|
||||
"metrics": [
|
||||
{
|
||||
"name": "Traffic Growth",
|
||||
"lastUpdated": latest_logs[0].execution_date.isoformat() if latest_logs else datetime.now().isoformat(),
|
||||
"updateFrequency": "Every 4 hours",
|
||||
"dataSource": "Google Analytics + AI Analysis",
|
||||
"confidence": 92
|
||||
},
|
||||
{
|
||||
"name": "Engagement Rate",
|
||||
"lastUpdated": latest_logs[0].execution_date.isoformat() if latest_logs else datetime.now().isoformat(),
|
||||
"updateFrequency": "Every 2 hours",
|
||||
"dataSource": "Social Media Analytics + Website Analytics",
|
||||
"confidence": 88
|
||||
},
|
||||
{
|
||||
"name": "Conversion Rate",
|
||||
"lastUpdated": latest_logs[0].execution_date.isoformat() if latest_logs else datetime.now().isoformat(),
|
||||
"updateFrequency": "Every 6 hours",
|
||||
"dataSource": "Google Analytics + CRM Data",
|
||||
"confidence": 85
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": freshness_data,
|
||||
"message": "Data freshness information retrieved successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error retrieving data freshness: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
|
||||
@router.get("/{strategy_id}/transparency-data")
|
||||
async def get_transparency_data(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Get comprehensive transparency data for a strategy including:
|
||||
- Data freshness information
|
||||
- Measurement methodology
|
||||
- AI monitoring tasks
|
||||
- Strategy mapping
|
||||
- AI insights
|
||||
"""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
return {
|
||||
"success": False,
|
||||
"data": None,
|
||||
"message": f"Strategy with ID {strategy_id} not found"
|
||||
}
|
||||
|
||||
# Get monitoring plan and tasks
|
||||
monitoring_plan = db.query(StrategyMonitoringPlan).filter(
|
||||
StrategyMonitoringPlan.strategy_id == strategy_id
|
||||
).first()
|
||||
|
||||
if not monitoring_plan:
|
||||
return {
|
||||
"success": False,
|
||||
"data": None,
|
||||
"message": "No monitoring plan found for this strategy"
|
||||
}
|
||||
|
||||
# Get all monitoring tasks
|
||||
monitoring_tasks = db.query(MonitoringTask).filter(
|
||||
MonitoringTask.strategy_id == strategy_id
|
||||
).all()
|
||||
|
||||
# Get task execution logs for data freshness
|
||||
task_logs = db.query(TaskExecutionLog).join(MonitoringTask).filter(
|
||||
MonitoringTask.strategy_id == strategy_id
|
||||
).order_by(desc(TaskExecutionLog.execution_date)).all()
|
||||
|
||||
# Get performance metrics for current values
|
||||
performance_metrics = db.query(StrategyPerformanceMetrics).filter(
|
||||
StrategyPerformanceMetrics.strategy_id == strategy_id
|
||||
).order_by(desc(StrategyPerformanceMetrics.created_at)).first()
|
||||
|
||||
# Build transparency data
|
||||
transparency_data = []
|
||||
|
||||
# Traffic Growth Metric
|
||||
traffic_growth_data = {
|
||||
"metricName": "Traffic Growth",
|
||||
"currentValue": 15.7, # This would come from actual analytics
|
||||
"unit": "%",
|
||||
"dataFreshness": {
|
||||
"lastUpdated": task_logs[0].execution_date.isoformat() if task_logs else datetime.now().isoformat(),
|
||||
"updateFrequency": "Every 4 hours",
|
||||
"dataSource": "Google Analytics + AI Analysis",
|
||||
"confidence": 92
|
||||
},
|
||||
"measurementMethodology": {
|
||||
"description": "Organic traffic growth compared to previous period",
|
||||
"calculationMethod": "Percentage change in organic sessions over 30-day rolling period, weighted by content performance and user engagement",
|
||||
"dataPoints": ["Organic Sessions", "Page Views", "Bounce Rate", "Time on Site", "Content Performance"],
|
||||
"validationProcess": "Cross-validated with Google Search Console data and AI-powered content performance analysis"
|
||||
},
|
||||
"monitoringTasks": [],
|
||||
"strategyMapping": {
|
||||
"relatedComponents": ["Strategic Insights", "Content Strategy", "Audience Analysis"],
|
||||
"impactAreas": ["Brand Awareness", "Lead Generation", "Market Reach"],
|
||||
"dependencies": ["SEO Optimization", "Content Quality", "User Experience"]
|
||||
},
|
||||
"aiInsights": {
|
||||
"trendAnalysis": "Traffic growth shows strong upward trend with 15.7% increase. Top-performing content categories are educational blog posts and case studies.",
|
||||
"recommendations": [
|
||||
"Increase content production in educational blog category by 25%",
|
||||
"Optimize case study content for better search visibility",
|
||||
"Implement A/B testing for content headlines",
|
||||
"Focus on long-form content (2000+ words) which shows 40% higher engagement"
|
||||
],
|
||||
"riskFactors": ["Seasonal traffic fluctuations", "Competitor content strategy changes", "Algorithm updates"],
|
||||
"opportunities": ["Video content expansion", "Guest posting opportunities", "Social media amplification"]
|
||||
}
|
||||
}
|
||||
|
||||
# Add real monitoring tasks - map based on task content and purpose
|
||||
for task in monitoring_tasks:
|
||||
task_title_lower = task.task_title.lower()
|
||||
task_description_lower = task.task_description.lower()
|
||||
|
||||
# Traffic Growth related tasks
|
||||
if any(keyword in task_title_lower or keyword in task_description_lower
|
||||
for keyword in ['traffic', 'organic', 'goal', 'strategic', 'performance', 'prediction']):
|
||||
task_data = {
|
||||
"title": task.task_title,
|
||||
"description": task.task_description,
|
||||
"assignee": task.assignee,
|
||||
"frequency": task.frequency,
|
||||
"metric": task.metric,
|
||||
"measurementMethod": task.measurement_method,
|
||||
"successCriteria": task.success_criteria,
|
||||
"alertThreshold": task.alert_threshold,
|
||||
"actionableInsights": getattr(task, 'actionable_insights', None),
|
||||
"status": "active",
|
||||
"lastExecuted": task_logs[0].execution_date.isoformat() if task_logs else None
|
||||
}
|
||||
traffic_growth_data["monitoringTasks"].append(task_data)
|
||||
|
||||
transparency_data.append(traffic_growth_data)
|
||||
|
||||
# Engagement Rate Metric
|
||||
engagement_data = {
|
||||
"metricName": "Engagement Rate",
|
||||
"currentValue": 8.3,
|
||||
"unit": "%",
|
||||
"dataFreshness": {
|
||||
"lastUpdated": task_logs[0].execution_date.isoformat() if task_logs else datetime.now().isoformat(),
|
||||
"updateFrequency": "Every 2 hours",
|
||||
"dataSource": "Social Media Analytics + Website Analytics",
|
||||
"confidence": 88
|
||||
},
|
||||
"measurementMethodology": {
|
||||
"description": "Average engagement rate across all content and social media",
|
||||
"calculationMethod": "Weighted average of likes, shares, comments, and time spent across all platforms",
|
||||
"dataPoints": ["Social Media Engagement", "Website Comments", "Time on Page", "Social Shares", "Email Engagement"],
|
||||
"validationProcess": "Cross-platform validation using multiple analytics tools and AI sentiment analysis"
|
||||
},
|
||||
"monitoringTasks": [],
|
||||
"strategyMapping": {
|
||||
"relatedComponents": ["Audience Analysis", "Content Strategy", "Social Media Strategy"],
|
||||
"impactAreas": ["Brand Engagement", "Community Building", "Customer Loyalty"],
|
||||
"dependencies": ["Content Quality", "Social Media Presence", "Community Management"]
|
||||
},
|
||||
"aiInsights": {
|
||||
"trendAnalysis": "Engagement rate is stable at 8.3% with peak engagement during lunch hours and early evenings.",
|
||||
"recommendations": [
|
||||
"Increase video content production by 50%",
|
||||
"Optimize posting times for peak engagement hours",
|
||||
"Implement interactive content elements",
|
||||
"Focus on community-building content"
|
||||
],
|
||||
"riskFactors": ["Platform algorithm changes", "Content fatigue", "Competition for attention"],
|
||||
"opportunities": ["Live streaming opportunities", "User-generated content campaigns", "Influencer collaborations"]
|
||||
}
|
||||
}
|
||||
|
||||
# Add engagement-related tasks
|
||||
for task in monitoring_tasks:
|
||||
task_title_lower = task.task_title.lower()
|
||||
task_description_lower = task.task_description.lower()
|
||||
|
||||
if any(keyword in task_title_lower or keyword in task_description_lower
|
||||
for keyword in ['engagement', 'social', 'community', 'audience', 'insight', 'competitive']):
|
||||
task_data = {
|
||||
"title": task.task_title,
|
||||
"description": task.task_description,
|
||||
"assignee": task.assignee,
|
||||
"frequency": task.frequency,
|
||||
"metric": task.metric,
|
||||
"measurementMethod": task.measurement_method,
|
||||
"successCriteria": task.success_criteria,
|
||||
"alertThreshold": task.alert_threshold,
|
||||
"actionableInsights": getattr(task, 'actionable_insights', None),
|
||||
"status": "active",
|
||||
"lastExecuted": task_logs[0].execution_date.isoformat() if task_logs else None
|
||||
}
|
||||
engagement_data["monitoringTasks"].append(task_data)
|
||||
|
||||
transparency_data.append(engagement_data)
|
||||
|
||||
# Conversion Rate Metric
|
||||
conversion_data = {
|
||||
"metricName": "Conversion Rate",
|
||||
"currentValue": 2.1,
|
||||
"unit": "%",
|
||||
"dataFreshness": {
|
||||
"lastUpdated": task_logs[0].execution_date.isoformat() if task_logs else datetime.now().isoformat(),
|
||||
"updateFrequency": "Every 6 hours",
|
||||
"dataSource": "Google Analytics + CRM Data",
|
||||
"confidence": 85
|
||||
},
|
||||
"measurementMethodology": {
|
||||
"description": "Content-driven conversion rate across all touchpoints",
|
||||
"calculationMethod": "Conversions divided by total visitors, weighted by content attribution and customer journey analysis",
|
||||
"dataPoints": ["Website Conversions", "Email Signups", "Lead Form Submissions", "Content Downloads", "Sales Attribution"],
|
||||
"validationProcess": "CRM integration validation and conversion funnel analysis"
|
||||
},
|
||||
"monitoringTasks": [],
|
||||
"strategyMapping": {
|
||||
"relatedComponents": ["Performance Predictions", "Implementation Roadmap", "Risk Assessment"],
|
||||
"impactAreas": ["Revenue Generation", "Lead Quality", "Customer Acquisition"],
|
||||
"dependencies": ["Content Quality", "User Experience", "Lead Nurturing"]
|
||||
},
|
||||
"aiInsights": {
|
||||
"trendAnalysis": "Conversion rate is improving steadily with 2.1% current rate. Top-converting content includes case studies and product demos.",
|
||||
"recommendations": [
|
||||
"Increase case study and demo content production",
|
||||
"Optimize mobile user experience further",
|
||||
"Implement personalized content recommendations",
|
||||
"A/B test call-to-action buttons and forms"
|
||||
],
|
||||
"riskFactors": ["Market competition", "Economic factors", "Technology changes"],
|
||||
"opportunities": ["Personalization opportunities", "Automation implementation", "Cross-selling strategies"]
|
||||
}
|
||||
}
|
||||
|
||||
# Add conversion-related tasks
|
||||
for task in monitoring_tasks:
|
||||
task_title_lower = task.task_title.lower()
|
||||
task_description_lower = task.task_description.lower()
|
||||
|
||||
if any(keyword in task_title_lower or keyword in task_description_lower
|
||||
for keyword in ['conversion', 'funnel', 'implementation', 'resource', 'risk', 'mitigation']):
|
||||
task_data = {
|
||||
"title": task.task_title,
|
||||
"description": task.task_description,
|
||||
"assignee": task.assignee,
|
||||
"frequency": task.frequency,
|
||||
"metric": task.metric,
|
||||
"measurementMethod": task.measurement_method,
|
||||
"successCriteria": task.success_criteria,
|
||||
"alertThreshold": task.alert_threshold,
|
||||
"actionableInsights": getattr(task, 'actionable_insights', None),
|
||||
"status": "active",
|
||||
"lastExecuted": task_logs[0].execution_date.isoformat() if task_logs else None
|
||||
}
|
||||
conversion_data["monitoringTasks"].append(task_data)
|
||||
|
||||
transparency_data.append(conversion_data)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": transparency_data,
|
||||
"message": "Transparency data retrieved successfully"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error retrieving transparency data: {str(e)}")
|
||||
return {
|
||||
"success": False,
|
||||
"data": None,
|
||||
"message": f"Error: {str(e)}"
|
||||
}
|
||||
458
backend/api/content_planning/quality_analysis_routes.py
Normal file
458
backend/api/content_planning/quality_analysis_routes.py
Normal file
@@ -0,0 +1,458 @@
|
||||
"""
|
||||
Quality Analysis API Routes
|
||||
Provides endpoints for AI-powered quality assessment and recommendations.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||
from typing import Dict, Any, List
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from services.ai_quality_analysis_service import AIQualityAnalysisService, QualityAnalysisResult
|
||||
from services.database import get_db
|
||||
from models.enhanced_strategy_models import EnhancedContentStrategy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/quality-analysis", tags=["quality-analysis"])
|
||||
|
||||
@router.post("/{strategy_id}/analyze")
|
||||
async def analyze_strategy_quality(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Analyze strategy quality using AI and return comprehensive results."""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Initialize quality analysis service
|
||||
quality_service = AIQualityAnalysisService()
|
||||
|
||||
# Perform quality analysis
|
||||
analysis_result = await quality_service.analyze_strategy_quality(strategy_id)
|
||||
|
||||
# Convert result to dictionary for API response
|
||||
result_dict = {
|
||||
"strategy_id": analysis_result.strategy_id,
|
||||
"overall_score": analysis_result.overall_score,
|
||||
"overall_status": analysis_result.overall_status.value,
|
||||
"confidence_score": analysis_result.confidence_score,
|
||||
"analysis_timestamp": analysis_result.analysis_timestamp.isoformat(),
|
||||
"metrics": [
|
||||
{
|
||||
"name": metric.name,
|
||||
"score": metric.score,
|
||||
"weight": metric.weight,
|
||||
"status": metric.status.value,
|
||||
"description": metric.description,
|
||||
"recommendations": metric.recommendations
|
||||
}
|
||||
for metric in analysis_result.metrics
|
||||
],
|
||||
"recommendations": analysis_result.recommendations
|
||||
}
|
||||
|
||||
logger.info(f"Quality analysis completed for strategy {strategy_id}")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": result_dict,
|
||||
"message": "Quality analysis completed successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error analyzing strategy quality for {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to analyze strategy quality: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{strategy_id}/metrics")
|
||||
async def get_quality_metrics(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get quality metrics for a strategy."""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Initialize quality analysis service
|
||||
quality_service = AIQualityAnalysisService()
|
||||
|
||||
# Perform quick quality analysis (cached if available)
|
||||
analysis_result = await quality_service.analyze_strategy_quality(strategy_id)
|
||||
|
||||
# Return metrics in a simplified format
|
||||
metrics_data = [
|
||||
{
|
||||
"name": metric.name,
|
||||
"score": metric.score,
|
||||
"status": metric.status.value,
|
||||
"description": metric.description
|
||||
}
|
||||
for metric in analysis_result.metrics
|
||||
]
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"strategy_id": strategy_id,
|
||||
"overall_score": analysis_result.overall_score,
|
||||
"overall_status": analysis_result.overall_status.value,
|
||||
"metrics": metrics_data,
|
||||
"last_updated": analysis_result.analysis_timestamp.isoformat()
|
||||
},
|
||||
"message": "Quality metrics retrieved successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting quality metrics for {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get quality metrics: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{strategy_id}/recommendations")
|
||||
async def get_quality_recommendations(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get AI-powered quality improvement recommendations."""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Initialize quality analysis service
|
||||
quality_service = AIQualityAnalysisService()
|
||||
|
||||
# Perform quality analysis to get recommendations
|
||||
analysis_result = await quality_service.analyze_strategy_quality(strategy_id)
|
||||
|
||||
# Get recommendations by category
|
||||
recommendations_by_category = {}
|
||||
for metric in analysis_result.metrics:
|
||||
if metric.recommendations:
|
||||
recommendations_by_category[metric.name] = metric.recommendations
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"strategy_id": strategy_id,
|
||||
"overall_recommendations": analysis_result.recommendations,
|
||||
"recommendations_by_category": recommendations_by_category,
|
||||
"priority_areas": [
|
||||
metric.name for metric in analysis_result.metrics
|
||||
if metric.status.value in ["needs_attention", "poor"]
|
||||
],
|
||||
"last_updated": analysis_result.analysis_timestamp.isoformat()
|
||||
},
|
||||
"message": "Quality recommendations retrieved successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting quality recommendations for {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get quality recommendations: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{strategy_id}/history")
|
||||
async def get_quality_history(
|
||||
strategy_id: int,
|
||||
days: int = Query(30, description="Number of days to look back"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get quality analysis history for a strategy."""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Initialize quality analysis service
|
||||
quality_service = AIQualityAnalysisService()
|
||||
|
||||
# Get quality history
|
||||
history = await quality_service.get_quality_history(strategy_id, days)
|
||||
|
||||
# Convert history to API format
|
||||
history_data = [
|
||||
{
|
||||
"timestamp": result.analysis_timestamp.isoformat(),
|
||||
"overall_score": result.overall_score,
|
||||
"overall_status": result.overall_status.value,
|
||||
"confidence_score": result.confidence_score
|
||||
}
|
||||
for result in history
|
||||
]
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"strategy_id": strategy_id,
|
||||
"history": history_data,
|
||||
"days": days,
|
||||
"total_analyses": len(history_data)
|
||||
},
|
||||
"message": "Quality history retrieved successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting quality history for {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get quality history: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{strategy_id}/trends")
|
||||
async def get_quality_trends(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get quality trends and patterns for a strategy."""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Initialize quality analysis service
|
||||
quality_service = AIQualityAnalysisService()
|
||||
|
||||
# Get quality trends
|
||||
trends = await quality_service.get_quality_trends(strategy_id)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"strategy_id": strategy_id,
|
||||
"trends": trends,
|
||||
"last_updated": datetime.utcnow().isoformat()
|
||||
},
|
||||
"message": "Quality trends retrieved successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting quality trends for {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get quality trends: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/{strategy_id}/quick-assessment")
|
||||
async def quick_quality_assessment(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Perform a quick quality assessment without full AI analysis."""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Perform quick assessment based on data completeness
|
||||
completeness_score = self._calculate_completeness_score(strategy)
|
||||
|
||||
# Determine status based on score
|
||||
if completeness_score >= 80:
|
||||
status = "excellent"
|
||||
elif completeness_score >= 65:
|
||||
status = "good"
|
||||
elif completeness_score >= 45:
|
||||
status = "needs_attention"
|
||||
else:
|
||||
status = "poor"
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"strategy_id": strategy_id,
|
||||
"completeness_score": completeness_score,
|
||||
"status": status,
|
||||
"assessment_type": "quick",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"message": "Quick assessment completed based on data completeness"
|
||||
},
|
||||
"message": "Quick quality assessment completed"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error performing quick assessment for {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to perform quick assessment: {str(e)}"
|
||||
)
|
||||
|
||||
def _calculate_completeness_score(self, strategy: EnhancedContentStrategy) -> float:
|
||||
"""Calculate completeness score based on filled fields."""
|
||||
try:
|
||||
# Define required fields for each category
|
||||
required_fields = {
|
||||
"business_context": [
|
||||
'business_objectives', 'target_metrics', 'content_budget',
|
||||
'team_size', 'implementation_timeline', 'market_share'
|
||||
],
|
||||
"audience_intelligence": [
|
||||
'content_preferences', 'consumption_patterns', 'audience_pain_points',
|
||||
'buying_journey', 'seasonal_trends', 'engagement_metrics'
|
||||
],
|
||||
"competitive_intelligence": [
|
||||
'top_competitors', 'competitor_content_strategies', 'market_gaps',
|
||||
'industry_trends', 'emerging_trends'
|
||||
],
|
||||
"content_strategy": [
|
||||
'preferred_formats', 'content_mix', 'content_frequency',
|
||||
'optimal_timing', 'quality_metrics', 'editorial_guidelines', 'brand_voice'
|
||||
],
|
||||
"performance_analytics": [
|
||||
'traffic_sources', 'conversion_rates', 'content_roi_targets',
|
||||
'ab_testing_capabilities'
|
||||
]
|
||||
}
|
||||
|
||||
total_fields = 0
|
||||
filled_fields = 0
|
||||
|
||||
for category, fields in required_fields.items():
|
||||
total_fields += len(fields)
|
||||
for field in fields:
|
||||
if hasattr(strategy, field) and getattr(strategy, field) is not None:
|
||||
filled_fields += 1
|
||||
|
||||
if total_fields == 0:
|
||||
return 0.0
|
||||
|
||||
return (filled_fields / total_fields) * 100
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating completeness score: {e}")
|
||||
return 0.0
|
||||
|
||||
@router.get("/{strategy_id}/dashboard")
|
||||
async def get_quality_dashboard(
|
||||
strategy_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get comprehensive quality dashboard data."""
|
||||
try:
|
||||
# Check if strategy exists
|
||||
strategy = db.query(EnhancedContentStrategy).filter(
|
||||
EnhancedContentStrategy.id == strategy_id
|
||||
).first()
|
||||
|
||||
if not strategy:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Strategy with ID {strategy_id} not found"
|
||||
)
|
||||
|
||||
# Initialize quality analysis service
|
||||
quality_service = AIQualityAnalysisService()
|
||||
|
||||
# Get comprehensive analysis
|
||||
analysis_result = await quality_service.analyze_strategy_quality(strategy_id)
|
||||
|
||||
# Get trends
|
||||
trends = await quality_service.get_quality_trends(strategy_id)
|
||||
|
||||
# Prepare dashboard data
|
||||
dashboard_data = {
|
||||
"strategy_id": strategy_id,
|
||||
"overall_score": analysis_result.overall_score,
|
||||
"overall_status": analysis_result.overall_status.value,
|
||||
"confidence_score": analysis_result.confidence_score,
|
||||
"metrics": [
|
||||
{
|
||||
"name": metric.name,
|
||||
"score": metric.score,
|
||||
"status": metric.status.value,
|
||||
"description": metric.description,
|
||||
"recommendations": metric.recommendations
|
||||
}
|
||||
for metric in analysis_result.metrics
|
||||
],
|
||||
"recommendations": analysis_result.recommendations,
|
||||
"trends": trends,
|
||||
"priority_areas": [
|
||||
metric.name for metric in analysis_result.metrics
|
||||
if metric.status.value in ["needs_attention", "poor"]
|
||||
],
|
||||
"strengths": [
|
||||
metric.name for metric in analysis_result.metrics
|
||||
if metric.status.value == "excellent"
|
||||
],
|
||||
"last_updated": analysis_result.analysis_timestamp.isoformat()
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": dashboard_data,
|
||||
"message": "Quality dashboard data retrieved successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting quality dashboard for {strategy_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get quality dashboard: {str(e)}"
|
||||
)
|
||||
Reference in New Issue
Block a user