- airtable_client: restrict leadform cursoid path (path 5) to PMX-only campaigns, preventing triple-attribution to search+pmx+leadform companion - slack_reporter: derive margen from ingreso-coste instead of stale MetricasDiarias field; split monthly rankings from fields to separate section blocks; add _trunc() helper to clamp all text blocks under Slack's 3000-char limit - agent: increase portfolio_daily_analysis max_tokens 500→800 - backfill: extend to June 11 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
98 lines
3.6 KiB
Python
98 lines
3.6 KiB
Python
"""
|
||
Recalcula leads_lake en MetricasDiarias para días 8-10 de junio 2026
|
||
añadiendo la atribución de leadforms via cursoid + UserAgent.
|
||
También actualiza ConvLeadsLakeMes, Leads Lake y ConvLeadsLakeMesFinal.
|
||
"""
|
||
import json
|
||
import re
|
||
from datetime import datetime
|
||
from airtable_client import AirtableClient
|
||
|
||
DAYS = ["2026-06-08", "2026-06-09", "2026-06-10", "2026-06-11"]
|
||
|
||
|
||
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
|
||
|
||
|
||
def run():
|
||
at = AirtableClient()
|
||
|
||
campaigns = at.get_active_gacampaignmes()
|
||
print(f"→ {len(campaigns)} campañas activas este mes\n")
|
||
|
||
# Mapping cursoid → PMX campaign_id para leadforms diarios
|
||
cursoid_to_campaign: dict[str, str] = {}
|
||
for c in campaigns:
|
||
num = _course_num(c["curso"])
|
||
if num and "pmx" in c["curso"].lower() and "_leadform" not in c["curso"].lower():
|
||
cursoid_to_campaign[num] = c["google_campaign_id"]
|
||
print(f"→ Mapping cursoid→campaign: {len(cursoid_to_campaign)} entradas")
|
||
|
||
# Obtener leads por campaña para cada día (una llamada bulk por día)
|
||
daily_counts: dict[str, dict[str, int]] = {}
|
||
for date_str in DAYS:
|
||
daily_counts[date_str] = at.get_leads_by_campaign_on_date(date_str, cursoid_to_campaign)
|
||
total = sum(daily_counts[date_str].values())
|
||
print(f" {date_str}: {total} leads totales encontrados")
|
||
print()
|
||
|
||
# Actualizar MetricasDiarias para cada campaña
|
||
metricas_updates = []
|
||
for campaign in campaigns:
|
||
cid = campaign["google_campaign_id"]
|
||
try:
|
||
md = json.loads(campaign["metricas_diarias"])
|
||
except (json.JSONDecodeError, TypeError):
|
||
md = {}
|
||
|
||
changed = False
|
||
for date_str in DAYS:
|
||
day_key = date_str[8:10]
|
||
if day_key not in md:
|
||
continue
|
||
new_count = daily_counts[date_str].get(cid, 0)
|
||
old_count = md[day_key].get("leads_lake", 0)
|
||
if old_count != new_count:
|
||
print(f" {campaign['curso'][:45]} día {day_key}: leads_lake {old_count} → {new_count}")
|
||
md[day_key]["leads_lake"] = new_count
|
||
# Recalcular ingreso_lxp para el día (leads_lake × PPL)
|
||
md[day_key]["ingreso"] = round(new_count * campaign["ppl"], 2)
|
||
changed = True
|
||
|
||
if changed:
|
||
metricas_updates.append({
|
||
"airtable_id": campaign["airtable_id"],
|
||
"metricas_json": json.dumps(md, ensure_ascii=False),
|
||
})
|
||
|
||
if metricas_updates:
|
||
print(f"\n→ Actualizando MetricasDiarias ({len(metricas_updates)} registros)...")
|
||
at.batch_update_metricas_diarias(metricas_updates)
|
||
print(" ✓ MetricasDiarias actualizado.")
|
||
else:
|
||
print(" ✓ MetricasDiarias sin cambios.")
|
||
|
||
# Recalcular totales mensuales con la nueva atribución
|
||
print("\n→ Recalculando totales mensuales (leads_lake acumulado del mes)...")
|
||
final_leads_data = []
|
||
for campaign in campaigns:
|
||
cid = campaign["google_campaign_id"]
|
||
leads, lead_ids = at.get_leads_this_month_gads(cid, campaign["curso"])
|
||
at.update_gacampaignmes_leads_lake(campaign["airtable_id"], lead_ids)
|
||
final_leads_data.append({
|
||
"airtable_id": campaign["airtable_id"],
|
||
"conv_leads_lake_mes": leads,
|
||
"conv_leads_lake_mes_grupo": leads,
|
||
})
|
||
if leads > 0:
|
||
print(f" {campaign['curso'][:45]}: {leads} leads")
|
||
|
||
at.batch_update_gacampaignmes_final_leads(final_leads_data)
|
||
print(" ✓ ConvLeadsLakeMesFinal actualizado.")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
run()
|