chore: initial sync
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
"""Combina atributos de multiples candidatos de la misma entidad."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
_NUMERIC_FIELDS = {"risk_score", "balance", "cvss"}
|
||||
_DATE_MIN_FIELDS = {"first_seen", "created_date"}
|
||||
_DATE_MAX_FIELDS = {"last_seen", "expires_date"}
|
||||
_BOOL_FIELDS = {"verified", "exploited"}
|
||||
|
||||
|
||||
def merge_entity_attributes(attr_list: list[dict]) -> dict:
|
||||
"""Combina atributos de multiples candidatos de la misma entidad.
|
||||
|
||||
Para cada campo presente en cualquier candidato recopila todos los valores
|
||||
non-null y aplica heuristicas de resolucion por tipo de campo:
|
||||
- Numerico (risk_score, balance, cvss): max
|
||||
- Fecha min (first_seen, created_date): min (mas antigua)
|
||||
- Fecha max (last_seen, expires_date): max (mas reciente)
|
||||
- Lista (cualquier valor de tipo list): union sin duplicados
|
||||
- Boolean (verified, exploited): OR logico
|
||||
- String: el mas largo
|
||||
|
||||
Args:
|
||||
attr_list: Lista de dicts con los atributos de cada candidato.
|
||||
|
||||
Returns:
|
||||
Dict con los atributos fusionados.
|
||||
"""
|
||||
if not attr_list:
|
||||
return {}
|
||||
|
||||
# Recopilar todas las claves presentes en cualquier candidato
|
||||
all_keys: set[str] = set()
|
||||
for attrs in attr_list:
|
||||
all_keys.update(attrs.keys())
|
||||
|
||||
merged: dict = {}
|
||||
|
||||
for key in all_keys:
|
||||
# Recopilar valores non-null
|
||||
values = [attrs[key] for attrs in attr_list if key in attrs and attrs[key] is not None]
|
||||
|
||||
if not values:
|
||||
merged[key] = None
|
||||
continue
|
||||
|
||||
if len(values) == 1:
|
||||
merged[key] = values[0]
|
||||
continue
|
||||
|
||||
# Todos iguales
|
||||
if all(v == values[0] for v in values):
|
||||
merged[key] = values[0]
|
||||
continue
|
||||
|
||||
# Resolver conflicto segun tipo de campo
|
||||
if key in _NUMERIC_FIELDS:
|
||||
merged[key] = max(values)
|
||||
elif key in _DATE_MIN_FIELDS:
|
||||
merged[key] = min(values)
|
||||
elif key in _DATE_MAX_FIELDS:
|
||||
merged[key] = max(values)
|
||||
elif key in _BOOL_FIELDS:
|
||||
merged[key] = any(values)
|
||||
elif isinstance(values[0], list):
|
||||
# Union de listas sin duplicados, preservando orden de aparicion
|
||||
seen: list = []
|
||||
for lst in values:
|
||||
for item in lst:
|
||||
if item not in seen:
|
||||
seen.append(item)
|
||||
merged[key] = seen
|
||||
else:
|
||||
# String u otro: usar el mas largo
|
||||
str_values = [str(v) for v in values]
|
||||
merged[key] = max(str_values, key=len)
|
||||
|
||||
return merged
|
||||
Reference in New Issue
Block a user