Initial commit - production deployment
This commit is contained in:
156
services/alert_processor/app/enrichment/business_impact.py
Normal file
156
services/alert_processor/app/enrichment/business_impact.py
Normal file
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
Business impact analyzer for alerts.
|
||||
|
||||
Calculates financial impact, affected orders, customer impact, and other
|
||||
business metrics from event metadata.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
import structlog
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
class BusinessImpactAnalyzer:
|
||||
"""Analyze business impact from event metadata"""
|
||||
|
||||
def analyze(self, event_type: str, metadata: Dict[str, Any]) -> dict:
|
||||
"""
|
||||
Analyze business impact for an event.
|
||||
|
||||
Returns dict with:
|
||||
- financial_impact_eur: Direct financial cost
|
||||
- affected_orders: Number of orders impacted
|
||||
- affected_customers: List of customer names
|
||||
- production_delay_hours: Hours of production delay
|
||||
- estimated_revenue_loss_eur: Potential revenue loss
|
||||
- customer_impact: high/medium/low
|
||||
- waste_risk_kg: Potential waste in kg
|
||||
"""
|
||||
|
||||
impact = {
|
||||
"financial_impact_eur": 0,
|
||||
"affected_orders": 0,
|
||||
"affected_customers": [],
|
||||
"production_delay_hours": 0,
|
||||
"estimated_revenue_loss_eur": 0,
|
||||
"customer_impact": "low",
|
||||
"waste_risk_kg": 0
|
||||
}
|
||||
|
||||
# Stock-related impacts
|
||||
if "stock" in event_type or "shortage" in event_type:
|
||||
impact.update(self._analyze_stock_impact(metadata))
|
||||
|
||||
# Production-related impacts
|
||||
elif "production" in event_type or "delay" in event_type or "equipment" in event_type:
|
||||
impact.update(self._analyze_production_impact(metadata))
|
||||
|
||||
# Procurement-related impacts
|
||||
elif "po_" in event_type or "delivery" in event_type:
|
||||
impact.update(self._analyze_procurement_impact(metadata))
|
||||
|
||||
# Quality-related impacts
|
||||
elif "quality" in event_type or "expired" in event_type:
|
||||
impact.update(self._analyze_quality_impact(metadata))
|
||||
|
||||
return impact
|
||||
|
||||
def _analyze_stock_impact(self, metadata: Dict[str, Any]) -> dict:
|
||||
"""Analyze impact of stock-related alerts"""
|
||||
impact = {}
|
||||
|
||||
# Calculate financial impact
|
||||
shortage_amount = metadata.get("shortage_amount", 0)
|
||||
unit_cost = metadata.get("unit_cost", 5) # Default €5/kg
|
||||
impact["financial_impact_eur"] = float(shortage_amount) * unit_cost
|
||||
|
||||
# Affected orders from metadata
|
||||
impact["affected_orders"] = metadata.get("affected_orders", 0)
|
||||
|
||||
# Customer impact based on affected orders
|
||||
if impact["affected_orders"] > 5:
|
||||
impact["customer_impact"] = "high"
|
||||
elif impact["affected_orders"] > 2:
|
||||
impact["customer_impact"] = "medium"
|
||||
|
||||
# Revenue loss (estimated)
|
||||
avg_order_value = 50 # €50 per order
|
||||
impact["estimated_revenue_loss_eur"] = impact["affected_orders"] * avg_order_value
|
||||
|
||||
return impact
|
||||
|
||||
def _analyze_production_impact(self, metadata: Dict[str, Any]) -> dict:
|
||||
"""Analyze impact of production-related alerts"""
|
||||
impact = {}
|
||||
|
||||
# Delay minutes to hours
|
||||
delay_minutes = metadata.get("delay_minutes", 0)
|
||||
impact["production_delay_hours"] = round(delay_minutes / 60, 1)
|
||||
|
||||
# Affected orders and customers
|
||||
impact["affected_orders"] = metadata.get("affected_orders", 0)
|
||||
|
||||
customer_names = metadata.get("customer_names", [])
|
||||
impact["affected_customers"] = customer_names
|
||||
|
||||
# Customer impact based on delay
|
||||
if delay_minutes > 120: # 2+ hours
|
||||
impact["customer_impact"] = "high"
|
||||
elif delay_minutes > 60: # 1+ hours
|
||||
impact["customer_impact"] = "medium"
|
||||
|
||||
# Financial impact: hourly production cost
|
||||
hourly_cost = 100 # €100/hour operational cost
|
||||
impact["financial_impact_eur"] = impact["production_delay_hours"] * hourly_cost
|
||||
|
||||
# Revenue loss
|
||||
if impact["affected_orders"] > 0:
|
||||
avg_order_value = 50
|
||||
impact["estimated_revenue_loss_eur"] = impact["affected_orders"] * avg_order_value
|
||||
|
||||
return impact
|
||||
|
||||
def _analyze_procurement_impact(self, metadata: Dict[str, Any]) -> dict:
|
||||
"""Analyze impact of procurement-related alerts"""
|
||||
impact = {}
|
||||
|
||||
# Extract potential_loss_eur from reasoning_data.parameters
|
||||
reasoning_data = metadata.get("reasoning_data", {})
|
||||
parameters = reasoning_data.get("parameters", {})
|
||||
potential_loss_eur = parameters.get("potential_loss_eur")
|
||||
|
||||
# Use potential loss from reasoning as financial impact (what's at risk)
|
||||
# Fallback to PO amount only if reasoning data is not available
|
||||
if potential_loss_eur is not None:
|
||||
impact["financial_impact_eur"] = float(potential_loss_eur)
|
||||
else:
|
||||
po_amount = metadata.get("po_amount", metadata.get("total_amount", 0))
|
||||
impact["financial_impact_eur"] = float(po_amount)
|
||||
|
||||
# Days overdue affects customer impact
|
||||
days_overdue = metadata.get("days_overdue", 0)
|
||||
if days_overdue > 3:
|
||||
impact["customer_impact"] = "high"
|
||||
elif days_overdue > 1:
|
||||
impact["customer_impact"] = "medium"
|
||||
|
||||
return impact
|
||||
|
||||
def _analyze_quality_impact(self, metadata: Dict[str, Any]) -> dict:
|
||||
"""Analyze impact of quality-related alerts"""
|
||||
impact = {}
|
||||
|
||||
# Expired products
|
||||
expired_count = metadata.get("expired_count", 0)
|
||||
total_value = metadata.get("total_value", 0)
|
||||
|
||||
impact["financial_impact_eur"] = float(total_value)
|
||||
impact["waste_risk_kg"] = metadata.get("total_quantity_kg", 0)
|
||||
|
||||
if expired_count > 5:
|
||||
impact["customer_impact"] = "high"
|
||||
elif expired_count > 2:
|
||||
impact["customer_impact"] = "medium"
|
||||
|
||||
return impact
|
||||
Reference in New Issue
Block a user