from pyairtable import Api from datetime import datetime import config class AirtableClient: def __init__(self): self.api = Api(config.AIRTABLE_TOKEN) self.leads = self.api.table(config.AIRTABLE_BASE_ID, config.LEADS_TABLE) self.campaigns = self.api.table(config.AIRTABLE_BASE_ID, config.CAMPAIGNS_TABLE) def get_active_campaigns(self) -> list[dict]: """Lee todas las campañas activas desde 'Google Ads Campaigns'.""" records = self.campaigns.all(formula="{Status}='Activa'") result = [] for r in records: f = r["fields"] result.append({ "airtable_id": r["id"], "curso": f.get("Campaign Name", "Sin nombre"), "cursoid_text": str(f.get("CursoID Text", "")).strip(), "google_campaign_id": str(f.get("CampaignID", "")).strip(), "ppl": float(f.get("PPL", 0)), "capping_mensual": int(f.get("CapTotalMes", 0)), "cpa_maximo": float(f.get("CPAMax", 0)), "margen_objetivo": float(f.get("MargenObjetivo", 0)), }) 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]) -> dict: """ Sincroniza campañas de Google Ads → Airtable. - Crea las campañas nuevas con el status real de Google Ads. - Actualiza nombre y/o status si han cambiado. Devuelve un resumen con listas de campañas creadas y actualizadas. """ STATUS_MAP = {"ENABLED": "Activa", "PAUSED": "Pausada"} all_records = self.campaigns.all() at_by_gid = { str(r["fields"].get("CampaignID", "")).strip(): r for r in all_records if r["fields"].get("CampaignID") } created = [] updated = [] for gc in google_campaigns: gid = gc["id"] at_status = STATUS_MAP.get(gc["status"], "Pausada") at_record = at_by_gid.get(gid) if at_record is None: self.campaigns.create({ "Campaign Name": gc["name"], "CampaignID": int(gid), "Status": at_status, }) created.append(gc["name"]) else: changes = {} if at_record["fields"].get("Campaign Name") != gc["name"]: changes["Campaign Name"] = gc["name"] if at_record["fields"].get("Status") != at_status: changes["Status"] = at_status if changes: self.campaigns.update(at_record["id"], changes) updated.append(gc["name"]) return {"created": created, "updated": updated} def get_leads_this_month(self, cursoid_text: str) -> int: """ Cuenta todos los leads validados del mes actual para un curso. Filtra por attr_cursoid (que coincide con CursoID Text de la campaña). """ now = datetime.now() mes_inicio = f"{now.year}-{now.month:02d}-01" formula = ( f"AND(" f"{{attr_cursoid}}='{cursoid_text}'," f"{{creado}}>='{mes_inicio}'" f")" ) records = self.leads.all(formula=formula) return len(records)