|
1 |
| -import pandas as pd |
2 |
| -import plotly.express as px |
3 |
| -from preswald import ( |
4 |
| - Workflow, |
5 |
| - get_df, |
6 |
| - text, |
7 |
| - selectbox, |
8 |
| - table, |
9 |
| - progress, |
10 |
| - plotly, |
11 |
| - separator, |
12 |
| - checkbox, |
13 |
| - alert, |
14 |
| -) |
| 1 | +from preswald import Workflow, text |
15 | 2 |
|
16 | 3 | workflow = Workflow()
|
17 | 4 |
|
18 |
| -# 1. Load and normalize data |
19 | 5 | @workflow.atom()
|
20 |
| -def load_data(): |
21 |
| - df = get_df("sample_csv") |
22 |
| - |
23 |
| - # Normalize column names: snake_case, stripped |
24 |
| - df.columns = ( |
25 |
| - df.columns |
26 |
| - .str.strip() |
27 |
| - .str.lower() |
28 |
| - .str.replace(" ", "_") |
29 |
| - .str.replace("%", "", regex=False) |
30 |
| - ) |
31 |
| - |
32 |
| - # Parse progress column if it exists |
33 |
| - if "strategic_goals_progress" in df.columns: |
34 |
| - try: |
35 |
| - df["strategic_goals_progress"] = ( |
36 |
| - df["strategic_goals_progress"] |
37 |
| - .astype(str) |
38 |
| - .str.replace('%', '', regex=False) |
39 |
| - .str.strip() |
40 |
| - .astype(float) |
41 |
| - ) |
42 |
| - except Exception as e: |
43 |
| - alert(f"⚠️ Failed to parse strategic progress: {e}", level="error") |
44 |
| - |
45 |
| - return df |
46 |
| - |
47 |
| -# 2. Quarter selector |
48 |
| -@workflow.atom(dependencies=["load_data"]) |
49 |
| -def quarter_selector(load_data): |
50 |
| - if "quarter" not in load_data.columns: |
51 |
| - alert("❌ Missing 'quarter' column.", level="error") |
52 |
| - return None |
53 |
| - |
54 |
| - quarters = sorted(load_data["quarter"].dropna().unique()) |
55 |
| - return selectbox("📅 Select a Quarter", options=quarters, default=quarters[-1]) |
56 |
| - |
57 |
| -# 3. Filtered snapshot |
58 |
| -@workflow.atom(dependencies=["load_data", "quarter_selector"]) |
59 |
| -def filtered_snapshot(load_data, quarter_selector): |
60 |
| - if quarter_selector is None: |
61 |
| - return pd.DataFrame() |
62 |
| - |
63 |
| - df_q = load_data[load_data["quarter"] == quarter_selector] |
64 |
| - if df_q.empty: |
65 |
| - alert(f"No data found for quarter: {quarter_selector}", level="warning") |
66 |
| - return df_q |
67 |
| - |
68 |
| -# 4. Executive summary |
69 |
| -@workflow.atom(dependencies=["filtered_snapshot"]) |
70 |
| -def executive_summary(filtered_snapshot): |
71 |
| - if filtered_snapshot.empty: |
72 |
| - return |
73 |
| - |
74 |
| - qtr = filtered_snapshot["quarter"].iloc[0] |
75 |
| - avg_rev = filtered_snapshot["revenue"].mean() |
76 |
| - avg_profit = filtered_snapshot["profit"].mean() |
77 |
| - |
78 |
| - text(f"# 📊 Executive Summary — {qtr}") |
79 |
| - text(f"**Average Revenue:** ${avg_rev:,.0f} **Average Profit:** ${avg_profit:,.0f}") |
80 |
| - separator() |
81 |
| - |
82 |
| -# 5. Financial bar chart |
83 |
| -@workflow.atom(dependencies=["filtered_snapshot"]) |
84 |
| -def financial_chart(filtered_snapshot): |
85 |
| - if filtered_snapshot.empty: |
86 |
| - return |
87 |
| - |
88 |
| - text("## 💰 Financial Performance") |
89 |
| - fig = px.bar( |
90 |
| - filtered_snapshot, |
91 |
| - x="quarter", |
92 |
| - y=["revenue", "profit"], |
93 |
| - barmode="group", |
94 |
| - title="Revenue & Profit", |
95 |
| - labels={"value": "USD", "variable": "Metric"}, |
96 |
| - ) |
97 |
| - plotly(fig) |
98 |
| - separator() |
99 |
| - |
100 |
| -# 6. Strategic goals progress |
101 |
| -@workflow.atom(dependencies=["filtered_snapshot"]) |
102 |
| -def strategy_progress(filtered_snapshot): |
103 |
| - if "strategic_goals_progress" not in filtered_snapshot.columns: |
104 |
| - alert("Missing 'strategic_goals_progress' column.", level="info") |
105 |
| - return |
106 |
| - |
107 |
| - text("## 🎯 Strategic Goals Progress") |
108 |
| - for _, row in filtered_snapshot.iterrows(): |
109 |
| - progress(label=f"{row['quarter']}", value=row["strategic_goals_progress"]) |
110 |
| - separator() |
111 |
| - |
112 |
| -# 7. Key initiatives & risks |
113 |
| -@workflow.atom(dependencies=["filtered_snapshot"]) |
114 |
| -def initiatives_summary(filtered_snapshot): |
115 |
| - text("## 📌 Key Initiatives and Risk Factors") |
116 |
| - expected_cols = ["quarter", "key_initiatives_status", "risk_factors"] |
117 |
| - for col in expected_cols: |
118 |
| - if col not in filtered_snapshot.columns: |
119 |
| - alert(f"Column '{col}' is missing from the data.", level="warning") |
120 |
| - return |
121 |
| - |
122 |
| - table(filtered_snapshot[expected_cols]) |
123 |
| - separator() |
124 |
| - |
125 |
| -# 8. Talent & hiring overview |
126 |
| -@workflow.atom(dependencies=["filtered_snapshot"]) |
127 |
| -def hiring_summary(filtered_snapshot): |
128 |
| - text("## 🧑💼 Talent & Hiring Overview") |
129 |
| - |
130 |
| - if "open_positions" in filtered_snapshot.columns: |
131 |
| - avg_open = filtered_snapshot["open_positions"].mean() |
132 |
| - text(f"**Avg. Open Positions:** {avg_open:.1f}") |
133 |
| - |
134 |
| - if "hiring_plan_status" in filtered_snapshot.columns: |
135 |
| - fig = px.bar( |
136 |
| - filtered_snapshot, |
137 |
| - x="quarter", |
138 |
| - y="open_positions", |
139 |
| - color="hiring_plan_status", |
140 |
| - title="Hiring Plan Status by Quarter" |
141 |
| - ) |
142 |
| - plotly(fig) |
143 |
| - else: |
144 |
| - alert("Missing 'open_positions' column.", level="warning") |
145 |
| - |
146 |
| - separator() |
147 |
| - |
148 |
| -# 9. Optional raw table view |
149 |
| -@workflow.atom(dependencies=["filtered_snapshot"]) |
150 |
| -def optional_table(filtered_snapshot): |
151 |
| - if checkbox("🔍 Show raw data for selected quarter"): |
152 |
| - table(filtered_snapshot, title="Raw Data Snapshot") |
153 |
| - |
154 |
| -# Run it! |
| 6 | +def intro(): |
| 7 | + text("# Board Meeting Report") |
| 8 | + text("**Company:** NimbusFlow, Inc. \n**Prepared for:** Board of Directors \n**Purpose:** Quarterly update covering financial results, strategic milestones, customer growth, and operational highlights for Q1.") |
| 9 | + |
| 10 | +@workflow.atom() |
| 11 | +def executive_summary(): |
| 12 | + text("## Executive Summary") |
| 13 | + text(""" |
| 14 | +NimbusFlow, a SaaS platform for automating approval workflows, saw accelerated adoption in Q1, particularly among mid-market and international accounts. With revenue growing faster than expected and churn reaching an all-time low, we enter Q2 with strong momentum. |
| 15 | +""") |
| 16 | + |
| 17 | +@workflow.atom() |
| 18 | +def financial_results(): |
| 19 | + text("## Financial Results") |
| 20 | + text(""" |
| 21 | +- **MRR:** $720K (Q/Q +22%) |
| 22 | +- **ARR:** $8.64M |
| 23 | +- **Gross Margin:** 78% |
| 24 | +- **Operating Expenses:** $1.9M |
| 25 | +- **Cash Position:** $11.2M |
| 26 | +- **Runway:** 18+ months at current burn |
| 27 | + |
| 28 | +We exceeded our MRR forecast by 7% and maintained healthy margins despite increased R&D spending. |
| 29 | +""") |
| 30 | + |
| 31 | +@workflow.atom() |
| 32 | +def strategic_initiatives(): |
| 33 | + text("## Strategic Initiatives") |
| 34 | + text(""" |
| 35 | +Key strategic workstreams advanced this quarter: |
| 36 | + |
| 37 | +- **Workflow Templates Library** – Launched with 40+ plug-and-play use cases. |
| 38 | +- **AI Suggest** – Beta feature that recommends approval paths based on historical patterns. |
| 39 | +- **Data Residency Controls** – Early access enabled for select EU customers ahead of full GDPR compliance rollout in Q2. |
| 40 | +""") |
| 41 | + |
| 42 | +@workflow.atom() |
| 43 | +def product_updates(): |
| 44 | + text("## Product Updates") |
| 45 | + text(""" |
| 46 | +- **New Integrations:** Slack, HubSpot, and Azure AD added to the integration hub. |
| 47 | +- **Admin Console Redesign:** Modern UI with role-based access control and audit logs. |
| 48 | +- **Mobile Enhancements:** Push notifications and offline mode now live for iOS and Android. |
| 49 | + |
| 50 | +Customer feedback on the new features has been positive, especially in high-volume ops teams. |
| 51 | +""") |
| 52 | + |
| 53 | +@workflow.atom() |
| 54 | +def customer_growth(): |
| 55 | + text("## Customer Growth") |
| 56 | + text(""" |
| 57 | +- **New Customers:** 89 logos added in Q1, including FinchBank, CoastalRX, and Alignly. |
| 58 | +- **Churn:** 2.3% (vs. 3.1% last quarter) |
| 59 | +- **Expansion Revenue:** $370K from seat and usage-based upgrades |
| 60 | + |
| 61 | +The SMB segment remains our highest-volume engine, while enterprise accounts drove most upsell revenue. |
| 62 | +""") |
| 63 | + |
| 64 | +@workflow.atom() |
| 65 | +def hiring_and_team(): |
| 66 | + text("## Team & Hiring") |
| 67 | + text(""" |
| 68 | +We added 14 new employees this quarter across: |
| 69 | + |
| 70 | +- **Engineering (7)** – Platform, security, and mobile |
| 71 | +- **Customer Success (3)** – US and EMEA |
| 72 | +- **Go-To-Market (4)** – AE, BDR, and content |
| 73 | + |
| 74 | +Time-to-productivity for new hires is down 20% thanks to an improved onboarding track. |
| 75 | +""") |
| 76 | + |
| 77 | +@workflow.atom() |
| 78 | +def operational_risks(): |
| 79 | + text("## Operational Risks") |
| 80 | + text(""" |
| 81 | +- **Load Spikes in EU Datacenter:** Mitigated by migrating shared tenants to scalable node pools. |
| 82 | +- **Onboarding Drop-off (SMB):** New cohort analysis revealed a sharp decline in activation by day 7. CX is investigating. |
| 83 | +- **Talent Competition:** Backend hiring in SF and NY is slower than forecast. Exploring remote-first roles in LATAM and Canada. |
| 84 | +""") |
| 85 | + |
| 86 | +@workflow.atom() |
| 87 | +def q2_roadmap(): |
| 88 | + text("## Q2 Roadmap Priorities") |
| 89 | + text(""" |
| 90 | +- **General Availability: AI Suggest** |
| 91 | +- **SOC 2 Type II Audit Completion** |
| 92 | +- **Self-Serve Billing & Plan Management** |
| 93 | +- **Partnership Launch with Zenith HRIS** |
| 94 | + |
| 95 | +These efforts align with our goals around automation, compliance, and monetization flexibility. |
| 96 | +""") |
| 97 | + |
| 98 | +@workflow.atom() |
| 99 | +def closing_notes(): |
| 100 | + text("## Closing Remarks") |
| 101 | + text(""" |
| 102 | +NimbusFlow is entering Q2 with clear direction, strong team execution, and continued market pull. As we build on our product and customer base, our focus will remain on scalability, security, and delivering fast value to every team that runs on Nimbus. |
| 103 | +""") |
| 104 | + |
155 | 105 | workflow.execute()
|
0 commit comments