leads-optimizer/analyzer.py
José Manuel Gómez a1aef8f58e Multiple fixes: lead matching, metrics aggregation, Log field, SPRINT with capping=0
- Fix GoogleCampaignID matching with comma delimiters (exact word boundary)
- Remove attr_utm_medium filter (campaign ID filter is specific enough)
- Fix get_campaign_metrics: accumulate all daily rows for monthly totals
- Fix SPRINT urgency: only trigger when capping > 0
- Add Log field to GACampaignMes with Airtable vs Google Ads discrepancy info
- batch_update_gacampaignmes_advice now also updates Log field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 11:05:06 +02:00

70 lines
2.7 KiB
Python

from datetime import datetime
import calendar
def analyze(campaign_config: dict, leads_entregados: int, ads_metrics: dict) -> dict:
now = datetime.now()
dias_mes = calendar.monthrange(now.year, now.month)[1]
dia_actual = now.day
ratio_mes = dia_actual / dias_mes
capping = campaign_config["capping_mensual"]
ppl = campaign_config["ppl"]
cpa_max = campaign_config["cpa_maximo"]
margen_objetivo = campaign_config.get("margen_objetivo", 0)
gasto = ads_metrics.get("cost", 0)
conversiones_google = ads_metrics.get("conversions", 0)
ratio_leads = leads_entregados / capping if capping > 0 else 0
cpa_actual = gasto / leads_entregados if leads_entregados > 0 else 0
revenue = leads_entregados * ppl
margen = (revenue - gasto) / revenue if revenue > 0 else 0
leads_restantes = capping - leads_entregados
dias_restantes = dias_mes - dia_actual
ritmo = ratio_leads - ratio_mes # positivo = adelantado, negativo = atrasado
# Urgencia
if ratio_leads >= 1.0:
urgencia = "PAUSAR"
elif capping > 0 and ratio_leads < ratio_mes - 0.15 and dias_restantes <= 5:
urgencia = "SPRINT"
elif ritmo < -0.15:
urgencia = "ACELERAR"
elif ritmo > 0.15:
urgencia = "FRENAR"
else:
urgencia = "EN_RITMO"
# Discrepancia entre leads Airtable (ConvLeadsLakeMes) y conversiones Google
conv_leads_lake_mes = campaign_config.get("conv_leads_lake_mes", leads_entregados)
discrepancia = abs(conversiones_google - conv_leads_lake_mes)
return {
"curso": campaign_config["curso"],
"campaign_id": campaign_config["google_campaign_id"],
"ppl": ppl,
"cpa_maximo": cpa_max,
"margen_objetivo": margen_objetivo,
"margen_ok": margen >= margen_objetivo if margen_objetivo > 0 else True,
"capping": capping,
"leads_entregados": leads_entregados,
"leads_restantes": leads_restantes,
"dias_restantes": dias_restantes,
"ratio_leads": round(ratio_leads, 3),
"ratio_mes": round(ratio_mes, 3),
"ritmo": round(ritmo, 3),
"urgencia": urgencia,
"cpa_actual": round(cpa_actual, 2),
"rentable": cpa_actual <= cpa_max if cpa_actual > 0 else True,
"margen": round(margen, 3),
"revenue_estimado": round(revenue, 2),
"gasto_acumulado": round(gasto, 2),
"budget_diario_actual": ads_metrics.get("budget_daily", 0),
"ctr": ads_metrics.get("ctr", 0),
"clicks": ads_metrics.get("clicks", 0),
"conversiones_google": conversiones_google,
"discrepancia_tracking": discrepancia,
"alerta_tracking": discrepancia > 10,
"status_google": ads_metrics.get("status", "UNKNOWN"),
}