Replace aggregate Margen column with per-vertical columns in monthly table

- Track v_margins dict per day in daily_totals (one entry per vertical)
- Monthly table now shows one margin column per vertical instead of aggregate Margen
- Verticals ordered by monthly margin (best first)
- Remove separate vertical breakdown section (now redundant)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jose Manuel 2026-06-13 23:20:50 +02:00
parent ff3aafe82a
commit e4840467c6
3 changed files with 43 additions and 29 deletions

6
run.py
View File

@ -125,10 +125,11 @@ def run():
v = _extract_vertical(row["campaign_name"])
target = vertical_cpls.get(v, config.META_TARGET_CPL)
margin = round((target - row["spend"] / row["leads"]) * row["leads"], 2) if row["leads"] > 0 else round(-row["spend"], 2)
d = _daily.setdefault(row["date"], {"spend": 0.0, "leads": 0, "margin": 0.0})
d = _daily.setdefault(row["date"], {"spend": 0.0, "leads": 0, "margin": 0.0, "v_margins": {}})
d["spend"] += row["spend"]
d["leads"] += row["leads"]
d["margin"] += margin
d["v_margins"][v] = d["v_margins"].get(v, 0.0) + margin
mv = monthly_verticals.setdefault(v, {"spend": 0.0, "leads": 0, "margin": 0.0, "target_cpl": target})
mv["spend"] += row["spend"]
mv["leads"] += row["leads"]
@ -139,7 +140,8 @@ def run():
"spend": round(d["spend"], 2),
"leads": int(d["leads"]),
"cpl": round(d["spend"] / d["leads"], 2) if d["leads"] > 0 else 0.0,
"margin": round(d["margin"], 2),
"margin": round(d["margin"], 2),
"v_margins": {v: round(m, 0) for v, m in d["v_margins"].items()},
}
for date, d in sorted(_daily.items())
]

View File

@ -48,10 +48,11 @@ def main():
round((target - row["spend"] / row["leads"]) * row["leads"], 2)
if row["leads"] > 0 else round(-row["spend"], 2)
)
d = _daily.setdefault(row["date"], {"spend": 0.0, "leads": 0, "margin": 0.0})
d = _daily.setdefault(row["date"], {"spend": 0.0, "leads": 0, "margin": 0.0, "v_margins": {}})
d["spend"] += row["spend"]
d["leads"] += row["leads"]
d["margin"] += margin
d["v_margins"][v] = d["v_margins"].get(v, 0.0) + margin
mv = monthly_verticals.setdefault(v, {"spend": 0.0, "leads": 0, "margin": 0.0, "target_cpl": target})
mv["spend"] += row["spend"]
mv["leads"] += row["leads"]
@ -62,7 +63,8 @@ def main():
"spend": round(d["spend"], 2),
"leads": int(d["leads"]),
"cpl": round(d["spend"] / d["leads"], 2) if d["leads"] > 0 else 0.0,
"margin": round(d["margin"], 2),
"margin": round(d["margin"], 2),
"v_margins": {v: round(m, 0) for v, m in d["v_margins"].items()},
}
for date, d in sorted(_daily.items())
]

View File

@ -171,40 +171,50 @@ def send_daily_report(
},
]
# ── Rentabilidad mensual con margen ───────────────────────────────────────
# ── Rentabilidad mensual con margen por vertical ─────────────────────────
if daily_totals:
lines = [f"{'Día':<5} {'Gasto':>7} {'Leads':>5} {'CPL':>7} {'Margen':>8} Est"]
lines.append("" * 42)
# Orden de verticales: por margen mensual acumulado (mejor primero)
v_order = (
sorted(monthly_verticals.keys(), key=lambda v: -monthly_verticals[v]["margin"])
if monthly_verticals else []
)
cw = 7 # ancho de cada columna de vertical
# Cabecera
header = f"{'Día':<5} {'Gasto':>6} {'Leads':>5} {'CPL':>7}"
for v in v_order:
header += f" {v[:6]:>{cw}}"
header += " Est"
sep = "" * len(header)
lines = [header, sep]
total_spend = total_leads = total_margin = 0.0
total_v = {v: 0.0 for v in v_order}
for d in daily_totals:
day = d["date"][8:10] + "/" + d["date"][5:7]
margin = d.get("margin", 0.0)
total_spend += d["spend"]
total_leads += d["leads"]
total_margin += margin
v_day = d.get("v_margins", {})
icon = "" if d["leads"] > 0 else ("" if d["spend"] > 0 else "")
m_sign = f"+{margin:.0f}" if margin >= 0 else f"{margin:.0f}"
lines.append(
f"{day:<5} {d['spend']:>6.0f}{d['leads']:>5} {d['cpl']:>6.2f}{m_sign:>8} {icon}"
)
lines.append("" * 42)
total_cpl = round(total_spend / total_leads, 2) if total_leads > 0 else 0.0
m_tot_sign = f"+{total_margin:.0f}" if total_margin >= 0 else f"{total_margin:.0f}"
lines.append(
f"{'TOTAL':<5} {total_spend:>6.0f}{int(total_leads):>5} {total_cpl:>6.2f}{m_tot_sign:>8}"
)
# Margen mensual por vertical
if monthly_verticals:
mv_lines = ["", f"{'Vertical':<16} {'Gasto':>7} {'Leads':>5} {'CPL':>7} {'Obj':>5} {'Margen':>8}"]
mv_lines.append("" * 54)
for v, mv in sorted(monthly_verticals.items(), key=lambda x: -x[1]["margin"]):
v_cpl = round(mv["spend"] / mv["leads"], 2) if mv["leads"] > 0 else 0.0
m_sign = f"+{mv['margin']:.0f}" if mv["margin"] >= 0 else f"{mv['margin']:.0f}"
obj = mv.get("target_cpl", 0)
mv_lines.append(
f"{v:<16} {mv['spend']:>6.0f}{mv['leads']:>5} {v_cpl:>6.2f}{obj:>4.1f}{m_sign:>8}"
)
lines += mv_lines
row = f"{day:<5} {d['spend']:>5.0f}{d['leads']:>5} {d['cpl']:>6.2f}"
for v in v_order:
vm = v_day.get(v, 0.0)
total_v[v] += vm
vm_s = (f"+{vm:.0f}" if vm >= 0 else f"{vm:.0f}") if round(vm) != 0 else ""
row += f" {vm_s:>{cw}}"
row += f" {icon}"
lines.append(row)
lines.append(sep)
total_row = f"{'TOTAL':<5} {total_spend:>5.0f}{int(total_leads):>5} {'':>7}"
for v in v_order:
tv = total_v[v]
tv_s = f"+{tv:.0f}" if tv >= 0 else f"{tv:.0f}"
total_row += f" {tv_s:>{cw}}"
lines.append(total_row)
blocks.append({
"type": "section",