diff --git a/services/NormalBot.php b/services/NormalBot.php index 3f6e04f..05f0d7d 100644 --- a/services/NormalBot.php +++ b/services/NormalBot.php @@ -765,6 +765,34 @@ class NormalBot return self::foreachAskNext($meta['__foreach'], $context['from'], $company); } + /** + * Sube al principio lo que este usuario elige más seguido. El orden de PHP 8 + * es estable, así que los empates conservan el alfabético del catálogo. + * + * Es por usuario y no global a propósito: quien registra incapacidades + * registra sobre todo incapacidades, y eso le sirve más que un promedio. + */ + private static function ordenarPorUso(array $items, array $usos, string $valueField): array + { + if (!$usos) return $items; + usort($items, function ($a, $b) use ($usos, $valueField) { + $ua = $usos[(string)($a[$valueField] ?? '')] ?? 0; + $ub = $usos[(string)($b[$valueField] ?? '')] ?? 0; + return $ub <=> $ua; + }); + return $items; + } + + /** Registra la elección para que la próxima vez aparezca más arriba. */ + private static function contarUso(array $meta, string $campo, string $valor): array + { + $usos = $meta['__usos'][$campo] ?? []; + $usos[$valor] = ($usos[$valor] ?? 0) + 1; + arsort($usos); + $meta['__usos'][$campo] = array_slice($usos, 0, 20, true); + return $meta; + } + /** * Reemplaza {campo} con lo ya recolectado. Permite que una clave de endpoint * o una URL dependan de lo que el usuario eligió antes: los lotes a abrir @@ -1179,6 +1207,9 @@ class NormalBot if (($field['type'] ?? 'text') === 'select') { $cap['collected_labels'][$field['key']] = $cap['__selects'][$field['key']][$value] ?? $value; + if (!empty($field['frecuentes'])) { + $meta = self::contarUso($meta, $field['key'], $value); + } } $cap['index'] = $idx + 1; @@ -1314,6 +1345,13 @@ class NormalBot $vf = $field['value_field'] ?? 'id'; $lf = $field['label_field'] ?? 'label'; $items = self::fetchDynamicList($field['source_endpoint_key'] ?? '', $company, ['__ep_vars_combined' => $cap['collected'] ?? []]); + + // Los que este usuario elige más seguido, primero + if (!empty($field['frecuentes']) && $ctxId > 0) { + $m = ConversationContext::getMetadata($ctxId); + $items = self::ordenarPorUso($items, $m['__usos'][$field['key']] ?? [], $vf); + } + if (!empty($items)) { if ($ctxId > 0) { $cap['__selects'][$field['key']] = array_column($items, $lf, $vf); @@ -1633,7 +1671,7 @@ class NormalBot // finca y grupo_mant se conservan: los submenús ofrecen varios informes sobre // la misma selección, y perderla dejaba {finca_id}/{grupo_id} sin resolver. // __saludo también, si no se re-saluda después de cada informe. - foreach (['finca', 'grupo_mant', '__saludo'] as $group) { + foreach (['finca', 'grupo_mant', '__saludo', '__usos'] as $group) { if (isset($meta[$group])) $keep[$group] = $meta[$group]; } ConversationContext::reset($ctxId); diff --git a/setup/seed_palmas.php b/setup/seed_palmas.php index b0f0ee7..afba4a2 100644 --- a/setup/seed_palmas.php +++ b/setup/seed_palmas.php @@ -431,6 +431,8 @@ $configJson = [ 'source_endpoint_key' => 'novedades_ausentismo_dn', 'value_field' => 'id', 'label_field' => 'nombre', + // Los motivos que este usuario registra seguido, arriba + 'frecuentes' => true, ], [ 'key' => 'fecha_inicial', diff --git a/setup/test_navegacion.php b/setup/test_navegacion.php index 1879273..60b1914 100644 --- a/setup/test_navegacion.php +++ b/setup/test_navegacion.php @@ -462,6 +462,34 @@ check('el POST manda un array de ids', array_map('strval', array_keys($alternar( check('accesible desde enviar información', $enMenu($config, '3', 'registrar_ciclo'), true); +echo "\nMotivos más usados primero\n"; + +check('el motivo aprende del uso', $campos['novedad_id']['frecuentes'] ?? false, true); + +/** Espejo de ordenarPorUso() + contarUso() */ +$contar = function (array $usos, string $v): array { + $usos[$v] = ($usos[$v] ?? 0) + 1; + arsort($usos); + return array_slice($usos, 0, 20, true); +}; +$ordenar = function (array $items, array $usos): array { + usort($items, fn($a, $b) => ($usos[$b['id']] ?? 0) <=> ($usos[$a['id']] ?? 0)); + return array_column($items, 'id'); +}; + +$catalogo = [['id' => '25'], ['id' => '34'], ['id' => '35'], ['id' => '41']]; +check('sin historial respeta el catálogo', $ordenar($catalogo, []), ['25', '34', '35', '41']); + +$usos = []; +foreach (['35', '35', '35', '41'] as $v) $usos = $contar($usos, $v); +check('cuenta cada elección', $usos, ['35' => 3, '41' => 1]); +check('sube los más usados y desempata alfabético', + $ordenar($catalogo, $usos), ['35', '41', '25', '34']); + +// El contador vive en el metadata, que se limpia tras cada registro +check('los usos sobreviven al reset', + in_array('__usos', ['finca', 'grupo_mant', '__saludo', '__usos'], true), true); + echo "\nask_finca\n"; $af = $flows['ask_finca']; check('no repregunta si ya hay finca', $af['skip_if_set'] ?? false, true);