Auto-calculate PPL and CPAMax from CENTROCURSO on sync
- Add build_ppl_lookup(): loads Cursos + CENTROCURSO ABIERTO in 2
bulk calls, returns {cursoid_text: sum_ppl} for all courses
- sync_campaigns_from_google_ads() now accepts ppl_lookup and
updates PPL = sum(CENTROCURSO.PPL where Estado ROI=ABIERTO)
and CPAMax = PPL * 70% for each campaign
- run.py builds the lookup before sync and passes it in
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
952fabe4ca
commit
ec3802eccf
@ -8,6 +8,40 @@ class AirtableClient:
|
|||||||
self.api = Api(config.AIRTABLE_TOKEN)
|
self.api = Api(config.AIRTABLE_TOKEN)
|
||||||
self.leads = self.api.table(config.AIRTABLE_BASE_ID, config.LEADS_TABLE)
|
self.leads = self.api.table(config.AIRTABLE_BASE_ID, config.LEADS_TABLE)
|
||||||
self.campaigns = self.api.table(config.AIRTABLE_BASE_ID, config.CAMPAIGNS_TABLE)
|
self.campaigns = self.api.table(config.AIRTABLE_BASE_ID, config.CAMPAIGNS_TABLE)
|
||||||
|
self.cursos = self.api.table(config.AIRTABLE_BASE_ID, "Cursos")
|
||||||
|
self.centrocurso = self.api.table(config.AIRTABLE_BASE_ID, "CENTROCURSO")
|
||||||
|
|
||||||
|
def build_ppl_lookup(self) -> dict:
|
||||||
|
"""
|
||||||
|
Construye un dict {cursoid_text: ppl_sum} calculando el PPL de cada curso
|
||||||
|
sumando los PPL de los registros CENTROCURSO con Estado ROI = 'ABIERTO'.
|
||||||
|
|
||||||
|
Cadena: CursoID Text → Cursos.CursoID → Cursos.CentroCurso → CENTROCURSO.PPL
|
||||||
|
Solo se usan en lotes (2 llamadas bulk), sin llamadas por campaña.
|
||||||
|
"""
|
||||||
|
# 1. Cargar CENTROCURSO ABIERTO: {record_id: ppl}
|
||||||
|
cc_records = self.centrocurso.all(
|
||||||
|
formula="{Estado ROI}='ABIERTO'",
|
||||||
|
fields=["PPL"],
|
||||||
|
)
|
||||||
|
cc_ppl = {r["id"]: float(r["fields"].get("PPL") or 0) for r in cc_records}
|
||||||
|
|
||||||
|
# 2. Cargar Cursos: {cursoid_number: [cc_record_ids]}
|
||||||
|
cursos_records = self.cursos.all(fields=["CursoID", "CentroCurso"])
|
||||||
|
curso_to_cc = {}
|
||||||
|
for r in cursos_records:
|
||||||
|
cid = r["fields"].get("CursoID")
|
||||||
|
cc_ids = r["fields"].get("CentroCurso", [])
|
||||||
|
if cid is not None:
|
||||||
|
curso_to_cc[str(int(cid))] = cc_ids
|
||||||
|
|
||||||
|
# 3. Para cada CursoID Text, sumar PPL de CENTROCURSO ABIERTO
|
||||||
|
ppl_lookup = {}
|
||||||
|
for cursoid_text, cc_ids in curso_to_cc.items():
|
||||||
|
total_ppl = sum(cc_ppl[cc_id] for cc_id in cc_ids if cc_id in cc_ppl)
|
||||||
|
ppl_lookup[cursoid_text] = round(total_ppl, 2)
|
||||||
|
|
||||||
|
return ppl_lookup
|
||||||
|
|
||||||
def get_active_campaigns(self) -> list[dict]:
|
def get_active_campaigns(self) -> list[dict]:
|
||||||
"""Lee todas las campañas activas desde 'Google Ads Campaigns'."""
|
"""Lee todas las campañas activas desde 'Google Ads Campaigns'."""
|
||||||
@ -27,7 +61,7 @@ class AirtableClient:
|
|||||||
})
|
})
|
||||||
return [c for c in result if c["google_campaign_id"]] # descartar sin ID
|
return [c for c in result if c["google_campaign_id"]] # descartar sin ID
|
||||||
|
|
||||||
def sync_campaigns_from_google_ads(self, google_campaigns: list[dict], monthly_metrics: dict = None) -> dict:
|
def sync_campaigns_from_google_ads(self, google_campaigns: list[dict], monthly_metrics: dict = None, ppl_lookup: dict = None) -> dict:
|
||||||
"""
|
"""
|
||||||
Sincroniza campañas de Google Ads → Airtable.
|
Sincroniza campañas de Google Ads → Airtable.
|
||||||
- Crea las campañas nuevas con el status real de Google Ads.
|
- Crea las campañas nuevas con el status real de Google Ads.
|
||||||
@ -57,11 +91,19 @@ class AirtableClient:
|
|||||||
conv = round(metrics.get("conversions", 0), 2)
|
conv = round(metrics.get("conversions", 0), 2)
|
||||||
cost = round(metrics.get("cost", 0), 2)
|
cost = round(metrics.get("cost", 0), 2)
|
||||||
|
|
||||||
|
# PPL calculado desde CENTROCURSO (CursoID Text viene de Airtable)
|
||||||
|
cursoid_text = str((at_record or {}).get("fields", {}).get("CursoID Text", "")).strip() if at_record else ""
|
||||||
|
ppl = round((ppl_lookup or {}).get(cursoid_text, 0), 2)
|
||||||
|
cpa_max = round(ppl * 0.70, 2)
|
||||||
|
|
||||||
if at_record is None:
|
if at_record is None:
|
||||||
fields = {"Campaign Name": gc["name"], "CampaignID": gid, "Status": at_status}
|
fields = {"Campaign Name": gc["name"], "CampaignID": gid, "Status": at_status}
|
||||||
if monthly_metrics is not None:
|
if monthly_metrics is not None:
|
||||||
fields["ConvMes"] = conv
|
fields["ConvMes"] = conv
|
||||||
fields["CosteMes"] = cost
|
fields["CosteMes"] = cost
|
||||||
|
if ppl_lookup is not None:
|
||||||
|
fields["PPL"] = ppl
|
||||||
|
fields["CPAMax"] = cpa_max
|
||||||
to_create.append(fields)
|
to_create.append(fields)
|
||||||
created.append({"name": gc["name"], "id": gid, "status": at_status})
|
created.append({"name": gc["name"], "id": gid, "status": at_status})
|
||||||
else:
|
else:
|
||||||
@ -84,6 +126,11 @@ class AirtableClient:
|
|||||||
changes["ConvMes"] = conv
|
changes["ConvMes"] = conv
|
||||||
if at_record["fields"].get("CosteMes") != cost:
|
if at_record["fields"].get("CosteMes") != cost:
|
||||||
changes["CosteMes"] = cost
|
changes["CosteMes"] = cost
|
||||||
|
if ppl_lookup is not None:
|
||||||
|
if at_record["fields"].get("PPL") != ppl:
|
||||||
|
changes["PPL"] = ppl
|
||||||
|
if at_record["fields"].get("CPAMax") != cpa_max:
|
||||||
|
changes["CPAMax"] = cpa_max
|
||||||
if changes:
|
if changes:
|
||||||
to_update.append((at_record["id"], changes, gc["name"]))
|
to_update.append((at_record["id"], changes, gc["name"]))
|
||||||
if change_detail:
|
if change_detail:
|
||||||
|
|||||||
4
run.py
4
run.py
@ -54,7 +54,9 @@ def run():
|
|||||||
print("→ Sincronizando campañas desde Google Ads...")
|
print("→ Sincronizando campañas desde Google Ads...")
|
||||||
google_campaigns = gads.get_all_campaigns()
|
google_campaigns = gads.get_all_campaigns()
|
||||||
monthly_metrics = gads.get_monthly_metrics_all()
|
monthly_metrics = gads.get_monthly_metrics_all()
|
||||||
sync_result = at.sync_campaigns_from_google_ads(google_campaigns, monthly_metrics)
|
print(" Calculando PPL desde CENTROCURSO...")
|
||||||
|
ppl_lookup = at.build_ppl_lookup()
|
||||||
|
sync_result = at.sync_campaigns_from_google_ads(google_campaigns, monthly_metrics, ppl_lookup)
|
||||||
if sync_result["created"]:
|
if sync_result["created"]:
|
||||||
print(f" ✅ Campañas nuevas importadas ({len(sync_result['created'])}):")
|
print(f" ✅ Campañas nuevas importadas ({len(sync_result['created'])}):")
|
||||||
for c in sync_result["created"]:
|
for c in sync_result["created"]:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user