digest-recommendations.js
Overview
Este archivo define el motor determinista de recomendaciones del digest. Su función es generar hasta tres mensajes de acción a partir de datos reales de cada listing, sin usar IA generativa ni sugerencias no verificables.
El módulo trabaja con reglas de prioridad fija. Evalúa listings con periodTotals asociados y devuelve recomendaciones en forma de strings listas para mostrar en el digest. También exporta los key de las reglas para identificación consistente en otras partes del sistema.
[!IMPORTANT] Las recomendaciones dependen solo de señales comprobables del listing y sus totales de período. El módulo no inventa acciones sin respaldo en los datos.
How it works
flowchart TD
A[buildDigestRecommendations] --> B[RULES]
B --> C[Rule 1]
B --> D[Rule 2]
B --> E[Rule 3]
B --> F[Rule 4]
B --> G[Rule 5]
A --> H[MAX_RECOMMENDATIONS]
A --> I[Array of listing and periodTotals]
C --> J[Recommendation strings]
D --> J
E --> J
F --> J
G --> J
J --> K[Up to 3 recommendations]
El flujo recorre las reglas en orden y busca el primer listing que cumpla cada una. Si encuentra coincidencia, agrega el texto construido por esa regla. El proceso termina cuando alcanza MAX_RECOMMENDATIONS, que vale 3.
[!NOTE] Una misma regla solo puede aportar una recomendación por digest. El módulo no repite la misma regla para varios listings dentro de la misma ejecución.
Key components
{
"title": "MAX_RECOMMENDATIONS",
"content": "Constante p\u00fablica que limita a 3 el n\u00famero total de recomendaciones devueltas por digest.",
"type": "enum",
"filePath": "services/api/src/modules/digest/digest-recommendations.js",
"badges": [
"public",
"read-only"
]
}
{
"title": "hasText",
"content": "Funci\u00f3n auxiliar que verifica si un valor contiene texto \u00fatil despu\u00e9s de convertirlo a string y recortarlo.",
"type": "function",
"filePath": "services/api/src/modules/digest/digest-recommendations.js",
"badges": [
"public",
"read-only"
]
}
{
"title": "isOptimizable",
"content": "Funci\u00f3n auxiliar que permite reglas de optimizaci\u00f3n solo cuando el listing no est\u00e1 archivado.",
"type": "function",
"filePath": "services/api/src/modules/digest/digest-recommendations.js",
"badges": [
"public",
"read-only"
]
}
{
"title": "RULES",
"content": "Lista ordenada de reglas deterministas. Cada regla define una condici\u00f3n de coincidencia y el texto que se mostrar\u00e1 en el digest.",
"type": "enum",
"filePath": "services/api/src/modules/digest/digest-recommendations.js",
"badges": [
"read-only"
]
}
{
"title": "buildDigestRecommendations",
"content": "Funci\u00f3n p\u00fablica que recibe listings con totales de per\u00edodo y devuelve hasta 3 recomendaciones en texto plano.",
"type": "function",
"filePath": "services/api/src/modules/digest/digest-recommendations.js",
"badges": [
"public"
]
}
{
"title": "DIGEST_RECOMMENDATION_RULE_KEYS",
"content": "Exportaci\u00f3n derivada con los identificadores de todas las reglas en el mismo orden en que se eval\u00faan.",
"type": "enum",
"filePath": "services/api/src/modules/digest/digest-recommendations.js",
"badges": [
"public",
"read-only"
]
}
Data & fields
[
{
"name": "listingsWithPeriodTotals",
"type": "Array<{ listing, periodTotals }>",
"description": "Entrada principal de buildDigestRecommendations. Cada elemento combina un listing con sus totales del período."
},
{
"name": "listing",
"type": "object",
"required": true,
"description": "Objeto del listing evaluado por las reglas.",
"children": [
{
"name": "title",
"type": "string",
"required": true,
"description": "Nombre visible del listing. Se usa en el texto de la recomendación."
},
{
"name": "publication.status",
"type": "string",
"description": "Estado de publicación usado por varias reglas. Los valores observados en el archivo son draft, published y archived."
},
{
"name": "media",
"type": "Array",
"description": "Colección de medios del listing. La regla add_photos la usa para contar fotos."
},
{
"name": "description.short",
"type": "string",
"description": "Descripción corta del listing. Se valida con hasText."
},
{
"name": "description.full",
"type": "string",
"description": "Descripción completa del listing. Se valida con hasText."
},
{
"name": "contact.phone",
"type": "string",
"description": "Teléfono de contacto del listing. Se valida con hasText."
},
{
"name": "contact.link",
"type": "string",
"description": "Enlace de contacto del listing. Se valida con hasText."
}
]
},
{
"name": "periodTotals",
"type": "object",
"description": "Totales agregados del período para el listing.",
"children": [
{
"name": "detailOpens",
"type": "number",
"description": "Cantidad de aperturas de detalle del período. La regla share_listing exige que sea 0."
}
]
}
]
Campos usados por las reglas
listing.publication.status[badge:optional]string— Determina si el listing está endraft,publishedoarchived.listing.media[badge:optional]array— Se cuenta para detectar listings con menos de 3 fotos.listing.description.short[badge:optional]string— Se usa para evaluar si la descripción está incompleta.listing.description.full[badge:optional]string— Se usa junto condescription.shortpara validar el texto descriptivo.listing.contact.phone[badge:optional]string— Se valida para detectar contacto incompleto.listing.contact.link[badge:optional]string— Se valida junto concontact.phone.listing.title[badge:required]string— Se inserta en el texto final de cada recomendación.periodTotals.detailOpens[badge:optional]number— Se usa para detectar listings publicados sin aperturas recientes.
Behaviors
hasText(value)— Convierte cualquier valor a texto, lo recorta y devuelvetruesi queda contenido.isOptimizable(listing)— Devuelvetruesi el listing no está archivado.RULES— Define cinco reglas evaluadas en orden de prioridad.buildDigestRecommendations(listingsWithPeriodTotals)— Recorre las reglas y devuelve hasta tres mensajes.DIGEST_RECOMMENDATION_RULE_KEYS— Expone los identificadores de las reglas en orden estable.
Reglas de recomendación
La lista RULES contiene cinco entradas con key, matches y build. Cada una cubre una situación concreta del listing.
-
publish_draft- Matchea cuando
listing.publication.status === 'draft'. - Genera el mensaje para publicar el borrador.
- Tiene la mayor prioridad.
- Matchea cuando
-
add_photos- Matchea solo si el listing es optimizable.
- Exige que no sea
draft. - Se activa con menos de 3 elementos en
listing.media. - Sugiere agregar más fotos.
-
complete_description- Matchea solo si el listing es optimizable.
- Exige que
description.shortydescription.fullestén vacíos. - Sugiere completar la descripción.
-
review_contact- Matchea solo si el listing es optimizable.
- Exige que
contact.phoneycontact.linkestén vacíos. - Sugiere revisar los datos de contacto.
-
share_listing- Matchea solo si el listing está publicado.
- Exige
periodTotals.detailOpens === 0. - Sugiere compartir el listing por falta de aperturas.
How the selection logic behaves
buildDigestRecommendations recibe un array de objetos con listing y periodTotals. Luego evalúa cada regla en el mismo orden en que aparece en RULES.
Si una regla coincide, toma el primer listing que la cumple y agrega su mensaje. Después pasa a la siguiente regla. El algoritmo no agrega dos veces la misma regla en un digest.
[!WARNING]
buildDigestRecommendationsno agrega recomendaciones por listing, sino por regla. Eso limita el resultado a una selección global de hasta tres mensajes.
Workflows
1. Recibir listings | La función recibe un array de objetos con `listing` y `periodTotals`.
2. Evaluar reglas | Recorre `RULES` en orden de prioridad.
3. Buscar primera coincidencia | Para cada regla, toma el primer listing que cumple `matches`.
4. Construir mensaje | La regla ejecuta `build` y produce un string final.
5. Limitar salida | El proceso se detiene al llegar a `MAX_RECOMMENDATIONS`.
Gotchas & edge cases
[!WARNING] Los listings archivados quedan excluidos de todas las recomendaciones de optimización y publicación, salvo la regla
publish_draft, que ya no puede matchear un archivado porque exigedraft.
[!NOTE]
share_listingdepende deperiodTotals.detailOpens. Si ese valor falta, se interpreta como0por el uso deNumber(... || 0).
[!CAUTION] La función
hasTextconsidera vacío cualquier valor nulo, indefinido, numérico convertido a cadena vacía o texto con solo espacios.
Related
Este módulo pertenece a services/api/src/modules/digest/digest-recommendations.js y trabaja sobre la estructura de digest agregada que incluye listing y periodTotals. Su diseño encaja con un digest determinista, donde las recomendaciones salen de reglas verificables y no de un proveedor externo.
Las reglas se apoyan en campos de publicación, medios, descripción, contacto y actividad del período. Eso conecta este archivo con la capa de agregación previa que prepara los listings para recomendación.