Files
bakery-ia/services/alert_processor/app/enrichment/urgency_analyzer.py
2025-12-05 20:07:01 +01:00

139 lines
4.7 KiB
Python

"""
Urgency analyzer for alerts.
Assesses time sensitivity, deadlines, and determines if action can wait.
"""
from typing import Dict, Any
from datetime import datetime, timedelta, timezone
import structlog
logger = structlog.get_logger()
class UrgencyAnalyzer:
"""Analyze urgency from event metadata"""
def analyze(self, event_type: str, metadata: Dict[str, Any]) -> dict:
"""
Analyze urgency for an event.
Returns dict with:
- hours_until_consequence: Time until impact occurs
- can_wait_until_tomorrow: Boolean
- deadline_utc: ISO datetime if deadline exists
- peak_hour_relevant: Boolean
- hours_pending: Age of alert
"""
urgency = {
"hours_until_consequence": 24, # Default: 24 hours
"can_wait_until_tomorrow": True,
"deadline_utc": None,
"peak_hour_relevant": False,
"hours_pending": 0
}
# Calculate based on event type
if "critical" in event_type or "urgent" in event_type:
urgency["hours_until_consequence"] = 2
urgency["can_wait_until_tomorrow"] = False
elif "production" in event_type:
urgency.update(self._analyze_production_urgency(metadata))
elif "stock" in event_type or "shortage" in event_type:
urgency.update(self._analyze_stock_urgency(metadata))
elif "delivery" in event_type or "overdue" in event_type:
urgency.update(self._analyze_delivery_urgency(metadata))
# Check for explicit deadlines
if "required_delivery_date" in metadata:
urgency.update(self._calculate_deadline_urgency(metadata["required_delivery_date"]))
if "production_date" in metadata:
urgency.update(self._calculate_deadline_urgency(metadata["production_date"]))
if "expected_date" in metadata:
urgency.update(self._calculate_deadline_urgency(metadata["expected_date"]))
return urgency
def _analyze_production_urgency(self, metadata: Dict[str, Any]) -> dict:
"""Analyze urgency for production alerts"""
urgency = {}
delay_minutes = metadata.get("delay_minutes", 0)
if delay_minutes > 120:
urgency["hours_until_consequence"] = 1
urgency["can_wait_until_tomorrow"] = False
elif delay_minutes > 60:
urgency["hours_until_consequence"] = 4
urgency["can_wait_until_tomorrow"] = False
else:
urgency["hours_until_consequence"] = 8
# Production is peak-hour sensitive
urgency["peak_hour_relevant"] = True
return urgency
def _analyze_stock_urgency(self, metadata: Dict[str, Any]) -> dict:
"""Analyze urgency for stock alerts"""
urgency = {}
# Hours until needed
if "hours_until" in metadata:
urgency["hours_until_consequence"] = metadata["hours_until"]
urgency["can_wait_until_tomorrow"] = urgency["hours_until_consequence"] > 24
# Days until expiry
elif "days_until_expiry" in metadata:
days = metadata["days_until_expiry"]
if days <= 1:
urgency["hours_until_consequence"] = days * 24
urgency["can_wait_until_tomorrow"] = False
else:
urgency["hours_until_consequence"] = days * 24
return urgency
def _analyze_delivery_urgency(self, metadata: Dict[str, Any]) -> dict:
"""Analyze urgency for delivery alerts"""
urgency = {}
days_overdue = metadata.get("days_overdue", 0)
if days_overdue > 3:
urgency["hours_until_consequence"] = 2
urgency["can_wait_until_tomorrow"] = False
elif days_overdue > 1:
urgency["hours_until_consequence"] = 8
urgency["can_wait_until_tomorrow"] = False
return urgency
def _calculate_deadline_urgency(self, deadline_str: str) -> dict:
"""Calculate urgency based on deadline"""
try:
if isinstance(deadline_str, str):
deadline = datetime.fromisoformat(deadline_str.replace('Z', '+00:00'))
else:
deadline = deadline_str
now = datetime.now(timezone.utc)
time_until = deadline - now
hours_until = time_until.total_seconds() / 3600
return {
"deadline_utc": deadline.isoformat(),
"hours_until_consequence": max(0, round(hours_until, 1)),
"can_wait_until_tomorrow": hours_until > 24
}
except Exception as e:
logger.warning("deadline_parse_failed", deadline=deadline_str, error=str(e))
return {}