139 lines
4.7 KiB
Python
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 {}
|