5 Commits

Author SHA1 Message Date
921000c47e Make approval buttons project-aware for the shared Slack app
meta-optimizer (Viviful) and this project share one Slack bot/app, but a
Slack app only supports a single Interactivity Request URL — so a single
approval_server must be able to tell which project's Baserow a button click
belongs to. Button values now carry a project tag ("approve:formacion:<id>"
instead of bare "approve:<id>"); approval_server.py parses it and routes
accordingly, with a registry slot to add other projects (e.g. "viviful") via
their Baserow REST credentials without importing their code.

Confirmed via api.slack.com/apps that the current Interactivity Request URL
points at a dead ngrok tunnel (ERR_NGROK_3200) — no approval server is
actually reachable in production today, for either project. Deployment is
being handled separately as part of the general app rollout.
2026-07-09 15:10:30 +02:00
788c00256f Fix Slack link-unfurl bug and add PPL/Margen to adset table
- Rename table abbreviations that Slack misread as domains: 'CPL.AT' was
  auto-unfurled as a link to the .AT (Austria) TLD, posting an unrelated
  business's ad copy into the channel. Switch '.' to '·' in all L/€/CPL
  abbreviations, and set unfurl_links=False/unfurl_media=False on every
  postMessage as defense in depth.
- Pass ppl/cpa_maximo into adset analysis too (ads already had it) so
  Claude's adset evaluation compares CPL against the course's PPL-derived
  rentability threshold, not just Meta's own bid cost cap.
- Add PPL and Margen columns to the adset numeric table in Slack.
2026-07-09 13:07:37 +02:00
769d86c896 Unified Formación report: leadform+landing leads, AT/Meta daily table, per-curso contrast, strategic diagnosis
- Broaden Airtable lead counting to attr_utm_source IN ('Lead ads','landingpage')
  — the 'landingpage' leads (100% fbclid, 0% gclid) were being missed entirely,
  undercounting real leads for '_web' suffixed campaigns and skewing
  capping/pacing decisions since yesterday's first production run.
- Add airtable_client.get_meta_leads_bulk() for day/curso-level aggregation.
- Drop per-familia Slack sectioning in favor of a single Formación block,
  chunked by campaign batches instead.
- Add daily AT-vs-Meta table, per-curso PPL/CPL contrast table (leadform vs
  landing breakdown), and a Claude-generated portfolio strategic diagnosis
  (ported from leads-optimizer's portfolio_daily_analysis).
- Persist daily aggregate totals to a new Baserow table (daily_metrics) so
  the dashboard and future reports don't depend on Meta's historical API
  access remaining available indefinitely.
- Surface adset/ad-level recommendations in the campaign cards instead of
  only numeric tables.
2026-07-09 11:02:19 +02:00
f8cbd5f29e Fix setup_airtable_meta_tables.py: UTF-8 stdout and idempotent re-runs
Windows console encoding crashed the script right after creating the first
table; make it re-runnable by skipping tables that already exist instead of
aborting entirely.
2026-07-08 19:43:24 +02:00
9239e2f67f Initial scaffold: Meta Optimizer for RoiFormacion campaigns
Ports meta-optimizer's Meta Ads execution/approval/creative-analysis layer
(agent.py, meta_ads_client.py, baserow_client.py, slack_notifier.py,
approval_server.py) and replaces the per-vertical CPL model with the
PPL + monthly-capping-per-course model already used by leads-optimizer,
via a new airtable_client.py that shares Cursos/Familias/CentroCurso/
CursoMes/Leads Lake with that project and adds Meta Ads Campaigns /
MetaCampaignMes alongside its Google Ads Campaigns / GACampaignMes.
2026-07-07 16:53:03 +02:00