Sync ConvMes and CosteMes to Airtable on each run

- Add get_monthly_metrics_all() in GoogleAdsClient: fetches
  conversions and cost for all campaigns in one query
- sync_campaigns_from_google_ads() now accepts monthly_metrics
  and updates ConvMes and CosteMes fields in Airtable
- run.py fetches monthly metrics before sync and passes them

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jose Manuel 2026-04-17 21:12:05 +02:00
parent e4362c6ad9
commit 952fabe4ca
3 changed files with 47 additions and 4 deletions

View File

@ -27,7 +27,7 @@ class AirtableClient:
})
return [c for c in result if c["google_campaign_id"]] # descartar sin ID
def sync_campaigns_from_google_ads(self, google_campaigns: list[dict]) -> dict:
def sync_campaigns_from_google_ads(self, google_campaigns: list[dict], monthly_metrics: dict = None) -> dict:
"""
Sincroniza campañas de Google Ads Airtable.
- Crea las campañas nuevas con el status real de Google Ads.
@ -53,8 +53,16 @@ class AirtableClient:
at_status = STATUS_MAP.get(gc["status"], "Pausada")
at_record = at_by_gid.get(gid)
metrics = (monthly_metrics or {}).get(gid, {})
conv = round(metrics.get("conversions", 0), 2)
cost = round(metrics.get("cost", 0), 2)
if at_record is None:
to_create.append({"Campaign Name": gc["name"], "CampaignID": gid, "Status": at_status})
fields = {"Campaign Name": gc["name"], "CampaignID": gid, "Status": at_status}
if monthly_metrics is not None:
fields["ConvMes"] = conv
fields["CosteMes"] = cost
to_create.append(fields)
created.append({"name": gc["name"], "id": gid, "status": at_status})
else:
changes = {}
@ -71,9 +79,15 @@ class AirtableClient:
"antes": at_record["fields"].get("Status"),
"ahora": at_status,
}
if monthly_metrics is not None:
if at_record["fields"].get("ConvMes") != conv:
changes["ConvMes"] = conv
if at_record["fields"].get("CosteMes") != cost:
changes["CosteMes"] = cost
if changes:
to_update.append((at_record["id"], changes, gc["name"]))
updated.append({"name": gc["name"], "id": gid, "changes": change_detail})
if change_detail:
updated.append({"name": gc["name"], "id": gid, "changes": change_detail})
# Crear en lotes de 10
for i in range(0, len(to_create), 10):

View File

@ -41,6 +41,34 @@ class GoogleAdsClient:
print(f" ❌ Error obteniendo campañas de Google Ads: {e}")
return []
def get_monthly_metrics_all(self) -> dict:
"""
Devuelve métricas del mes actual para TODAS las campañas en una sola query.
Retorna dict {campaign_id: {conversions, cost}}.
"""
ga_service = self.client.get_service("GoogleAdsService")
query = """
SELECT
campaign.id,
metrics.conversions,
metrics.cost_micros
FROM campaign
WHERE campaign.status != 'REMOVED'
AND segments.date DURING THIS_MONTH
"""
result = {}
try:
response = ga_service.search(customer_id=self.customer_id, query=query)
for row in response:
cid = str(row.campaign.id)
result[cid] = {
"conversions": row.metrics.conversions,
"cost": row.metrics.cost_micros / 1_000_000,
}
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."""
ga_service = self.client.get_service("GoogleAdsService")

3
run.py
View File

@ -53,7 +53,8 @@ def run():
# Sincronizar catálogo de campañas desde Google Ads → Airtable
print("→ Sincronizando campañas desde Google Ads...")
google_campaigns = gads.get_all_campaigns()
sync_result = at.sync_campaigns_from_google_ads(google_campaigns)
monthly_metrics = gads.get_monthly_metrics_all()
sync_result = at.sync_campaigns_from_google_ads(google_campaigns, monthly_metrics)
if sync_result["created"]:
print(f" ✅ Campañas nuevas importadas ({len(sync_result['created'])}):")
for c in sync_result["created"]: