#!/usr/bin/env python3 """ Seed demo data for Unified Alert Service This script creates demo alerts that showcase the enrichment capabilities: - Low stock (AI already handled - prevented issue) - Supplier delay (action needed - critical) - Waste trend (trend warning - standard) - Orchestrator actions (for context enrichment) The unified alert-processor service automatically enriches all alerts with: - Multi-factor priority scoring - Orchestrator context (AI actions) - Business impact analysis - Smart actions with deep links - Timing intelligence Usage: python services/demo_session/scripts/seed_enriched_alert_demo.py """ import asyncio import os import sys import uuid from datetime import datetime, timedelta from pathlib import Path # Add project root to path sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent)) from shared.messaging import RabbitMQClient import structlog logger = structlog.get_logger() # Demo tenant ID (should match existing demo tenant) DEMO_TENANT_ID = "demo-tenant-bakery-ia" # Demo entity IDs (should match existing demo data) FLOUR_INGREDIENT_ID = "flour-tipo-55" YEAST_INGREDIENT_ID = "yeast-fresh" CROISSANT_PRODUCT_ID = "croissant-mantequilla" CROISSANT_BATCH_ID = "batch-croissants-001" YEAST_SUPPLIER_ID = "supplier-levadura-fresh" FLOUR_PO_ID = "po-flour-demo-001" # Demo alerts for unified alert-processor (automatically enriched) DEMO_ALERTS = [ # Alert 1: Low Stock - AI Already Handled (Prevented Issue) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'item_type': 'alert', 'service': 'inventory', 'type': 'low_stock', 'severity': 'warning', # Will be enriched with priority score ~71 'title': 'Bajo Stock: Harina Tipo 55', 'message': 'Stock: 45kg, Mínimo: 200kg', 'actions': [], # Will be enhanced with smart actions 'metadata': { 'ingredient_id': FLOUR_INGREDIENT_ID, 'ingredient_name': 'Harina Tipo 55', 'current_stock': 45, 'minimum_stock': 200, 'unit': 'kg', 'supplier_name': 'Harinera San José', 'last_order_date': (datetime.utcnow() - timedelta(days=7)).isoformat(), 'i18n': { 'title_key': 'alerts.low_stock_warning.title', 'message_key': 'alerts.low_stock_warning.message_generic', 'title_params': {'ingredient_name': 'Harina Tipo 55'}, 'message_params': { 'ingredient_name': 'Harina Tipo 55', 'current_stock': 45, 'minimum_stock': 200 } } }, 'timestamp': datetime.utcnow().isoformat() }, # Alert 2: Supplier Delay - Action Needed (Critical) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'item_type': 'alert', 'service': 'procurement', 'type': 'supplier_delay', 'severity': 'critical', # Will be enriched with priority score ~92 'actions': [], # Will be enhanced with smart actions 'title': 'Retraso de Proveedor: Levadura Fresh', 'message': 'Entrega retrasada 24 horas. Levadura necesaria para producción de mañana.', 'metadata': { 'supplier_id': YEAST_SUPPLIER_ID, 'supplier_name': 'Levadura Fresh', 'supplier_phone': '+34-555-1234', 'supplier_email': 'pedidos@levadura-fresh.es', 'ingredient_id': YEAST_INGREDIENT_ID, 'ingredient_name': 'Levadura Fresca', 'batch_id': CROISSANT_BATCH_ID, 'batch_name': 'Croissants Mantequilla Mañana', 'orders_affected': 3, 'financial_impact_eur': 450, 'deadline': (datetime.utcnow() + timedelta(hours=6)).isoformat(), 'quantity_needed': 15, 'unit': 'kg', 'i18n': { 'title_key': 'alerts.supplier_delay.title', 'message_key': 'alerts.supplier_delay.message', 'title_params': {'supplier_name': 'Levadura Fresh'}, 'message_params': { 'supplier_name': 'Levadura Fresh', 'ingredient_name': 'Levadura Fresca', 'po_id': 'PO-DEMO-123', 'new_delivery_date': (datetime.utcnow() + timedelta(hours=24)).strftime('%Y-%m-%d'), 'original_delivery_date': (datetime.utcnow() - timedelta(hours=24)).strftime('%Y-%m-%d') } } }, 'timestamp': datetime.utcnow().isoformat() }, # Alert 3: Waste Trend - Trend Warning (Standard) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'item_type': 'alert', 'service': 'production', 'type': 'waste_trend', 'severity': 'medium', # Will be enriched with priority score ~58 'actions': [], # Will be enhanced with smart actions 'title': 'Tendencia de Desperdicio: Croissants', 'message': 'Desperdicio aumentó 15% en 3 días. Patrón detectado: sobreproducción miércoles.', 'metadata': { 'product_id': CROISSANT_PRODUCT_ID, 'product_name': 'Croissant Mantequilla', 'waste_percentage': 23, 'baseline_percentage': 8, 'trend_days': 3, 'pattern': 'wednesday_overproduction', 'pattern_description': 'Desperdicio consistentemente alto los miércoles (18%, 21%, 23%)', 'financial_impact_eur': 180, 'recommendation': 'Reducir producción miércoles en 25 unidades', 'confidence': 0.85, 'historical_data': [ {'day': 'Mon', 'waste_pct': 7}, {'day': 'Tue', 'waste_pct': 9}, {'day': 'Wed', 'waste_pct': 23}, {'day': 'Thu', 'waste_pct': 8}, {'day': 'Fri', 'waste_pct': 6} ], 'i18n': { 'title_key': 'alerts.waste_trend.title', 'message_key': 'alerts.waste_trend.message', 'title_params': {'product_name': 'Croissant Mantequilla'}, 'message_params': { 'product_name': 'Croissant Mantequilla', 'spike_percent': 15, 'trend_days': 3, 'pattern': 'wednesday_overproduction' } } }, 'timestamp': datetime.utcnow().isoformat() }, # Alert 4: Forecast Anomaly - Information (Low Priority) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'item_type': 'alert', 'service': 'forecasting', 'type': 'forecast_updated', 'severity': 'low', # Will be enriched with priority score ~35 'actions': [], # Will be enhanced with smart actions 'title': 'Previsión Actualizada: Fin de Semana Soleado', 'message': 'Pronóstico meteorológico actualizado: soleado sábado y domingo. Aumento de demanda esperado.', 'metadata': { 'forecast_type': 'weather_based', 'weather_condition': 'sunny', 'days_affected': ['2024-11-23', '2024-11-24'], 'expected_demand_increase_pct': 15, 'confidence': 0.78, 'recommended_action': 'Aumentar producción croissants y pan rústico 15%', 'i18n': { 'title_key': 'alerts.demand_surge_weekend.title', 'message_key': 'alerts.demand_surge_weekend.message', 'title_params': {'weekend_date': (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%d')}, 'message_params': { 'surge_percent': 15, 'date': (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%d'), 'products': ['croissants', 'pan rustico'] } } }, 'timestamp': datetime.utcnow().isoformat() }, # Alert 5: Equipment Maintenance - Action Needed (Medium) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'item_type': 'alert', 'service': 'production', 'type': 'equipment_maintenance', 'severity': 'medium', # Will be enriched with priority score ~65 'actions': [], # Will be enhanced with smart actions 'title': 'Mantenimiento Programado: Horno Industrial', 'message': 'Horno principal requiere mantenimiento en 48 horas según calendario.', 'metadata': { 'equipment_id': 'oven-001', 'equipment_name': 'Horno Industrial Principal', 'equipment_type': 'oven', 'maintenance_type': 'preventive', 'scheduled_date': (datetime.utcnow() + timedelta(hours=48)).isoformat(), 'estimated_duration_hours': 3, 'last_maintenance': (datetime.utcnow() - timedelta(days=90)).isoformat(), 'maintenance_interval_days': 90, 'supplier_contact': 'TecnoHornos Madrid', 'supplier_phone': '+34-555-6789', 'i18n': { 'title_key': 'alerts.maintenance_required.title', 'message_key': 'alerts.maintenance_required.message_with_hours', 'title_params': {'equipment_name': 'Horno Industrial Principal'}, 'message_params': { 'equipment_name': 'Horno Industrial Principal', 'hours_until': 48, 'maintenance_date': (datetime.utcnow() + timedelta(hours=48)).strftime('%Y-%m-%d') } } }, 'timestamp': datetime.utcnow().isoformat() } ] # Demo orchestrator actions (for context enrichment) DEMO_ORCHESTRATOR_ACTIONS = [ # Action 1: PO Created for Flour (provides context for Alert 1) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'action_type': 'purchase_order_created', 'reasoning_type': 'preventive', 'entity_type': 'purchase_order', 'entity_id': FLOUR_PO_ID, 'summary': 'Pedido de compra #12345 creado para 500kg harina', 'reasoning_summary': 'Detecté que el stock se agotará en 2.3 días basándome en patrones históricos de demanda. Creé pedido de compra para llegar el viernes.', 'metadata': { 'ingredient_id': FLOUR_INGREDIENT_ID, 'ingredient_name': 'Harina Tipo 55', 'quantity': 500, 'unit': 'kg', 'supplier_name': 'Harinera San José', 'po_number': 'PO-12345', 'estimated_delivery': (datetime.utcnow() + timedelta(days=2, hours=10)).isoformat(), 'estimated_savings_eur': 200, 'prevented_issue': 'stockout', 'confidence': 0.92, 'demand_forecast_method': 'historical_patterns', 'stock_runout_days': 2.3 }, 'created_at': (datetime.utcnow() - timedelta(hours=2)).isoformat() }, # Action 2: Batch Scheduled for Weekend (weather-based) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'action_type': 'batch_scheduled', 'reasoning_type': 'weather_based', 'entity_type': 'production_batch', 'entity_id': CROISSANT_BATCH_ID, 'summary': 'Aumentada producción croissants 20% para sábado', 'reasoning_summary': 'Pronóstico meteorológico muestra fin de semana soleado. Datos históricos muestran aumento de demanda 15% en sábados soleados.', 'metadata': { 'product_id': CROISSANT_PRODUCT_ID, 'product_name': 'Croissant Mantequilla', 'quantity_increase': 25, 'quantity_increase_pct': 20, 'date': (datetime.utcnow() + timedelta(days=2)).date().isoformat(), 'weather_condition': 'sunny', 'confidence': 0.85, 'historical_sunny_saturday_demand_increase': 15, 'estimated_additional_revenue_eur': 180 }, 'created_at': (datetime.utcnow() - timedelta(hours=4)).isoformat() }, # Action 3: Waste Reduction Adjustment (for trend context) { 'id': str(uuid.uuid4()), 'tenant_id': DEMO_TENANT_ID, 'action_type': 'production_adjusted', 'reasoning_type': 'waste_reduction', 'entity_type': 'production_plan', 'entity_id': str(uuid.uuid4()), 'summary': 'Reducida producción lunes 10% basado en patrón post-fin de semana', 'reasoning_summary': 'Detecté patrón de baja demanda post-fin de semana. Histórico muestra reducción 12% demanda lunes. Ajusté producción para prevenir desperdicio.', 'metadata': { 'day_of_week': 'monday', 'reduction_pct': 10, 'historical_demand_drop_pct': 12, 'products_affected': ['Croissant Mantequilla', 'Pan Rústico'], 'estimated_waste_prevented_eur': 85, 'confidence': 0.78 }, 'created_at': (datetime.utcnow() - timedelta(hours=20)).isoformat() } ] async def seed_enriched_alert_demo(): """ Seed demo data for unified alert service This creates alerts that will be automatically enriched by alert-processor with: - Multi-factor priority scoring - Orchestrator context (AI actions) - Business impact analysis - Smart actions - Timing intelligence """ print("\n" + "="*70) print("🚀 SEEDING DEMO DATA FOR UNIFIED ALERT SERVICE") print("="*70 + "\n") rabbitmq_url = os.getenv('RABBITMQ_URL', 'amqp://guest:guest@localhost:5672/') try: # Initialize RabbitMQ client logger.info("Connecting to RabbitMQ", url=rabbitmq_url) rabbitmq_client = RabbitMQClient(rabbitmq_url, 'demo-seeder') await rabbitmq_client.connect() # Publish demo alerts (will be automatically enriched) print("\n📤 Publishing Alerts (Automatic Enrichment):") print("-" * 70) for i, alert in enumerate(DEMO_ALERTS, 1): routing_key = f"{alert['item_type']}.{alert['service']}.{alert['type']}" # Use publish_event method (correct API) success = await rabbitmq_client.publish_event( exchange_name='alerts.exchange', routing_key=routing_key, event_data=alert, persistent=True ) if not success: logger.warning("Failed to publish alert", alert_id=alert['id']) continue print(f" {i}. ✅ {alert['title']}") print(f" Service: {alert['service']}") print(f" Severity: {alert['severity']} (will be enriched)") print(f" Routing Key: {routing_key}") print() # Note about orchestrator actions print("\n📊 Orchestrator Actions (for Context Enrichment):") print("-" * 70) print("NOTE: Orchestrator actions should be seeded via orchestrator service.") print("These provide context for 'AI already handled' enrichment.\n") for i, action in enumerate(DEMO_ORCHESTRATOR_ACTIONS, 1): print(f" {i}. {action['summary']}") print(f" Type: {action['reasoning_type']}") print(f" Created: {action['created_at']}") print() print("💡 TIP: To seed orchestrator actions, run:") print(" python services/orchestrator/scripts/seed_demo_actions.py") print() await rabbitmq_client.disconnect() print("="*70) print("✅ DEMO ALERTS SEEDED SUCCESSFULLY!") print("="*70) print() print("Next steps:") print(" 1. Verify alert-processor service is running:") print(" kubectl get pods -l app.kubernetes.io/name=alert-processor-service") print() print(" 2. Check logs for automatic enrichment:") print(" kubectl logs -f deployment/alert-processor-service") print() print(" 3. View enriched alerts in dashboard:") print(" http://localhost:3000/dashboard") print() print("Expected automatic enrichment:") print(" • Low Stock Alert → Important (71) - Prevented Issue + Smart Actions") print(" • Supplier Delay → Critical (92) - Action Needed + Deep Links") print(" • Waste Trend → Standard (58) - Trend Warning + AI Reasoning") print(" • Forecast Update → Info (35) - Information") print(" • Equipment Maint → Standard (65) - Action Needed + Timing") print() print("All alerts enriched with:") print(" ✓ Multi-factor priority scores") print(" ✓ Orchestrator context (AI actions)") print(" ✓ Business impact analysis") print(" ✓ Smart actions with deep links") print(" ✓ Timing intelligence") print() except Exception as e: logger.error("Failed to seed demo data", error=str(e)) print(f"\n❌ ERROR: {str(e)}") print("\nTroubleshooting:") print(" • Verify RabbitMQ is running: kubectl get pods | grep rabbitmq") print(" • Check RABBITMQ_URL environment variable") print(" • Ensure raw_alerts exchange can be created") raise if __name__ == "__main__": # Load environment variables from dotenv import load_dotenv load_dotenv() # Run seeder asyncio.run(seed_enriched_alert_demo())