285 lines
8.5 KiB
Python
285 lines
8.5 KiB
Python
|
|
# ================================================================
|
||
|
|
# shared/notifications/alert_integration.py
|
||
|
|
# ================================================================
|
||
|
|
"""
|
||
|
|
Simplified Alert Integration - Placeholder for unified alert system
|
||
|
|
"""
|
||
|
|
|
||
|
|
import structlog
|
||
|
|
from typing import Optional, Dict, Any, List
|
||
|
|
from datetime import datetime
|
||
|
|
import enum
|
||
|
|
from uuid import UUID
|
||
|
|
|
||
|
|
logger = structlog.get_logger()
|
||
|
|
|
||
|
|
|
||
|
|
class AlertSeverity(enum.Enum):
|
||
|
|
"""Alert severity levels"""
|
||
|
|
LOW = "low"
|
||
|
|
MEDIUM = "medium"
|
||
|
|
HIGH = "high"
|
||
|
|
CRITICAL = "critical"
|
||
|
|
|
||
|
|
|
||
|
|
class AlertType(enum.Enum):
|
||
|
|
"""Alert types for different bakery operations"""
|
||
|
|
# Production Alerts
|
||
|
|
PRODUCTION_DELAY = "production_delay"
|
||
|
|
BATCH_FAILURE = "batch_failure"
|
||
|
|
EQUIPMENT_MALFUNCTION = "equipment_malfunction"
|
||
|
|
TEMPERATURE_VIOLATION = "temperature_violation"
|
||
|
|
QUALITY_ISSUE = "quality_issue"
|
||
|
|
|
||
|
|
# Inventory Alerts
|
||
|
|
LOW_STOCK = "low_stock"
|
||
|
|
OUT_OF_STOCK = "out_of_stock"
|
||
|
|
EXPIRATION_WARNING = "expiration_warning"
|
||
|
|
TEMPERATURE_BREACH = "temperature_breach"
|
||
|
|
FOOD_SAFETY_VIOLATION = "food_safety_violation"
|
||
|
|
|
||
|
|
# Supplier Alerts
|
||
|
|
SUPPLIER_PERFORMANCE = "supplier_performance"
|
||
|
|
DELIVERY_DELAY = "delivery_delay"
|
||
|
|
QUALITY_ISSUES = "quality_issues"
|
||
|
|
CONTRACT_EXPIRY = "contract_expiry"
|
||
|
|
|
||
|
|
# Order Alerts
|
||
|
|
ORDER_DELAY = "order_delay"
|
||
|
|
CUSTOMER_COMPLAINT = "customer_complaint"
|
||
|
|
PAYMENT_ISSUE = "payment_issue"
|
||
|
|
|
||
|
|
|
||
|
|
class AlertSource(enum.Enum):
|
||
|
|
"""Sources that can generate alerts"""
|
||
|
|
PRODUCTION_SERVICE = "production_service"
|
||
|
|
INVENTORY_SERVICE = "inventory_service"
|
||
|
|
SUPPLIERS_SERVICE = "suppliers_service"
|
||
|
|
ORDERS_SERVICE = "orders_service"
|
||
|
|
EXTERNAL_SERVICE = "external_service"
|
||
|
|
|
||
|
|
|
||
|
|
class AlertCategory(enum.Enum):
|
||
|
|
"""Alert categories for organization"""
|
||
|
|
OPERATIONAL = "operational"
|
||
|
|
QUALITY = "quality"
|
||
|
|
SAFETY = "safety"
|
||
|
|
FINANCIAL = "financial"
|
||
|
|
COMPLIANCE = "compliance"
|
||
|
|
|
||
|
|
|
||
|
|
class AlertIntegration:
|
||
|
|
"""
|
||
|
|
Simplified alert integration that logs alerts.
|
||
|
|
TODO: Implement proper service-to-service communication for notifications
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.logger = structlog.get_logger("alert_integration")
|
||
|
|
|
||
|
|
async def create_alert(
|
||
|
|
self,
|
||
|
|
tenant_id: UUID,
|
||
|
|
alert_type: AlertType,
|
||
|
|
severity: AlertSeverity,
|
||
|
|
title: str,
|
||
|
|
message: str,
|
||
|
|
source: AlertSource,
|
||
|
|
category: AlertCategory = None,
|
||
|
|
entity_id: Optional[UUID] = None,
|
||
|
|
metadata: Optional[Dict[str, Any]] = None,
|
||
|
|
recipients: Optional[List[UUID]] = None
|
||
|
|
) -> Optional[str]:
|
||
|
|
"""
|
||
|
|
Create a new alert (currently just logs it)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Alert ID if successful, None otherwise
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
alert_data = {
|
||
|
|
"tenant_id": str(tenant_id),
|
||
|
|
"alert_type": alert_type.value,
|
||
|
|
"severity": severity.value,
|
||
|
|
"title": title,
|
||
|
|
"message": message,
|
||
|
|
"source": source.value,
|
||
|
|
"category": category.value if category else None,
|
||
|
|
"entity_id": str(entity_id) if entity_id else None,
|
||
|
|
"metadata": metadata or {},
|
||
|
|
"recipients": [str(r) for r in recipients] if recipients else [],
|
||
|
|
"timestamp": datetime.utcnow().isoformat()
|
||
|
|
}
|
||
|
|
|
||
|
|
# For now, just log the alert
|
||
|
|
self.logger.info(
|
||
|
|
"Alert created",
|
||
|
|
**alert_data
|
||
|
|
)
|
||
|
|
|
||
|
|
# Return a mock alert ID
|
||
|
|
return f"alert_{datetime.utcnow().timestamp()}"
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(
|
||
|
|
"Failed to create alert",
|
||
|
|
tenant_id=str(tenant_id),
|
||
|
|
alert_type=alert_type.value,
|
||
|
|
error=str(e)
|
||
|
|
)
|
||
|
|
return None
|
||
|
|
|
||
|
|
async def acknowledge_alert(self, alert_id: str, user_id: UUID) -> bool:
|
||
|
|
"""Acknowledge an alert (currently just logs it)"""
|
||
|
|
try:
|
||
|
|
self.logger.info(
|
||
|
|
"Alert acknowledged",
|
||
|
|
alert_id=alert_id,
|
||
|
|
user_id=str(user_id),
|
||
|
|
timestamp=datetime.utcnow().isoformat()
|
||
|
|
)
|
||
|
|
return True
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(
|
||
|
|
"Failed to acknowledge alert",
|
||
|
|
alert_id=alert_id,
|
||
|
|
error=str(e)
|
||
|
|
)
|
||
|
|
return False
|
||
|
|
|
||
|
|
async def resolve_alert(self, alert_id: str, user_id: UUID, resolution: str = None) -> bool:
|
||
|
|
"""Resolve an alert (currently just logs it)"""
|
||
|
|
try:
|
||
|
|
self.logger.info(
|
||
|
|
"Alert resolved",
|
||
|
|
alert_id=alert_id,
|
||
|
|
user_id=str(user_id),
|
||
|
|
resolution=resolution,
|
||
|
|
timestamp=datetime.utcnow().isoformat()
|
||
|
|
)
|
||
|
|
return True
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(
|
||
|
|
"Failed to resolve alert",
|
||
|
|
alert_id=alert_id,
|
||
|
|
error=str(e)
|
||
|
|
)
|
||
|
|
return False
|
||
|
|
|
||
|
|
# Convenience methods for specific alert types
|
||
|
|
async def create_inventory_alert(
|
||
|
|
self,
|
||
|
|
tenant_id: UUID,
|
||
|
|
alert_type: AlertType,
|
||
|
|
severity: AlertSeverity,
|
||
|
|
title: str,
|
||
|
|
message: str,
|
||
|
|
item_id: UUID = None,
|
||
|
|
**kwargs
|
||
|
|
) -> Optional[str]:
|
||
|
|
"""Create an inventory-specific alert"""
|
||
|
|
metadata = kwargs.pop('metadata', {})
|
||
|
|
if item_id:
|
||
|
|
metadata['item_id'] = str(item_id)
|
||
|
|
|
||
|
|
return await self.create_alert(
|
||
|
|
tenant_id=tenant_id,
|
||
|
|
alert_type=alert_type,
|
||
|
|
severity=severity,
|
||
|
|
title=title,
|
||
|
|
message=message,
|
||
|
|
source=AlertSource.INVENTORY_SERVICE,
|
||
|
|
category=AlertCategory.OPERATIONAL,
|
||
|
|
entity_id=item_id,
|
||
|
|
metadata=metadata,
|
||
|
|
**kwargs
|
||
|
|
)
|
||
|
|
|
||
|
|
async def create_production_alert(
|
||
|
|
self,
|
||
|
|
tenant_id: UUID,
|
||
|
|
alert_type: AlertType,
|
||
|
|
severity: AlertSeverity,
|
||
|
|
title: str,
|
||
|
|
message: str,
|
||
|
|
batch_id: UUID = None,
|
||
|
|
equipment_id: UUID = None,
|
||
|
|
**kwargs
|
||
|
|
) -> Optional[str]:
|
||
|
|
"""Create a production-specific alert"""
|
||
|
|
metadata = kwargs.pop('metadata', {})
|
||
|
|
if batch_id:
|
||
|
|
metadata['batch_id'] = str(batch_id)
|
||
|
|
if equipment_id:
|
||
|
|
metadata['equipment_id'] = str(equipment_id)
|
||
|
|
|
||
|
|
return await self.create_alert(
|
||
|
|
tenant_id=tenant_id,
|
||
|
|
alert_type=alert_type,
|
||
|
|
severity=severity,
|
||
|
|
title=title,
|
||
|
|
message=message,
|
||
|
|
source=AlertSource.PRODUCTION_SERVICE,
|
||
|
|
category=AlertCategory.OPERATIONAL,
|
||
|
|
metadata=metadata,
|
||
|
|
**kwargs
|
||
|
|
)
|
||
|
|
|
||
|
|
async def create_supplier_alert(
|
||
|
|
self,
|
||
|
|
tenant_id: UUID,
|
||
|
|
alert_type: AlertType,
|
||
|
|
severity: AlertSeverity,
|
||
|
|
title: str,
|
||
|
|
message: str,
|
||
|
|
supplier_id: UUID = None,
|
||
|
|
**kwargs
|
||
|
|
) -> Optional[str]:
|
||
|
|
"""Create a supplier-specific alert"""
|
||
|
|
metadata = kwargs.pop('metadata', {})
|
||
|
|
if supplier_id:
|
||
|
|
metadata['supplier_id'] = str(supplier_id)
|
||
|
|
|
||
|
|
return await self.create_alert(
|
||
|
|
tenant_id=tenant_id,
|
||
|
|
alert_type=alert_type,
|
||
|
|
severity=severity,
|
||
|
|
title=title,
|
||
|
|
message=message,
|
||
|
|
source=AlertSource.SUPPLIERS_SERVICE,
|
||
|
|
category=AlertCategory.QUALITY,
|
||
|
|
entity_id=supplier_id,
|
||
|
|
metadata=metadata,
|
||
|
|
**kwargs
|
||
|
|
)
|
||
|
|
|
||
|
|
async def create_order_alert(
|
||
|
|
self,
|
||
|
|
tenant_id: UUID,
|
||
|
|
alert_type: AlertType,
|
||
|
|
severity: AlertSeverity,
|
||
|
|
title: str,
|
||
|
|
message: str,
|
||
|
|
order_id: UUID = None,
|
||
|
|
customer_id: UUID = None,
|
||
|
|
**kwargs
|
||
|
|
) -> Optional[str]:
|
||
|
|
"""Create an order-specific alert"""
|
||
|
|
metadata = kwargs.pop('metadata', {})
|
||
|
|
if order_id:
|
||
|
|
metadata['order_id'] = str(order_id)
|
||
|
|
if customer_id:
|
||
|
|
metadata['customer_id'] = str(customer_id)
|
||
|
|
|
||
|
|
return await self.create_alert(
|
||
|
|
tenant_id=tenant_id,
|
||
|
|
alert_type=alert_type,
|
||
|
|
severity=severity,
|
||
|
|
title=title,
|
||
|
|
message=message,
|
||
|
|
source=AlertSource.ORDERS_SERVICE,
|
||
|
|
category=AlertCategory.OPERATIONAL,
|
||
|
|
entity_id=order_id,
|
||
|
|
metadata=metadata,
|
||
|
|
**kwargs
|
||
|
|
)
|