Skip to main content

merge_jsons (series Q)

Propósito

Fusión jerárquica de estructuras JSON (observable → sintético → data por agrupador → Q), sin perder históricos y actualizando solo lo nuevo. Evita el overwrite por .update() plano.

Estructura objetivo (esquema mental)

{
<observable>: {
<sintetico>: {
"metadata": {"last_updated": "...", ...},
"data": {
<grouper_value>: {
"2019Q4": <valor>,
"2020Q1": <valor>,
...
},
...
}
},
...
},
...
}

La fusión correcta agrega/actualiza por clave Q sin borrar trimestres previos y refresca metadata.last_updated.

Implementación recomendada

def merge_jsons(main_data: dict, new_data: dict) -> dict:
"""
Funde estructuras jerárquicas sin perder históricos trimestrales.
Niveles: observable -> sintetico -> data[grouper][Q] = valor
- Actualiza 'metadata.last_updated' con el del bloque nuevo.
- No hace overwrite masivo: actualiza Q puntuales.
"""
for observable, metrics in new_data.items():
if observable not in main_data:
main_data[observable] = {}

for sintetico, details in metrics.items():
# Inicializar nodo si no existe
if sintetico not in main_data[observable]:
main_data[observable][sintetico] = {
"metadata": details.get("metadata", {}),
"data": {}
}
else:
# Refrescar solo el last_updated (o mergear metadata si aplica)
if "metadata" in details and "last_updated" in details["metadata"]:
main_data[observable][sintetico]["metadata"]["last_updated"] = \
details["metadata"]["last_updated"]

# Merge fino por agrupador y Q
for grp_val, ts in details.get("data", {}).items():
main_node = main_data[observable][sintetico]["data"].setdefault(grp_val, {})
# ts es dict de Q->valor: actualizar uno a uno
for q, v in ts.items():
main_node[q] = v
return main_data

Este patrón condensa tus iteraciones jerárquicas previas y corrige el overwrite de series que generaba pérdida de Qs.

Política de no-overwrite

  • Regla: escribir por Q concreta; nunca reemplazar data[grp_val] entero.
  • Metadata: refrescar solo last_updated (o merge selectivo).

QA mínimo

  • Antes/después: contar Qs únicos por grp_val.
  • Idempotencia: aplicar dos veces merge_jsons(main, new) no cambia el resultado.
  • Verboz: opcionalmente loguear altas/updates por (observable, sintetico, grp_val, Q).

Errores comunes y cómo evitarlos

  • .update() al nivel equivocado → pisa el dict completo de Qs. Solución: loop granular por Q.
  • Introducir listas de dicts para series → complica dedupe. Preferí dict {Q: valor}.
  • No inicializar nodos intermediosKeyError. Usar setdefault/get.

Relacionados

  • /metodos/etl_json_policies (serialización, compresión y contratos).
  • Pocket → logging & QA de merges para checks de filas y diffs.