diff --git a/agent.py b/agent.py index c78a8ef..3791ce1 100644 --- a/agent.py +++ b/agent.py @@ -228,7 +228,7 @@ def weekly_strategic_analysis(games_md_this: dict, games_md_prev_week: dict, def decide(analysis: dict) -> dict: response = client.messages.create( model="claude-sonnet-4-6", - max_tokens=700, + max_tokens=750, system=SYSTEM_PROMPT, messages=[{ "role": "user", diff --git a/analyzer.py b/analyzer.py index e77b328..3e5d720 100644 --- a/analyzer.py +++ b/analyzer.py @@ -35,7 +35,7 @@ def analyze(campaign_config: dict, leads_entregados: int, ads_metrics: dict) -> else: urgencia = "EN_RITMO" - # Discrepancia entre leads Airtable (ConvLeadsLakeMes) y conversiones Google + # Discrepancia en ambas direcciones: Google > Airtable puede indicar conversiones falsas conv_leads_lake_mes = campaign_config.get("conv_leads_lake_mes", leads_entregados) discrepancia = abs(conversiones_google - conv_leads_lake_mes) diff --git a/run.py b/run.py index 002d6b8..23f80e2 100644 --- a/run.py +++ b/run.py @@ -220,10 +220,8 @@ def run(): if is_pmx_with_leadform: leads_grupo = int(metrics.get("conversions", 0)) + leadform_conv_by_course.get(course_num, 0) - elif is_pmx_with_companion: - leads_grupo = int(metrics.get("conversions", 0)) else: - leads_grupo = leads + leads_grupo = int(metrics.get("conversions", 0)) analysis = analyze(campaign, leads_grupo, metrics) decision = decide(analysis) @@ -296,8 +294,11 @@ def run(): f"Rentable: {'✅' if analysis['rentable'] else '❌'}") if analysis["alerta_tracking"]: - print(f" 🚨 ALERTA TRACKING: {analysis['discrepancia_tracking']} leads de diferencia " - f"entre Airtable ({campaign['conv_leads_lake_mes']}) y Google Ads ({int(analysis['conversiones_google'])})") + at_leads = campaign['conv_leads_lake_mes'] + ga_leads = int(analysis['conversiones_google']) + direction = "Airtable > Google (sobre-atribución?)" if at_leads > ga_leads else "Google > Airtable (conversiones falsas?)" + print(f" 🚨 ALERTA TRACKING: {analysis['discrepancia_tracking']:.0f} leads de diferencia " + f"— Airtable {at_leads} vs Google Ads {ga_leads} — {direction}") print(f" Decisión: {accion_icono} {decision['accion']} " f"(confianza: {decision['confianza']*100:.0f}%)") diff --git a/slack_reporter.py b/slack_reporter.py index da8e455..7128519 100644 --- a/slack_reporter.py +++ b/slack_reporter.py @@ -185,35 +185,56 @@ def build_and_send(collected: list, dry_run: bool, prev_month_metricas: dict = N except ValueError: continue if d not in daily_totals: - daily_totals[d] = {"coste": 0.0, "ingreso_sum": 0.0} - daily_totals[d]["coste"] += vals.get("coste", 0) - daily_totals[d]["ingreso_sum"] += vals.get("ingreso", 0) + daily_totals[d] = {"coste": 0.0, "ing_gads": 0.0, "ing_at": 0.0, "leads_gads": 0, "leads_at": 0} + daily_totals[d]["coste"] += vals.get("coste", 0) + daily_totals[d]["ing_gads"] += vals.get("ingreso", 0) # conv_hoy × PPL + daily_totals[d]["ing_at"] += vals.get("leads_lake", 0) * ppl # leads_lake × PPL + daily_totals[d]["leads_gads"] += int(vals.get("leads", 0)) + daily_totals[d]["leads_at"] += int(vals.get("leads_lake", 0)) + + def _eur(v: float) -> str: + return f"{v:,.0f}€".replace(",", ".") + + def _marg(v: float) -> str: + return ("+" if v >= 0 else "") + f"{v:,.0f}€".replace(",", ".") + + def _pct(v: float) -> str: + return ("+" if v >= 0 else "") + f"{v:.1f}%" margin_table_block = None if daily_totals and not primer_dia_mes: - rows = ["Día Margen € %"] - total_coste = total_ing = 0.0 + hdr = f"{'Día':>3} {'L.AT':>5} {'L.GA':>5} {'Coste':>7} {'€.AT':>7} {'€.GA':>7} {'Margen':>9} {'%':>7}" + sep = "─" * len(hdr) + rows = [hdr, sep] + tot = {"coste": 0.0, "ing_gads": 0.0, "ing_at": 0.0, "leads_gads": 0, "leads_at": 0} for d in sorted(daily_totals): - coste = daily_totals[d]["coste"] - ing_sum = daily_totals[d]["ingreso_sum"] - margen = ing_sum - coste - pct = round(margen / ing_sum * 100, 1) if ing_sum > 0 else 0.0 - total_coste += coste - total_ing += ing_sum - s_eur = ("+" if margen >= 0 else "") + f"{margen:,.0f}€".replace(",", ".") - s_pct = ("+" if pct >= 0 else "") + f"{pct:.1f}%" - rows.append(f"{d:02d} {s_eur:>9} {s_pct:>7}") - total_margen = total_ing - total_coste - total_pct = round(total_margen / total_ing * 100, 1) if total_ing > 0 else 0.0 - s_tot_eur = ("+" if total_margen >= 0 else "") + f"{total_margen:,.0f}€".replace(",", ".") - s_tot_pct = ("+" if total_pct >= 0 else "") + f"{total_pct:.1f}%" - rows.append("─" * 24) - rows.append(f"TOT {s_tot_eur:>9} {s_tot_pct:>7}") + v = daily_totals[d] + ing = v["ing_gads"] # margen: conv Google Ads × PPL + marg = ing - v["coste"] + pct_v = round(marg / ing * 100, 1) if ing > 0 else 0.0 + for k in tot: + tot[k] += v[k] + rows.append( + f"{d:02d} {v['leads_at']:>5} {v['leads_gads']:>5}" + f" {_eur(v['coste']):>7}" + f" {_eur(v['ing_at']):>7} {_eur(v['ing_gads']):>7}" + f" {_marg(marg):>9} {_pct(pct_v):>7}" + ) + tot_ing = tot["ing_gads"] + tot_marg = tot_ing - tot["coste"] + tot_pct = round(tot_marg / tot_ing * 100, 1) if tot_ing > 0 else 0.0 + rows.append(sep) + rows.append( + f"{'TOT':>3} {tot['leads_at']:>5} {tot['leads_gads']:>5}" + f" {_eur(tot['coste']):>7}" + f" {_eur(tot['ing_at']):>7} {_eur(tot['ing_gads']):>7}" + f" {_marg(tot_marg):>9} {_pct(tot_pct):>7}" + ) margin_table_block = { "type": "section", "text": { "type": "mrkdwn", - "text": _trunc(f"📅 *MÁRGENES POR DÍA — {MESES_ES[now.month].upper()}*\n```\n" + "\n".join(rows) + "\n```"), + "text": _trunc(f"📅 *MÉTRICAS POR DÍA — {MESES_ES[now.month].upper()}* _(L.AT=leads Airtable · L.GA=conv Google Ads · €.AT=fact×PPL Airtable · €.GA=fact×PPL Google Ads)_\n```\n" + "\n".join(rows) + "\n```"), }, }