diff --git a/index.py b/index.py index ca0645e..044c865 100755 --- a/index.py +++ b/index.py @@ -2149,69 +2149,71 @@ def run_update(accounts=None, exclude=None, full_refresh=False): active_locs_with_obj.sort(key=get_download_epoch) - # 3. Construir Grupo A (Secuencial - 27 sucursales) - group_a = active_locs_with_obj[:27] - group_a_ids = {x[0] for x in group_a} - - # 4. Construir Grupo B (Alta Frecuencia - Hasta 7 sucursales) - group_b_pool = [] + # 3. Construir Grupo 0 (Alarmas Activas - SIEMPRE incluidas, sin excepción) + # Se extraen ANTES de los demás grupos para garantizar presencia en cada ciclo. + group_0 = [] + group_0_ids = set() + non_alarm_locs = [] for loc_id, location in active_locs_with_obj: - if loc_id in group_a_ids: - continue loc_id_str = str(loc_id) prev = last_loc_map.get(loc_id_str, {}) - - status_val = prev.get('status', 0) - if isinstance(status_val, ArmingState): - status_val = status_val.value - - is_disarmed = (status_val == 10200 or str(status_val).upper() == "DISARMED") - - # Alarma real activa en zonas o particiones is_triggered = ( len(prev.get('triggered_partitions', [])) > 0 or any(z.get('status') == 'Alarma' for z in prev.get('active_zones', [])) ) - has_faults = len(prev.get('active_zones', [])) > 0 - - if is_disarmed or is_triggered or has_faults: - # Peso de severidad: - # 0 = Alarma Activa (Prioridad Absoluta) - # 1 = Zonas con fallas/abiertas (Prioridad Media) - # 2 = Desarmado simple (Prioridad Baja) - severity_weight = 2 - if is_triggered: - severity_weight = 0 - elif has_faults: - severity_weight = 1 - - group_b_pool.append((loc_id, location, severity_weight)) - - # Ordenar por severidad de fallo/alarma primero, y en caso de empate por antigüedad - def sort_group_b(item): - loc_tuple = item - loc_id_str = str(loc_tuple[0]) + if is_triggered: + group_0.append((loc_id, location)) + group_0_ids.add(loc_id) + log_msg(f" 🚨 Grupo 0 (Alarma): '{location.location_name}' forzado en este ciclo.") + else: + non_alarm_locs.append((loc_id, location)) + + # 4. Construir Grupo A (Secuencial - 27 sucursales, solo NO alarmadas) + group_a = non_alarm_locs[:27] + group_a_ids = {x[0] for x in group_a} + + # 5. Construir Grupo B (Alta Frecuencia - Hasta 7 sucursales no alarmadas) + group_b_pool = [] + for loc_id, location in non_alarm_locs: + if loc_id in group_a_ids: + continue + loc_id_str = str(loc_id) prev = last_loc_map.get(loc_id_str, {}) - dl_epoch = prev.get('last_download_epoch', 0) - severity = loc_tuple[2] - return (severity, dl_epoch) - + + status_val = prev.get('status', 0) + if isinstance(status_val, ArmingState): + status_val = status_val.value + + is_disarmed = (status_val == 10200 or str(status_val).upper() == "DISARMED") + has_faults = len(prev.get('active_zones', [])) > 0 + + if is_disarmed or has_faults: + severity_weight = 1 if has_faults else 2 + group_b_pool.append((loc_id, location, severity_weight)) + + # Ordenar por severidad primero, luego por antigüedad + def sort_group_b(item): + loc_id_str = str(item[0]) + prev = last_loc_map.get(loc_id_str, {}) + return (item[2], prev.get('last_download_epoch', 0)) + group_b_pool.sort(key=sort_group_b) group_b = [(x[0], x[1]) for x in group_b_pool[:7]] group_b_ids = {x[0] for x in group_b} - # 5. Construir Grupo C (Triggers de SOAP pendientes) + # 6. Construir Grupo C (Triggers de SOAP pendientes, excluye alarmadas, A y B) group_c = [] for loc_id, location in active_locs_with_obj: - if loc_id in group_a_ids or loc_id in group_b_ids: + if loc_id in group_0_ids or loc_id in group_a_ids or loc_id in group_b_ids: continue loc_id_str = str(loc_id) prev = last_loc_map.get(loc_id_str, {}) if prev.get('needs_full_update', False): group_c.append((loc_id, location)) - # Lote completo a procesar en este ciclo - lote_completo = group_a + group_b + group_c + # Lote completo a procesar en este ciclo. + # group_0 va PRIMERO: sucursales en alarma activa garantizadas en cada ciclo. + lote_completo = group_0 + group_a + group_b + group_c lote_ids = {x[0] for x in lote_completo} loc_list = []