From 523e04d61f5894031c5b36d6f566cc4018cbd31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20G=C3=B3mez?= Date: Mon, 4 May 2026 10:47:27 +0200 Subject: [PATCH] Backfill Google Ads daily leads for May in MetricasDiarias - Add get_daily_conversions_for_month() to GoogleAdsClient - Add backfill_leads_google_mayo.py: populates 'leads' (Google conv) for all May days - 147 day-entries updated across 49 GACampaignMes records Co-Authored-By: Claude Sonnet 4.6 --- backfill_leads_google_mayo.py | 62 +++++++++++++++++++++++++++++++++++ google_ads_client.py | 33 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 backfill_leads_google_mayo.py diff --git a/backfill_leads_google_mayo.py b/backfill_leads_google_mayo.py new file mode 100644 index 0000000..1070667 --- /dev/null +++ b/backfill_leads_google_mayo.py @@ -0,0 +1,62 @@ +""" +Script one-off: rellena el campo 'leads' (conversiones Google Ads) en MetricasDiarias +para cada dia de mayo ya registrado en GACampaignMes. + +Ejecutar una sola vez: + python backfill_leads_google_mayo.py +""" +import json +from airtable_client import AirtableClient +from google_ads_client import GoogleAdsClient +at = AirtableClient() +gads = GoogleAdsClient() + +print("Obteniendo conversiones diarias de Google Ads para mayo 2026...") +daily_conv = gads.get_daily_conversions_for_month(2026, 5) +print(f" > {len(daily_conv)} dias con datos en Google Ads") + +print("Cargando registros GACampaignMes de mayo...") +records = at.gacampaignmes.all( + formula="AND({Mes}='5',{Año}='2026')", + fields=["CampaignID", "MetricasDiarias", "Campaign Name (from CampaignID)"], +) + +campaigns_records = at.campaigns.all(fields=["CampaignID"]) +at_id_to_gid = {r["id"]: str(r["fields"].get("CampaignID", "")).strip() for r in campaigns_records} + +gid_to_gcm: dict[str, tuple[str, dict]] = {} +for r in records: + at_cids = r["fields"].get("CampaignID", []) + if not at_cids: + continue + gid = at_id_to_gid.get(at_cids[0], "") + if not gid: + continue + try: + md = json.loads(r["fields"].get("MetricasDiarias") or "{}") + except (json.JSONDecodeError, TypeError): + md = {} + gid_to_gcm[gid] = (r["id"], md) + +total_updated = 0 +for date_str, conv_by_cid in sorted(daily_conv.items()): + day_str = date_str[8:10] # "YYYY-MM-DD" -> "DD" + for gid, (gcm_id, md) in gid_to_gcm.items(): + if day_str in md and "leads" not in md[day_str]: + md[day_str]["leads"] = int(conv_by_cid.get(gid, 0)) + total_updated += 1 + +print(f"Entradas actualizadas en memoria: {total_updated}") + +batch = [ + {"id": gcm_id, "fields": {"MetricasDiarias": json.dumps(md, ensure_ascii=False)}} + for _, (gcm_id, md) in gid_to_gcm.items() + if any("leads" in v for v in md.values()) +] + +print(f"Guardando {len(batch)} registros en Airtable...") +for i in range(0, len(batch), 10): + at.gacampaignmes.batch_update(batch[i:i+10]) + print(f" > {min(i+10, len(batch))}/{len(batch)} guardados") + +print(f"OK Backfill completado: {len(batch)} registros, {total_updated} entradas de dia actualizadas.") diff --git a/google_ads_client.py b/google_ads_client.py index 165ac1a..49c86de 100644 --- a/google_ads_client.py +++ b/google_ads_client.py @@ -100,6 +100,39 @@ class GoogleAdsClient: print(f" ❌ Error obteniendo métricas de hoy: {e}") return result + def get_daily_conversions_for_month(self, year: int, month: int) -> dict: + """ + Devuelve conversiones diarias de todas las campañas para un mes completo. + Retorna dict {date_str ('YYYY-MM-DD'): {campaign_id: conversions}}. + """ + start = f"{year}-{month:02d}-01" + # último día del mes + if month == 12: + end = f"{year + 1}-01-01" + else: + end = f"{year}-{month + 1:02d}-01" + ga_service = self.client.get_service("GoogleAdsService") + query = f""" + SELECT + campaign.id, + segments.date, + metrics.conversions + FROM campaign + WHERE campaign.status != 'REMOVED' + AND segments.date >= '{start}' + AND segments.date < '{end}' + """ + result: dict = {} + try: + response = ga_service.search(customer_id=self.customer_id, query=query) + for row in response: + date_str = row.segments.date + cid = str(row.campaign.id) + result.setdefault(date_str, {})[cid] = row.metrics.conversions + except GoogleAdsException as e: + print(f" ❌ Error obteniendo métricas mensuales: {e}") + return result + def get_campaign_metrics(self, campaign_id: str) -> dict: """Métricas del mes en curso para una campaña concreta (acumulado mensual).""" ga_service = self.client.get_service("GoogleAdsService")