From d9a8e7ee0d825eb6069a52dbb02b5882ecc0f924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20G=C3=B3mez?= Date: Tue, 21 Apr 2026 13:05:42 +0200 Subject: [PATCH] Update campaign status in both tables from per-campaign Google Ads metrics - get_active_gacampaignmes() now returns campaign_at_id (Google Ads Campaigns record ID) - New batch_update_status() updates Status in GACampaignMes and Google Ads Campaigns - run.py first pass collects status from get_campaign_metrics() and batch-updates both tables at end of pass, ensuring fresh status after each run Co-Authored-By: Claude Sonnet 4.6 --- airtable_client.py | 39 ++++++++++++++++++++++++++++++++------- run.py | 14 ++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/airtable_client.py b/airtable_client.py index 22e4d0e..6da4d94 100644 --- a/airtable_client.py +++ b/airtable_client.py @@ -272,13 +272,15 @@ class AirtableClient: mes_num = str(now.month) anio_str = str(now.year) - # Mapping at_record_id → google_campaign_id + # Mapping at_record_id → google_campaign_id y gid → at_record_id campaigns_records = self.campaigns.all(fields=["CampaignID"]) - at_id_to_gid = { - r["id"]: str(r["fields"].get("CampaignID", "")).strip() - for r in campaigns_records - if r["fields"].get("CampaignID") - } + at_id_to_gid = {} + gid_to_at_id = {} + for r in campaigns_records: + gid = str(r["fields"].get("CampaignID", "")).strip() + if gid: + at_id_to_gid[r["id"]] = gid + gid_to_at_id[gid] = r["id"] formula = f"AND({{Mes}}='{mes_num}',{{Año}}='{anio_str}')" records = self.gacampaignmes.all(formula=formula) @@ -295,7 +297,8 @@ class AirtableClient: name = name_list[0] if name_list else "Sin nombre" result.append({ - "airtable_id": r["id"], + "airtable_id": r["id"], # GACampaignMes record ID + "campaign_at_id": gid_to_at_id.get(gid, ""), # Google Ads Campaigns record ID "curso": name, "cursoid_text": "", "google_campaign_id": gid, @@ -306,6 +309,28 @@ class AirtableClient: }) return result + def batch_update_status(self, updates: list[tuple[str, str, str]]) -> None: + """ + Actualiza en lote el campo Status en GACampaignMes y Google Ads Campaigns. + updates: lista de (gcm_record_id, campaign_at_id, google_status) + google_status: 'ENABLED' | 'PAUSED' + """ + STATUS_MAP = {"ENABLED": "Activa", "PAUSED": "Pausada"} + + gcm_batch = [] + cat_batch = [] + for gcm_id, cat_id, google_status in updates: + at_status = STATUS_MAP.get(google_status, "Pausada") + if gcm_id: + gcm_batch.append({"id": gcm_id, "fields": {"Status": at_status}}) + if cat_id: + cat_batch.append({"id": cat_id, "fields": {"Status": at_status}}) + + for i in range(0, len(gcm_batch), 10): + self.gacampaignmes.batch_update(gcm_batch[i:i+10]) + for i in range(0, len(cat_batch), 10): + self.campaigns.batch_update(cat_batch[i:i+10]) + def get_leads_this_month_gads(self, campaign_id: str) -> tuple[int, list[str]]: """ Leads del mes actual para una campaña de Google Ads. diff --git a/run.py b/run.py index 7cbb07f..106814d 100644 --- a/run.py +++ b/run.py @@ -121,6 +121,7 @@ def run(): # === PRIMERA PASADA: recopilar datos de todas las campañas === collected = [] skipped = [] + status_updates = [] # (gcm_id, campaign_at_id, google_status) para batch update for campaign in campaigns: cid = campaign["google_campaign_id"] @@ -133,6 +134,15 @@ def run(): skipped.append(f"[{cid}] {campaign['curso']}") continue + # Recopilar status para actualizar ambas tablas al final del pase + google_status = metrics.get("status", "") + if google_status: + status_updates.append(( + campaign["airtable_id"], + campaign.get("campaign_at_id", ""), + google_status, + )) + analysis = analyze(campaign, leads, metrics) decision = decide(analysis) @@ -144,6 +154,10 @@ def run(): "decision": decision, }) + # Actualizar status en GACampaignMes y Google Ads Campaigns + if status_updates: + at.batch_update_status(status_updates) + if skipped: print(f" ⚠️ {len(skipped)} campañas sin métricas omitidas:") for s in skipped: