Add improved production UI 3

This commit is contained in:
Urtzi Alfaro
2025-09-23 19:24:22 +02:00
parent 7f871fc933
commit 7892c5a739
47 changed files with 6211 additions and 267 deletions

View File

@@ -49,14 +49,14 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
max_instances=1
)
# Equipment monitoring - disabled (equipment tables not available in production database)
# self.scheduler.add_job(
# self.check_equipment_status,
# CronTrigger(minute='*/3'),
# id='equipment_check',
# misfire_grace_time=30,
# max_instances=1
# )
# Equipment monitoring - check equipment status for maintenance alerts
self.scheduler.add_job(
self.check_equipment_status,
CronTrigger(minute='*/30'), # Check every 30 minutes
id='equipment_check',
misfire_grace_time=30,
max_instances=1
)
# Efficiency recommendations - every 30 minutes (recommendations)
self.scheduler.add_job(
@@ -394,19 +394,61 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
error=str(e))
async def check_equipment_status(self):
"""Check equipment status and failures (alerts)"""
# Equipment tables don't exist in production database - skip this check
logger.debug("Equipment check skipped - equipment tables not available in production database")
return
"""Check equipment status and maintenance requirements (alerts)"""
try:
self._checks_performed += 1
# Query equipment that needs attention
query = """
SELECT
e.id, e.tenant_id, e.name, e.type, e.status,
e.efficiency_percentage, e.uptime_percentage,
e.last_maintenance_date, e.next_maintenance_date,
e.maintenance_interval_days,
EXTRACT(DAYS FROM (e.next_maintenance_date - NOW())) as days_to_maintenance,
COUNT(ea.id) as active_alerts
FROM equipment e
LEFT JOIN alerts ea ON ea.equipment_id = e.id
AND ea.is_active = true
AND ea.is_resolved = false
WHERE e.is_active = true
AND e.tenant_id = $1
GROUP BY e.id, e.tenant_id, e.name, e.type, e.status,
e.efficiency_percentage, e.uptime_percentage,
e.last_maintenance_date, e.next_maintenance_date,
e.maintenance_interval_days
ORDER BY e.next_maintenance_date ASC
"""
tenants = await self.get_active_tenants()
for tenant_id in tenants:
try:
from sqlalchemy import text
async with self.db_manager.get_session() as session:
result = await session.execute(text(query), {"tenant_id": tenant_id})
equipment_list = result.fetchall()
for equipment in equipment_list:
await self._process_equipment_issue(equipment)
except Exception as e:
logger.error("Error checking equipment status",
tenant_id=str(tenant_id),
error=str(e))
except Exception as e:
logger.error("Equipment status check failed", error=str(e))
self._errors_count += 1
async def _process_equipment_issue(self, equipment: Dict[str, Any]):
"""Process equipment issue"""
try:
status = equipment['status']
efficiency = equipment.get('efficiency_percent', 100)
efficiency = equipment.get('efficiency_percentage', 100)
days_to_maintenance = equipment.get('days_to_maintenance', 30)
if status == 'error':
if status == 'down':
template_data = self.format_spanish_message(
'equipment_failure',
equipment_name=equipment['name']
@@ -422,41 +464,52 @@ class ProductionAlertService(BaseAlertService, AlertServiceMixin):
'equipment_id': str(equipment['id']),
'equipment_name': equipment['name'],
'equipment_type': equipment['type'],
'error_count': equipment.get('error_count', 0),
'last_reading': equipment.get('last_reading').isoformat() if equipment.get('last_reading') else None
'efficiency': efficiency
}
}, item_type='alert')
elif status == 'maintenance_required' or days_to_maintenance <= 1:
severity = 'high' if days_to_maintenance <= 1 else 'medium'
elif status == 'maintenance' or (days_to_maintenance is not None and days_to_maintenance <= 3):
severity = 'high' if (days_to_maintenance is not None and days_to_maintenance <= 1) else 'medium'
template_data = self.format_spanish_message(
'maintenance_required',
equipment_name=equipment['name'],
days_until_maintenance=max(0, int(days_to_maintenance)) if days_to_maintenance is not None else 3
)
await self.publish_item(equipment['tenant_id'], {
'type': 'maintenance_required',
'severity': severity,
'title': f'🔧 Mantenimiento Requerido: {equipment["name"]}',
'message': f'Equipo {equipment["name"]} requiere mantenimiento en {days_to_maintenance} días.',
'actions': ['Programar mantenimiento', 'Revisar historial', 'Preparar repuestos', 'Planificar parada'],
'title': template_data['title'],
'message': template_data['message'],
'actions': template_data['actions'],
'metadata': {
'equipment_id': str(equipment['id']),
'equipment_name': equipment['name'],
'days_to_maintenance': days_to_maintenance,
'last_maintenance': equipment.get('last_maintenance').isoformat() if equipment.get('last_maintenance') else None
'last_maintenance': equipment.get('last_maintenance_date')
}
}, item_type='alert')
elif efficiency < 80:
elif efficiency is not None and efficiency < 80:
severity = 'medium' if efficiency < 70 else 'low'
template_data = self.format_spanish_message(
'low_equipment_efficiency',
equipment_name=equipment['name'],
efficiency_percent=round(efficiency, 1)
)
await self.publish_item(equipment['tenant_id'], {
'type': 'low_equipment_efficiency',
'severity': severity,
'title': f'📉 Baja Eficiencia: {equipment["name"]}',
'message': f'Eficiencia del {equipment["name"]} bajó a {efficiency:.1f}%. Revisar funcionamiento.',
'actions': ['Revisar configuración', 'Limpiar equipo', 'Calibrar sensores', 'Revisar mantenimiento'],
'title': template_data['title'],
'message': template_data['message'],
'actions': template_data['actions'],
'metadata': {
'equipment_id': str(equipment['id']),
'efficiency_percent': float(efficiency),
'temperature': equipment.get('temperature'),
'vibration_level': equipment.get('vibration_level')
'equipment_name': equipment['name'],
'efficiency_percent': float(efficiency)
}
}, item_type='alert')