New alert system and panel de control page
This commit is contained in:
@@ -239,6 +239,166 @@ async def get_trends(
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get trends: {str(e)}")
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/tenants/{tenant_id}/alerts/analytics/dashboard",
|
||||
response_model=Dict[str, Any],
|
||||
summary="Get enriched alert analytics for dashboard"
|
||||
)
|
||||
async def get_dashboard_analytics(
|
||||
tenant_id: UUID = Path(..., description="Tenant ID"),
|
||||
days: int = Query(30, ge=1, le=90, description="Number of days to analyze"),
|
||||
current_user: dict = Depends(get_current_user_dep)
|
||||
):
|
||||
"""
|
||||
Get enriched alert analytics optimized for dashboard display.
|
||||
|
||||
Returns metrics based on the new enrichment system:
|
||||
- AI handling rate (% of prevented_issue alerts)
|
||||
- Priority distribution (critical, important, standard, info)
|
||||
- Type class breakdown (action_needed, prevented_issue, trend_warning, etc.)
|
||||
- Total financial impact at risk
|
||||
- Average response time by priority level
|
||||
- Prevented issues and estimated savings
|
||||
"""
|
||||
from app.config import AlertProcessorConfig
|
||||
from shared.database.base import create_database_manager
|
||||
from app.models.events import Alert, AlertStatus, AlertTypeClass, PriorityLevel
|
||||
from sqlalchemy import select, func, and_
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
try:
|
||||
config = AlertProcessorConfig()
|
||||
db_manager = create_database_manager(config.DATABASE_URL, "alert-processor")
|
||||
|
||||
async with db_manager.get_session() as session:
|
||||
cutoff_date = datetime.utcnow() - timedelta(days=days)
|
||||
|
||||
# Total alerts
|
||||
total_query = select(func.count(Alert.id)).where(
|
||||
and_(
|
||||
Alert.tenant_id == tenant_id,
|
||||
Alert.created_at >= cutoff_date
|
||||
)
|
||||
)
|
||||
total_result = await session.execute(total_query)
|
||||
total_alerts = total_result.scalar() or 0
|
||||
|
||||
# Priority distribution
|
||||
priority_query = select(
|
||||
Alert.priority_level,
|
||||
func.count(Alert.id).label('count')
|
||||
).where(
|
||||
and_(
|
||||
Alert.tenant_id == tenant_id,
|
||||
Alert.created_at >= cutoff_date
|
||||
)
|
||||
).group_by(Alert.priority_level)
|
||||
|
||||
priority_result = await session.execute(priority_query)
|
||||
priority_dist = {row.priority_level: row.count for row in priority_result}
|
||||
|
||||
# Type class distribution
|
||||
type_class_query = select(
|
||||
Alert.type_class,
|
||||
func.count(Alert.id).label('count')
|
||||
).where(
|
||||
and_(
|
||||
Alert.tenant_id == tenant_id,
|
||||
Alert.created_at >= cutoff_date
|
||||
)
|
||||
).group_by(Alert.type_class)
|
||||
|
||||
type_class_result = await session.execute(type_class_query)
|
||||
type_class_dist = {row.type_class: row.count for row in type_class_result}
|
||||
|
||||
# AI handling metrics
|
||||
prevented_count = type_class_dist.get(AlertTypeClass.PREVENTED_ISSUE, 0)
|
||||
ai_handling_percentage = (prevented_count / total_alerts * 100) if total_alerts > 0 else 0
|
||||
|
||||
# Financial impact - sum all business_impact.financial_impact_eur from active alerts
|
||||
active_alerts_query = select(Alert.id, Alert.business_impact).where(
|
||||
and_(
|
||||
Alert.tenant_id == tenant_id,
|
||||
Alert.status == AlertStatus.ACTIVE
|
||||
)
|
||||
)
|
||||
active_alerts_result = await session.execute(active_alerts_query)
|
||||
active_alerts = active_alerts_result.all()
|
||||
|
||||
total_financial_impact = sum(
|
||||
(alert.business_impact or {}).get('financial_impact_eur', 0)
|
||||
for alert in active_alerts
|
||||
)
|
||||
|
||||
# Prevented issues savings
|
||||
prevented_alerts_query = select(Alert.id, Alert.orchestrator_context).where(
|
||||
and_(
|
||||
Alert.tenant_id == tenant_id,
|
||||
Alert.type_class == 'prevented_issue',
|
||||
Alert.created_at >= cutoff_date
|
||||
)
|
||||
)
|
||||
prevented_alerts_result = await session.execute(prevented_alerts_query)
|
||||
prevented_alerts = prevented_alerts_result.all()
|
||||
|
||||
estimated_savings = sum(
|
||||
(alert.orchestrator_context or {}).get('estimated_savings_eur', 0)
|
||||
for alert in prevented_alerts
|
||||
)
|
||||
|
||||
# Active alerts by type class
|
||||
active_by_type_query = select(
|
||||
Alert.type_class,
|
||||
func.count(Alert.id).label('count')
|
||||
).where(
|
||||
and_(
|
||||
Alert.tenant_id == tenant_id,
|
||||
Alert.status == AlertStatus.ACTIVE
|
||||
)
|
||||
).group_by(Alert.type_class)
|
||||
|
||||
active_by_type_result = await session.execute(active_by_type_query)
|
||||
active_by_type = {row.type_class: row.count for row in active_by_type_result}
|
||||
|
||||
# Get period comparison for trends
|
||||
from app.repositories.analytics_repository import AlertAnalyticsRepository
|
||||
analytics_repo = AlertAnalyticsRepository(session)
|
||||
period_comparison = await analytics_repo.get_period_comparison(
|
||||
tenant_id=tenant_id,
|
||||
current_days=days,
|
||||
previous_days=days
|
||||
)
|
||||
|
||||
return {
|
||||
"period_days": days,
|
||||
"total_alerts": total_alerts,
|
||||
"active_alerts": len(active_alerts),
|
||||
"ai_handling_rate": round(ai_handling_percentage, 1),
|
||||
"prevented_issues_count": prevented_count,
|
||||
"estimated_savings_eur": round(estimated_savings, 2),
|
||||
"total_financial_impact_at_risk_eur": round(total_financial_impact, 2),
|
||||
"priority_distribution": {
|
||||
"critical": priority_dist.get(PriorityLevel.CRITICAL, 0),
|
||||
"important": priority_dist.get(PriorityLevel.IMPORTANT, 0),
|
||||
"standard": priority_dist.get(PriorityLevel.STANDARD, 0),
|
||||
"info": priority_dist.get(PriorityLevel.INFO, 0)
|
||||
},
|
||||
"type_class_distribution": {
|
||||
"action_needed": type_class_dist.get(AlertTypeClass.ACTION_NEEDED, 0),
|
||||
"prevented_issue": type_class_dist.get(AlertTypeClass.PREVENTED_ISSUE, 0),
|
||||
"trend_warning": type_class_dist.get(AlertTypeClass.TREND_WARNING, 0),
|
||||
"escalation": type_class_dist.get(AlertTypeClass.ESCALATION, 0),
|
||||
"information": type_class_dist.get(AlertTypeClass.INFORMATION, 0)
|
||||
},
|
||||
"active_by_type_class": active_by_type,
|
||||
"period_comparison": period_comparison
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get dashboard analytics", error=str(e), tenant_id=str(tenant_id))
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get dashboard analytics: {str(e)}")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Tenant Data Deletion Operations (Internal Service Only)
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user