diff --git a/run.py b/run.py index 315f359..10eb51f 100644 --- a/run.py +++ b/run.py @@ -125,6 +125,32 @@ def run(): print(f"→ {len(campaigns)} campañas con actividad este mes") print("→ Analizando...\n") + # Detección anticipada de cursos con Search Y PMX simultáneos + def _course_num(name: str) -> str | None: + m = re.search(r'fco_(?:search|pmx)_(\d+)', name, re.IGNORECASE) + return m.group(1) if m else None + + course_types_pre: dict[str, set] = {} + for c in campaigns: + num = _course_num(c["curso"]) + if num: + course_types_pre.setdefault(num, set()) + if "pmx" in c["curso"].lower(): + course_types_pre[num].add("pmx") + elif "search" in c["curso"].lower(): + course_types_pre[num].add("search") + courses_with_both = { + num for num, types in course_types_pre.items() + if "pmx" in types and "search" in types + } + + # Detección anticipada de cursos con campaña _leadform activa + courses_with_leadform = { + _course_num(c["curso"]) + for c in campaigns + if "_leadform" in c["curso"].lower() and _course_num(c["curso"]) + } + # === PRIMERA PASADA: recopilar datos de todas las campañas === collected = [] skipped = [] @@ -150,12 +176,21 @@ def run(): google_status, )) - analysis = analyze(campaign, leads, metrics) + # Para PMX con companion Search: usar conversiones Google como leads de análisis + course_num = _course_num(campaign["curso"]) + is_pmx_with_companion = ( + "pmx" in campaign["curso"].lower() + and course_num in courses_with_both + ) + leads_grupo = int(metrics.get("conversions", 0)) if is_pmx_with_companion else leads + + analysis = analyze(campaign, leads_grupo, metrics) decision = decide(analysis) collected.append({ "campaign": campaign, "leads": leads, + "leads_grupo": leads_grupo, "metrics": metrics, "analysis": analysis, "decision": decision, @@ -174,23 +209,6 @@ def run(): # Ordenar: 0=críticas → 1=no críticas → 2=mantener collected.sort(key=_priority) - # Detectar cursos con campaña Search Y PMX simultáneas (posible reatribución PMX) - def _course_num(name: str) -> str | None: - m = re.search(r'fco_(?:search|pmx)_(\d+)', name, re.IGNORECASE) - return m.group(1) if m else None - - course_types: dict[str, set] = {} - for item in collected: - name = item["campaign"]["curso"] - num = _course_num(name) - if num: - course_types.setdefault(num, set()) - if "pmx" in name.lower(): - course_types[num].add("pmx") - elif "search" in name.lower(): - course_types[num].add("search") - courses_with_both = {num for num, types in course_types.items() if "pmx" in types and "search" in types} - # === SEGUNDA PASADA: imprimir en orden + aplicar decisiones === resumen = [] advice_updates = [] # (gcm_record_id, consejo, criticidad) para batch update final @@ -258,6 +276,13 @@ def run(): if "search" in campaign["curso"].lower() and course_num in courses_with_both: log_text += " | ⚠️ PMX ATTRIBUTION: campaña Search con companion PMX activo — parte de las conversiones de Google pueden estar reatribuidas a la campaña PMX" + # Nota leadform: los leads se capturan en Google sin pasar por la web + is_leadform = "_leadform" in campaign["curso"].lower() + if is_leadform: + log_text += " | ℹ️ LEADFORM: leads capturados directamente en Google (sin visitar la web) — no llegan a Airtable" + elif course_num in courses_with_leadform: + log_text += " | ⚠️ LEADFORM COMPANION: existe una campaña _leadform activa para este curso — parte de las conversiones de Google pueden provenir de leads capturados directamente en Google" + advice_updates.append(( campaign["airtable_id"], decision.get("consejo", ""), @@ -286,26 +311,15 @@ def run(): print(" ✓ Consejos y criticidad guardados.") # Snapshot diario: ConvLeadsLakeMesFinal + ConvLeadsLakeMesGrupo - # Para PMX con Search companion del mismo curso, Grupo = leads del Search - course_search_leads = { - _course_num(item["campaign"]["curso"]): item["leads"] - for item in collected - if "search" in item["campaign"]["curso"].lower() - and _course_num(item["campaign"]["curso"]) - } - final_leads_data = [] - for item in collected: - name = item["campaign"]["curso"] - num = _course_num(name) - if num and "pmx" in name.lower() and num in courses_with_both: - grupo = course_search_leads.get(num, item["leads"]) - else: - grupo = item["leads"] - final_leads_data.append({ + # PMX con companion Search → Grupo = conversiones Google (ya calculado en leads_grupo) + final_leads_data = [ + { "airtable_id": item["campaign"]["airtable_id"], "conv_leads_lake_mes": item["leads"], - "conv_leads_lake_mes_grupo": grupo, - }) + "conv_leads_lake_mes_grupo": item["leads_grupo"], + } + for item in collected + ] if final_leads_data: print(f"→ Actualizando ConvLeadsLakeMesFinal ({len(final_leads_data)} registros)...") at.batch_update_gacampaignmes_final_leads(final_leads_data)