feat: los motivos de ausentismo mas usados aparecen primero

El catalogo llega ordenado alfabeticamente y son 17 opciones, asi que el motivo
habitual quedaba en cualquier lado y a veces en la segunda pagina.

El bot cuenta que elige cada usuario y los sube. Es por usuario y no global a
proposito: quien registra incapacidades registra sobre todo incapacidades, y
eso le sirve mas que un promedio de toda la operacion.

El orden de PHP 8 es estable, asi que los empates conservan el alfabetico. El
contador se limita a 20 entradas y sobrevive al reset que corre tras cada
registro. Va como opcion por campo (frecuentes) para no reordenar listas donde
el alfabetico importa, como los lotes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-05 23:12:49 -05:00
co-authored by Claude Sonnet 4.6
parent 6ff2914162
commit 2d92867431
3 changed files with 69 additions and 1 deletions
+39 -1
View File
@@ -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);