Skip to main content
Cuando un admin va a asignar un camello a un Job, GET /api/admin/jobs/:id/candidates calcula un score por candidato y devuelve el pool ordenado. El score se compone de razones (reasons) independientes que se suman; cada razón trae su etiqueta y sus puntos, así que la respuesta explica por qué un candidato quedó donde quedó, no solo el número final.
Esta página documenta el mecanismo tal como está implementado en src/lib/match-score-shared.ts. Los pesos son constantes reales del código (MATCH_WEIGHTS), no una estimación.

Factores y pesos

Estos valores son los de la constante MATCH_WEIGHTS en src/lib/match-score-shared.ts:

Geografía

La ciudad y el barrio se comparan normalizados (minúsculas, sin tildes, sin espacios sobrantes) para que variaciones de escritura no rompan la coincidencia. Si el candidato no está en la misma ciudad del Job, se revisa si declaró esa ciudad entre sus municipalities (los municipios que él mismo dijo cubrir, no una cobertura inferida por defecto). Si tampoco, la razón de geografía suma 0 puntos pero igual aparece en la respuesta (“Fuera de cobertura”).

Reputación

No se usa el promedio de calificación en bruto: se calcula una media bayesiana con un prior de priorRating: 4 y peso priorWeight: 3. La fórmula es:
Sin reseñas, bayes cae exactamente en priorRating (4 sobre 5): un camello nuevo no queda enterrado por no tener historial, pero tampoco una única reseña de 5 estrellas lo dispara al tope. Esa media bayesiana se escala contra el máximo de rating (5) y se multiplica por el peso rating (25): points = round((bayes / 5) * 25).

Carga

load son los trabajos activos del candidato. Los puntos son max(0, 20 - 7 * load): 20 puntos si está libre, 13 con un trabajo activo, 6 con dos, y 0 a partir de tres — nunca negativo.

Premium

Si el candidato es premium, se suma un bono fijo de 15 puntos como una razón adicional (kind: "premium"). Si no lo es, esa razón simplemente no aparece en la lista de motivos.

Desempate

El score es la suma de todas las razones aplicables. Cuando dos candidatos empatan en score, rankCandidates desempata en este orden: más reseñas (reviewCount) primero, luego orden alfabético del nombre (localeCompare en español), y por último el id como desempate final y determinista.

El endpoint

GET /api/admin/jobs/:id/candidates devuelve { items }: el pool de candidatos para el servicio de ese Job, ya ordenado por score, con los motivos (reasons) de cada puntaje incluidos por candidato. Acepta limit de 1 a 50 (default 20) y responde 404 si el trabajo no existe.