Add new alert architecture

This commit is contained in:
Urtzi Alfaro
2025-08-23 10:19:58 +02:00
parent 1a9839240e
commit 4b4268d640
45 changed files with 6518 additions and 1590 deletions

218
shared/alerts/templates.py Normal file
View File

@@ -0,0 +1,218 @@
# shared/alerts/templates.py
"""
Alert and recommendation templates in Spanish for the bakery platform
"""
from typing import Dict, Any
ITEM_TEMPLATES = {
# ALERTS - Critical Issues Requiring Immediate Action
'critical_stock_shortage': {
'es': {
'title': '🚨 Stock Crítico: {ingredient_name}',
'message': 'Solo {current_stock}kg disponibles, necesarios {required_stock}kg para producción de mañana. Acción inmediata requerida.',
'actions': ['Realizar pedido de emergencia', 'Contactar proveedor', 'Ajustar plan de producción']
},
'en': {
'title': '🚨 Critical Stock: {ingredient_name}',
'message': 'Only {current_stock}kg available, {required_stock}kg needed for tomorrow\'s production. Immediate action required.',
'actions': ['Place emergency order', 'Contact supplier', 'Adjust production plan']
}
},
'temperature_breach': {
'es': {
'title': '🌡️ ALERTA TEMPERATURA',
'message': '{location}: {temperature}°C durante {duration} minutos. Revisar productos inmediatamente para evitar deterioro.',
'actions': ['Verificar productos', 'Llamar técnico refrigeración', 'Documentar incidencia', 'Mover productos']
},
'en': {
'title': '🌡️ TEMPERATURE ALERT',
'message': '{location}: {temperature}°C for {duration} minutes. Check products immediately to prevent spoilage.',
'actions': ['Check products', 'Call refrigeration technician', 'Document incident', 'Move products']
}
},
'production_delay': {
'es': {
'title': '⏰ Retraso en Producción',
'message': 'Lote {batch_name} con {delay_minutes} minutos de retraso. Impacto en entregas del día.',
'actions': ['Acelerar producción', 'Notificar clientes', 'Reorganizar horarios', 'Buscar capacidad adicional']
}
},
'expired_products': {
'es': {
'title': '📅 Productos Caducados',
'message': '{product_count} productos han caducado hoy. Retirar inmediatamente por seguridad alimentaria.',
'actions': ['Retirar productos', 'Revisar inventario', 'Ajustar pedidos', 'Documentar pérdidas']
}
},
'equipment_failure': {
'es': {
'title': '⚙️ Fallo de Equipo',
'message': '{equipment_name} no está funcionando correctamente. Producción afectada.',
'actions': ['Parar producción', 'Llamar mantenimiento', 'Usar equipo alternativo', 'Documentar fallo']
}
},
'order_overload': {
'es': {
'title': '📋 Sobrecarga de Pedidos',
'message': 'Capacidad excedida en {percentage}%. Riesgo de no cumplir entregas.',
'actions': ['Priorizar pedidos', 'Aumentar turnos', 'Rechazar nuevos pedidos', 'Buscar ayuda externa']
}
},
'supplier_delay': {
'es': {
'title': '🚚 Retraso de Proveedor',
'message': 'Entrega de {supplier_name} retrasada {hours} horas. Impacto en producción de {products}.',
'actions': ['Contactar proveedor', 'Buscar alternativas', 'Ajustar producción', 'Usar stock reserva']
}
},
# RECOMMENDATIONS - Proactive Suggestions for Optimization
'inventory_optimization': {
'es': {
'title': '📈 Optimización de Stock: {ingredient_name}',
'message': 'Basado en tendencias de {period} días, sugerimos aumentar stock mínimo en {suggested_increase}kg para reducir costos.',
'actions': ['Revisar niveles mínimos', 'Analizar proveedores', 'Actualizar configuración', 'Programar pedido mayor']
},
'en': {
'title': '📈 Stock Optimization: {ingredient_name}',
'message': 'Based on {period} day trends, suggest increasing minimum stock by {suggested_increase}kg to reduce costs.',
'actions': ['Review minimum levels', 'Analyze suppliers', 'Update configuration', 'Schedule larger order']
}
},
'production_efficiency': {
'es': {
'title': '⚙️ Mejora de Eficiencia',
'message': 'Cambiar horarios de horneado a {suggested_time} puede reducir costos energéticos en {savings_percent}%.',
'actions': ['Revisar horarios', 'Consultar personal', 'Probar nuevo horario', 'Medir resultados']
}
},
'sales_opportunity': {
'es': {
'title': '💰 Oportunidad de Venta',
'message': '{product_name} tiene alta demanda los {days}. Incrementar producción puede aumentar ventas {increase_percent}%.',
'actions': ['Aumentar producción', 'Promocionar producto', 'Revisar precios', 'Planificar ingredientes']
}
},
'seasonal_adjustment': {
'es': {
'title': '🍂 Ajuste Estacional',
'message': 'Época de {season}: ajustar producción de {products} según patrones históricos.',
'actions': ['Revisar recetas estacionales', 'Ajustar inventario', 'Planificar promociones', 'Entrenar personal']
}
},
'cost_reduction': {
'es': {
'title': '💡 Reducción de Costos',
'message': 'Cambiar a proveedor {supplier_name} para {ingredient} puede ahorrar {savings_euros}€/mes.',
'actions': ['Evaluar calidad', 'Negociar precios', 'Probar muestras', 'Cambiar proveedor gradualmente']
}
},
'waste_reduction': {
'es': {
'title': '♻️ Reducción de Desperdicio',
'message': 'Ajustar tamaños de lote de {product} puede reducir desperdicio en {waste_reduction_percent}%.',
'actions': ['Analizar ventas', 'Ajustar recetas', 'Cambiar lotes', 'Monitorear resultados']
}
},
'quality_improvement': {
'es': {
'title': '⭐ Mejora de Calidad',
'message': 'Temperatura de horneado de {product} puede optimizarse para mejor textura y sabor.',
'actions': ['Probar temperaturas', 'Documentar cambios', 'Entrenar panaderos', 'Obtener feedback']
}
},
'customer_satisfaction': {
'es': {
'title': '😊 Satisfacción del Cliente',
'message': 'Clientes solicitan más {product} los {days}. Considerar aumentar disponibilidad.',
'actions': ['Revisar comentarios', 'Aumentar producción', 'Crear promociones', 'Mejorar exhibición']
}
},
'energy_optimization': {
'es': {
'title': '⚡ Optimización Energética',
'message': 'Consolidar horneado entre {start_time} y {end_time} puede reducir costos energéticos {savings_euros}€/día.',
'actions': ['Revisar horarios energía', 'Reorganizar producción', 'Optimizar hornos', 'Medir consumo']
}
},
'staff_optimization': {
'es': {
'title': '👥 Optimización de Personal',
'message': 'Picos de trabajo los {days} a las {hours}. Considerar ajustar turnos para mejor eficiencia.',
'actions': ['Analizar cargas trabajo', 'Reorganizar turnos', 'Entrenar polivalencia', 'Contratar temporal']
}
}
}
def format_item_message(template_key: str, language: str, **kwargs) -> Dict[str, Any]:
"""Format item message using template with validation"""
template = ITEM_TEMPLATES.get(template_key, {}).get(language, {})
if not template:
# Fallback for missing templates
return {
'title': f'Notificación: {template_key}',
'message': f'Información: {", ".join([f"{k}: {v}" for k, v in kwargs.items()])}',
'actions': ['Revisar', 'Documentar']
}
try:
# Format with provided kwargs, handling missing values gracefully
formatted_title = template['title'].format(**kwargs)
formatted_message = template['message'].format(**kwargs)
return {
'title': formatted_title,
'message': formatted_message,
'actions': template.get('actions', [])
}
except KeyError as e:
# Handle missing format parameters
return {
'title': template.get('title', f'Notificación: {template_key}'),
'message': f"Error en plantilla - parámetro faltante: {e}. Datos: {kwargs}",
'actions': template.get('actions', ['Revisar configuración'])
}
def get_severity_emoji(severity: str) -> str:
"""Get emoji for severity level"""
emoji_map = {
'urgent': '🚨',
'high': '⚠️',
'medium': '💡',
'low': ''
}
return emoji_map.get(severity, '📋')
def get_item_type_emoji(item_type: str) -> str:
"""Get emoji for item type"""
emoji_map = {
'alert': '🚨',
'recommendation': '💡'
}
return emoji_map.get(item_type, '📋')
def format_business_time(hour: int) -> str:
"""Format hour in Spanish business context"""
if hour == 0:
return "medianoche"
elif hour < 12:
return f"{hour}:00 AM"
elif hour == 12:
return "12:00 PM (mediodía)"
else:
return f"{hour-12}:00 PM"
def get_spanish_day_name(day_number: int) -> str:
"""Get Spanish day name (0=Monday)"""
days = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo"]
return days[day_number] if 0 <= day_number <= 6 else "día desconocido"
def format_currency(amount: float) -> str:
"""Format currency in Spanish Euro format"""
return f"{amount:.2f}"
def format_percentage(value: float) -> str:
"""Format percentage in Spanish format"""
return f"{value:.1f}%"