system-prompts-and-models-o.../dealix/auto_client_acquisition/customer_success/qbr_generator.py
2026-05-01 14:03:52 +03:00

258 lines
9.1 KiB
Python

"""
Quarterly Business Review (QBR) Generator.
Composes a monthly/quarterly executive summary per customer pulling from:
- EmailSendLog metrics (sent / replied / bounced)
- GmailDraftRecord + LinkedInDraftRecord counts
- LeadScoreRecord priority distribution
- Customer health score
- Suppression activity (proof of compliance)
Output: structured dict ready for:
- markdown export (for email to customer)
- PowerPoint generation (PPTX skill — future)
- dashboard rendering
Pure-function — takes pre-fetched data, computes the brief.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass, field
from datetime import datetime, timedelta, timezone
from typing import Any
@dataclass
class QBRSection:
title: str
bullets: list[str] = field(default_factory=list)
metrics: dict[str, Any] = field(default_factory=dict)
@dataclass
class QBRReport:
customer_id: str
customer_name: str
period_start: str
period_end: str
period_days: int
health_overall: float
health_bucket: str
# Executive summary
headline_metric: str
headline_delta: str
# Sections
sections: list[QBRSection] = field(default_factory=list)
# Recommendations
next_quarter_focus: list[str] = field(default_factory=list)
upsell_opportunities: list[str] = field(default_factory=list)
# Generated_at
generated_at: str = ""
def to_dict(self) -> dict[str, Any]:
return {
**{k: v for k, v in asdict(self).items() if k != "sections"},
"sections": [asdict(s) for s in self.sections],
}
def to_markdown(self) -> str:
"""Render as markdown for email send."""
lines = [
f"# QBR — {self.customer_name}",
f"**Period:** {self.period_start}{self.period_end} ({self.period_days} days)",
f"**Health Score:** {self.health_overall}/100 ({self.health_bucket})",
"",
f"## 🎯 Headline",
f"**{self.headline_metric}** — {self.headline_delta}",
"",
]
for section in self.sections:
lines.append(f"## {section.title}")
for b in section.bullets:
lines.append(f"- {b}")
if section.metrics:
lines.append("")
for k, v in section.metrics.items():
lines.append(f" - `{k}`: {v}")
lines.append("")
if self.next_quarter_focus:
lines.append("## 🚀 Next Quarter Focus")
for f in self.next_quarter_focus:
lines.append(f"- {f}")
lines.append("")
if self.upsell_opportunities:
lines.append("## 💎 Upsell Opportunities")
for u in self.upsell_opportunities:
lines.append(f"- {u}")
lines.append("")
lines.append(f"_Generated by Dealix at {self.generated_at}_")
return "\n".join(lines)
def generate_qbr(
*,
customer_id: str,
customer_name: str,
period_days: int = 30,
# Outreach metrics
emails_sent: int = 0,
emails_replied: int = 0,
emails_bounced: int = 0,
drafts_created: int = 0,
drafts_sent: int = 0,
linkedin_drafts: int = 0,
linkedin_sent: int = 0,
# Pipeline metrics
new_leads: int = 0,
qualified_leads: int = 0,
demos_booked: int = 0,
deals_won: int = 0,
pipeline_value_sar: float = 0,
closed_revenue_sar: float = 0,
# Compliance
suppression_added: int = 0,
opt_outs_received: int = 0,
# Health
health_overall: float = 70,
health_bucket: str = "stable",
# Top performers
best_sector: str | None = None,
best_message_angle: str | None = None,
# Plan
current_plan: str = "Growth",
) -> QBRReport:
"""Compose QBR from raw metrics."""
now = datetime.now(timezone.utc)
period_start = (now - timedelta(days=period_days)).date().isoformat()
period_end = now.date().isoformat()
# Headline = highest-impact metric
if deals_won > 0:
headline_metric = f"Closed {deals_won} deals worth {closed_revenue_sar:,.0f} SAR"
headline_delta = f"via Dealix-generated pipeline"
elif demos_booked > 0:
headline_metric = f"{demos_booked} demos booked from {emails_sent + linkedin_sent} outreaches"
ratio = (demos_booked / max(1, emails_sent + linkedin_sent)) * 100
headline_delta = f"{ratio:.1f}% conversion to demo"
elif emails_replied > 0:
headline_metric = f"{emails_replied} replies on {emails_sent} sends"
rate = (emails_replied / max(1, emails_sent)) * 100
headline_delta = f"{rate:.1f}% reply rate"
else:
headline_metric = f"{drafts_created} personalized drafts generated"
headline_delta = "Pipeline being built — first conversions expected next period"
sections: list[QBRSection] = []
# Section 1: Outreach activity
reply_rate = (emails_replied / max(1, emails_sent)) * 100
bounce_rate = (emails_bounced / max(1, emails_sent)) * 100
sections.append(QBRSection(
title="📨 Outreach Activity",
bullets=[
f"{drafts_created} drafts generated, {drafts_sent} sent ({drafts_sent/max(1,drafts_created)*100:.0f}% approval rate)",
f"{emails_sent} emails reached inboxes — {reply_rate:.1f}% reply rate",
f"{linkedin_drafts} LinkedIn drafts; {linkedin_sent} sent manually",
f"Bounce rate: {bounce_rate:.1f}% (target < 5%)",
],
metrics={
"drafts_created": drafts_created, "drafts_sent": drafts_sent,
"emails_sent": emails_sent, "emails_replied": emails_replied,
"linkedin_drafts": linkedin_drafts, "linkedin_sent": linkedin_sent,
"reply_rate_pct": round(reply_rate, 2),
"bounce_rate_pct": round(bounce_rate, 2),
},
))
# Section 2: Pipeline impact
sections.append(QBRSection(
title="📈 Pipeline Impact",
bullets=[
f"{new_leads} new leads ingested",
f"{qualified_leads} qualified by Dealix scoring",
f"{demos_booked} demos booked",
f"{deals_won} deals closed — {closed_revenue_sar:,.0f} SAR revenue",
f"Open pipeline: {pipeline_value_sar:,.0f} SAR",
],
metrics={
"new_leads": new_leads, "qualified": qualified_leads,
"demos": demos_booked, "deals_won": deals_won,
"pipeline_sar": pipeline_value_sar,
"closed_sar": closed_revenue_sar,
},
))
# Section 3: Compliance
sections.append(QBRSection(
title="🛡️ Compliance Health",
bullets=[
f"{suppression_added} new suppression entries added",
f"{opt_outs_received} opt-outs honored automatically (RFC 8058 + STOP)",
f"All sends passed 11-gate compliance check",
f"Audit log: 100% complete",
],
metrics={
"suppression_added": suppression_added,
"opt_outs": opt_outs_received,
},
))
# Section 4: Top performers
top_bullets = []
if best_sector:
top_bullets.append(f"Best sector: **{best_sector}**")
if best_message_angle:
top_bullets.append(f"Best message angle: **{best_message_angle}**")
if top_bullets:
sections.append(QBRSection(
title="🏆 What Worked",
bullets=top_bullets,
))
# Recommendations
next_focus: list[str] = []
if reply_rate < 3 and emails_sent >= 50:
next_focus.append("Iterate subject lines — current reply rate below 3%")
if bounce_rate > 8:
next_focus.append("Audit data sources — bounce rate >8% threatens Gmail reputation")
if demos_booked == 0 and emails_replied >= 5:
next_focus.append("Review reply-to-demo conversion — leads engaging but not converting")
if drafts_created < period_days:
next_focus.append("Increase daily run cadence — currently below 1 draft/day")
if not next_focus:
next_focus.append("Maintain current cadence + run /score-tuner monthly")
upsell: list[str] = []
if current_plan == "Starter" and emails_sent >= 400:
upsell.append("Approaching Starter cap — Growth tier 2,500 lead/mo at 2,999 SAR")
if current_plan in {"Starter", "Growth"} and deals_won >= 3:
upsell.append("Strong outcomes — Scale tier unlocks API + dedicated AM at 7,999 SAR")
if best_sector and demos_booked >= 5:
upsell.append(f"Best-sector ({best_sector}) showing strong ROI — consider expanding to adjacent sectors")
if health_bucket == "healthy" and deals_won >= 1:
upsell.append("Strong NPS candidate → request testimonial + 10% referral kickback")
return QBRReport(
customer_id=customer_id,
customer_name=customer_name,
period_start=period_start,
period_end=period_end,
period_days=period_days,
health_overall=health_overall,
health_bucket=health_bucket,
headline_metric=headline_metric,
headline_delta=headline_delta,
sections=sections,
next_quarter_focus=next_focus,
upsell_opportunities=upsell,
generated_at=now.isoformat(),
)