diff --git a/google_ads_client.py b/google_ads_client.py index df0b82c..0c817f3 100644 --- a/google_ads_client.py +++ b/google_ads_client.py @@ -100,6 +100,70 @@ class GoogleAdsClient: print(f" ❌ Error obteniendo métricas de hoy: {e}") return result + def get_metrics_for_date(self, date_str: str) -> dict: + """ + Devuelve métricas de una fecha concreta ('YYYY-MM-DD') para TODAS las + campañas en una sola query. Retorna dict {campaign_id: {conversions, cost}}. + """ + ga_service = self.client.get_service("GoogleAdsService") + query = f""" + SELECT + campaign.id, + metrics.conversions, + metrics.cost_micros + FROM campaign + WHERE campaign.status != 'REMOVED' + AND segments.date = '{date_str}' + """ + 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 del {date_str}: {e}") + return result + + def get_daily_metrics_for_month(self, year: int, month: int) -> dict: + """ + Devuelve coste y conversiones diarias de todas las campañas para un mes + completo. Retorna dict {date_str ('YYYY-MM-DD'): {campaign_id: {cost, conversions}}}. + """ + start = f"{year}-{month:02d}-01" + 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, + metrics.cost_micros + 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] = { + "conversions": row.metrics.conversions, + "cost": row.metrics.cost_micros / 1_000_000, + } + except GoogleAdsException as e: + print(f" ❌ Error obteniendo métricas diarias del mes: {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. diff --git a/run.py b/run.py index df95ab0..2614bc5 100644 --- a/run.py +++ b/run.py @@ -398,8 +398,13 @@ def run(): if metricas_updates: print(f"→ Actualizando MetricasDiarias ({len(metricas_updates)} registros)...") if cambio_mes: - # Ayer pertenece al mes anterior: redirigir escritura al GACampaignMes correcto + # Ayer pertenece al mes anterior: redirigir escritura al GACampaignMes correcto. + # El "campaign" en memoria es el del mes nuevo (recién creado, MetricasDiarias + # casi vacío) — hay que hacer merge con el MetricasDiarias REAL del mes anterior + # antes de escribir, o se pierde el resto de días ya acumulados (bug que borró + # el histórico de junio salvo el día 30 al hacer un overwrite directo). prev_map = at.get_gcm_id_map_for_month(ayer.month, ayer.year) + prev_metricas = at.get_metricas_diarias_prev_month() redirected = [] omitidos = [] for u in metricas_updates: @@ -409,7 +414,14 @@ def run(): None, ) if gid and gid in prev_map: + try: + existing_md = json.loads(prev_metricas.get(gid, {}).get("metricas") or "{}") + except (json.JSONDecodeError, TypeError): + existing_md = {} + nuevo_dia_md = json.loads(u["metricas_json"]) + existing_md.update(nuevo_dia_md) u["airtable_id"] = prev_map[gid] + u["metricas_json"] = json.dumps(existing_md, ensure_ascii=False) redirected.append(u) else: omitidos.append(gid)