#!/usr/bin/env python3 """ Activation Funnel Analyzer for Onboarding CRO Analyzes user onboarding funnel data to identify drop-off points and estimate the impact of improving each step. Usage: python3 activation_funnel_analyzer.py # Demo mode python3 activation_funnel_analyzer.py funnel.json # From data python3 activation_funnel_analyzer.py funnel.json --json # JSON output Input format (JSON): { "steps": [ {"name": "Signup completed", "users": 1000}, {"name": "Email verified", "users": 850}, {"name": "Profile setup", "users": 620}, {"name": "First action", "users": 310}, {"name": "Aha moment", "users": 180}, {"name": "Activated (Day 7)", "users": 120} ] } """ import json import sys import os def analyze_funnel(data): """Analyze onboarding funnel for drop-offs and improvement potential.""" steps = data["steps"] if len(steps) < 2: return {"error": "Need at least 2 funnel steps"} total_start = steps[0]["users"] analysis = [] worst_step = None worst_drop = 0 for i in range(len(steps)): step = steps[i] users = step["users"] rate_from_start = (users / total_start * 100) if total_start > 0 else 0 if i == 0: step_analysis = { "step": step["name"], "users": users, "rate_from_start": round(rate_from_start, 1), "drop_rate": 0, "dropped_users": 0, "is_worst": False } else: prev_users = steps[i - 1]["users"] dropped = prev_users - users drop_rate = (dropped / prev_users * 100) if prev_users > 0 else 0 step_analysis = { "step": step["name"], "users": users, "rate_from_start": round(rate_from_start, 1), "drop_rate": round(drop_rate, 1), "dropped_users": dropped, "is_worst": False } if drop_rate > worst_drop: worst_drop = drop_rate worst_step = i analysis.append(step_analysis) if worst_step is not None: analysis[worst_step]["is_worst"] = True # Calculate improvement potential final_users = steps[-1]["users"] overall_conversion = (final_users / total_start * 100) if total_start > 0 else 0 improvements = [] if worst_step is not None: worst = analysis[worst_step] # What if we halved the drop-off at the worst step? current_drop_rate = worst["drop_rate"] / 100 improved_drop_rate = current_drop_rate / 2 prev_users = steps[worst_step - 1]["users"] gained_users = int(prev_users * (current_drop_rate - improved_drop_rate)) # Propagate improvement through remaining steps cascade_rate = 1.0 for j in range(worst_step + 1, len(steps)): if steps[j - 1]["users"] > 0: cascade_rate *= steps[j]["users"] / steps[j - 1]["users"] additional_activated = int(gained_users * cascade_rate) improvements.append({ "action": f"Halve drop-off at '{worst['step']}'", "current_drop": f"{worst['drop_rate']}%", "target_drop": f"{worst['drop_rate'] / 2:.1f}%", "users_saved": gained_users, "additional_activated": additional_activated, "impact_on_overall": f"+{(additional_activated / total_start * 100):.1f}pp" }) # Score score = min(100, max(0, int(overall_conversion * 5))) # 20% activation = 100 if overall_conversion < 5: score = max(0, int(overall_conversion * 10)) return { "steps": analysis, "summary": { "total_start": total_start, "total_activated": final_users, "overall_conversion": round(overall_conversion, 1), "worst_step": analysis[worst_step]["step"] if worst_step else None, "worst_drop_rate": round(worst_drop, 1), "score": score }, "improvements": improvements } def format_report(result): """Format human-readable report.""" lines = [] lines.append("") lines.append("=" * 65) lines.append(" ONBOARDING FUNNEL — ACTIVATION ANALYSIS") lines.append("=" * 65) lines.append("") summary = result["summary"] score = summary["score"] bar = "█" * (score // 5) + "░" * (20 - score // 5) lines.append(f" ACTIVATION SCORE: {score}/100") lines.append(f" [{bar}]") lines.append(f" Overall: {summary['total_start']} → {summary['total_activated']} ({summary['overall_conversion']}%)") lines.append("") # Funnel visualization lines.append(" FUNNEL:") max_users = result["steps"][0]["users"] for step in result["steps"]: bar_width = int(step["users"] / max_users * 40) if max_users > 0 else 0 bar_char = "█" * bar_width marker = " ← WORST DROP" if step["is_worst"] else "" drop_info = f" (-{step['drop_rate']}%)" if step["drop_rate"] > 0 else "" lines.append(f" {bar_char} {step['users']:>5} | {step['step']}{drop_info}{marker}") lines.append("") # Step-by-step breakdown lines.append(" STEP BREAKDOWN:") lines.append(f" {'Step':<25} {'Users':>7} {'From Start':>12} {'Drop':>8} {'Lost':>7}") lines.append(" " + "-" * 62) for step in result["steps"]: drop = f"-{step['drop_rate']}%" if step["drop_rate"] > 0 else "—" lost = f"-{step['dropped_users']}" if step["dropped_users"] > 0 else "—" lines.append(f" {step['step']:<25} {step['users']:>7} {step['rate_from_start']:>10.1f}% {drop:>8} {lost:>7}") lines.append("") # Improvement potential if result["improvements"]: lines.append(" 💡 IMPROVEMENT POTENTIAL:") for imp in result["improvements"]: lines.append(f" Action: {imp['action']}") lines.append(f" Drop: {imp['current_drop']} → {imp['target_drop']}") lines.append(f" Users saved at step: +{imp['users_saved']}") lines.append(f" Additional activated: +{imp['additional_activated']}") lines.append(f" Impact on overall rate: {imp['impact_on_overall']}") lines.append("") return "\n".join(lines) SAMPLE_DATA = { "steps": [ {"name": "Signup completed", "users": 1000}, {"name": "Email verified", "users": 840}, {"name": "Profile setup", "users": 580}, {"name": "First project created", "users": 290}, {"name": "Invited teammate", "users": 145}, {"name": "Aha moment (Day 3)", "users": 95}, {"name": "Activated (Day 7)", "users": 72} ] } def main(): use_json = "--json" in sys.argv args = [a for a in sys.argv[1:] if a != "--json"] if args and os.path.isfile(args[0]): with open(args[0]) as f: data = json.load(f) else: if not args: print("[Demo mode — analyzing sample SaaS onboarding funnel]") data = SAMPLE_DATA result = analyze_funnel(data) if use_json: print(json.dumps(result, indent=2)) else: print(format_report(result)) if __name__ == "__main__": main()