Delete app.py
Browse files
app.py
DELETED
@@ -1,1676 +0,0 @@
|
|
1 |
-
|
2 |
-
# Initialize form values based on selected template
|
3 |
-
if selected_template and selected_template != "Custom Scenario":
|
4 |
-
new_hires = decision_templates[selected_template]["new_hires"]
|
5 |
-
new_marketing = decision_templates[selected_template]["new_marketing"]
|
6 |
-
other_expenses = decision_templates[selected_template]["other_expenses"]
|
7 |
-
growth_impact = decision_templates[selected_template]["growth_impact"]
|
8 |
-
question = decision_templates[selected_template]["question"]
|
9 |
-
else:
|
10 |
-
new_hires = 0
|
11 |
-
new_marketing = 0
|
12 |
-
other_expenses = 0
|
13 |
-
growth_impact = 0.0
|
14 |
-
question = ""
|
15 |
-
|
16 |
-
# Decision input form
|
17 |
-
with st.form("decision_form"):
|
18 |
-
st.subheader("Scenario Parameters")
|
19 |
-
|
20 |
-
col1, col2 = st.columns(2)
|
21 |
-
|
22 |
-
with col1:
|
23 |
-
new_hires = st.number_input("New Engineering Hires", min_value=0, max_value=10, value=new_hires,
|
24 |
-
help=f"Each engineer costs ${ENGINEER_SALARY:,} per month")
|
25 |
-
st.caption(f"Monthly Cost: ${new_hires * ENGINEER_SALARY:,}")
|
26 |
-
|
27 |
-
new_marketing = st.number_input("Additional Monthly Marketing Budget",
|
28 |
-
min_value=0, max_value=50000, value=new_marketing, step=1000,
|
29 |
-
help="Additional marketing spend per month")
|
30 |
-
|
31 |
-
with col2:
|
32 |
-
other_expenses = st.number_input("Other Additional Monthly Expenses",
|
33 |
-
min_value=0, max_value=50000, value=other_expenses, step=1000,
|
34 |
-
help="Any other additional monthly expenses")
|
35 |
-
|
36 |
-
growth_impact = st.slider("Estimated Impact on Monthly Growth Rate",
|
37 |
-
min_value=0.0, max_value=0.10, value=growth_impact, step=0.01,
|
38 |
-
format="%.2f",
|
39 |
-
help="Estimated increase in monthly growth rate due to these investments")
|
40 |
-
st.caption(f"New Growth Rate: {(startup_data['growth_rate'] + growth_impact) * 100:.1f}% (current: {startup_data['growth_rate'] * 100:.1f}%)")
|
41 |
-
|
42 |
-
question = st.text_area("Describe your decision scenario",
|
43 |
-
value=question,
|
44 |
-
height=100,
|
45 |
-
placeholder="E.g., We're considering hiring two more engineers and increasing our marketing budget...")
|
46 |
-
|
47 |
-
decision_summary = f"""
|
48 |
-
- {new_hires} new engineers: ${new_hires * ENGINEER_SALARY:,}/month
|
49 |
-
- Marketing increase: ${new_marketing:,}/month
|
50 |
-
- Other expenses: ${other_expenses:,}/month
|
51 |
-
- Total additional burn: ${new_hires * ENGINEER_SALARY + new_marketing + other_expenses:,}/month
|
52 |
-
- Growth impact: +{growth_impact * 100:.1f}% monthly growth
|
53 |
-
"""
|
54 |
-
|
55 |
-
st.markdown(f"**Decision Summary:**\n{decision_summary}")
|
56 |
-
|
57 |
-
submitted = st.form_submit_button("Simulate Decision")
|
58 |
-
|
59 |
-
if submitted:
|
60 |
-
# Calculate current and new runway
|
61 |
-
current_runway, new_runway, current_df, new_df = simulate_decision(
|
62 |
-
startup_data['cash'],
|
63 |
-
startup_data['burn_rate'],
|
64 |
-
startup_data['revenue'],
|
65 |
-
startup_data['growth_rate'],
|
66 |
-
other_expenses,
|
67 |
-
new_hires,
|
68 |
-
new_marketing,
|
69 |
-
growth_impact
|
70 |
-
)
|
71 |
-
|
72 |
-
# Display results
|
73 |
-
st.markdown("<h3>Decision Impact Analysis</h3>", unsafe_allow_html=True)
|
74 |
-
|
75 |
-
# Summary metrics
|
76 |
-
col1, col2, col3 = st.columns(3)
|
77 |
-
|
78 |
-
with col1:
|
79 |
-
st.metric("Current Runway", f"{current_runway} months")
|
80 |
-
with col2:
|
81 |
-
runway_change = new_runway - current_runway
|
82 |
-
st.metric("New Runway", f"{new_runway} months",
|
83 |
-
delta=f"{runway_change} months",
|
84 |
-
delta_color="off" if runway_change == 0 else ("normal" if runway_change > 0 else "inverse"))
|
85 |
-
with col3:
|
86 |
-
new_burn = startup_data['burn_rate'] + other_expenses + (new_hires * ENGINEER_SALARY) + new_marketing
|
87 |
-
burn_change = new_burn - startup_data['burn_rate']
|
88 |
-
burn_percentage = burn_change / startup_data['burn_rate'] * 100
|
89 |
-
st.metric("New Monthly Burn", f"${new_burn:,}",
|
90 |
-
delta=f"${burn_change:,} ({burn_percentage:.1f}%)",
|
91 |
-
delta_color="inverse")
|
92 |
-
|
93 |
-
# Cash projection comparison
|
94 |
-
st.subheader("Cash Projection Comparison")
|
95 |
-
|
96 |
-
# Combine dataframes for comparison
|
97 |
-
current_df['Scenario'] = 'Current'
|
98 |
-
new_df['Scenario'] = 'After Decision'
|
99 |
-
|
100 |
-
combined_df = pd.concat([current_df, new_df])
|
101 |
-
combined_df = combined_df.reset_index()
|
102 |
-
combined_df = combined_df.rename(columns={'index': 'Date'})
|
103 |
-
|
104 |
-
# Plot comparison
|
105 |
-
fig = px.line(combined_df, x='Date', y='Cumulative_Cash', color='Scenario',
|
106 |
-
title="Cash Runway Comparison",
|
107 |
-
labels={'Cumulative_Cash': 'Remaining Cash'},
|
108 |
-
color_discrete_sequence=['#4c78a8', '#f58518'])
|
109 |
-
|
110 |
-
fig.add_hline(y=0, line_dash="dash", line_color="red", annotation_text="Out of Cash")
|
111 |
-
|
112 |
-
fig.update_layout(
|
113 |
-
height=400,
|
114 |
-
plot_bgcolor='rgba(240,247,255,0.8)',
|
115 |
-
xaxis_title="Date",
|
116 |
-
yaxis_title="Cash Balance ($)",
|
117 |
-
font=dict(family="Arial, sans-serif", size=12),
|
118 |
-
margin=dict(l=20, r=20, t=40, b=20),
|
119 |
-
)
|
120 |
-
|
121 |
-
st.plotly_chart(fig, use_container_width=True)
|
122 |
-
|
123 |
-
# Get AI analysis
|
124 |
-
if question:
|
125 |
-
decision_params = {
|
126 |
-
"new_hires": new_hires,
|
127 |
-
"new_marketing": new_marketing,
|
128 |
-
"other_expenses": other_expenses,
|
129 |
-
"growth_impact": growth_impact
|
130 |
-
}
|
131 |
-
|
132 |
-
analysis_key = f"decision_analysis_{new_hires}_{new_marketing}_{other_expenses}_{growth_impact}"
|
133 |
-
if analysis_key not in st.session_state.insights_cache:
|
134 |
-
analysis = generate_ai_response(f"""
|
135 |
-
You are a financial advisor for startups. A founder asks:
|
136 |
-
"{question}"
|
137 |
-
|
138 |
-
Here's their current financial situation:
|
139 |
-
- Current cash: ${startup_data['cash']}
|
140 |
-
- Monthly burn rate: ${startup_data['burn_rate']}
|
141 |
-
- Monthly revenue: ${startup_data['revenue']}
|
142 |
-
- Monthly growth rate: {startup_data['growth_rate'] * 100}%
|
143 |
-
|
144 |
-
They're considering these changes:
|
145 |
-
- Adding {decision_params['new_hires']} new engineers (${ENGINEER_SALARY}/month each)
|
146 |
-
- Increasing marketing budget by ${decision_params['new_marketing']}/month
|
147 |
-
- Adding ${decision_params['other_expenses']}/month in other expenses
|
148 |
-
- Expecting {decision_params['growth_impact'] * 100}% additional monthly growth
|
149 |
-
|
150 |
-
Analyze this decision thoroughly:
|
151 |
-
1. Quantify the impact on runway (exact calculation)
|
152 |
-
2. Assess the risk level (low, medium, high)
|
153 |
-
3. Compare the ROI potential
|
154 |
-
4. Provide 3 specific recommendations or alternatives
|
155 |
-
5. Suggest timeline and milestones for implementation if approved
|
156 |
-
|
157 |
-
Be direct and specific with numbers and timeframes.
|
158 |
-
""")
|
159 |
-
st.session_state.insights_cache[analysis_key] = analysis
|
160 |
-
|
161 |
-
st.markdown("<div class='advisor-card'>", unsafe_allow_html=True)
|
162 |
-
st.markdown("<span class='ai-badge'>AI Decision Analysis</span>", unsafe_allow_html=True)
|
163 |
-
st.markdown(f"<p class='advice-text'>{st.session_state.insights_cache[analysis_key]}</p>", unsafe_allow_html=True)
|
164 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
165 |
-
|
166 |
-
# Risk assessment
|
167 |
-
risk_level = "High" if new_runway < 3 else ("Medium" if new_runway < 6 else "Low")
|
168 |
-
risk_color = "danger-metric" if risk_level == "High" else ("warning-metric" if risk_level == "Medium" else "good-metric")
|
169 |
-
|
170 |
-
st.markdown(f"""
|
171 |
-
<div class='metric-card'>
|
172 |
-
<p class='metric-label'>Risk Assessment</p>
|
173 |
-
<p class='metric-value {risk_color}'>{risk_level} Risk Decision</p>
|
174 |
-
<p>This decision would give you {new_runway} months of runway.</p>
|
175 |
-
</div>
|
176 |
-
""", unsafe_allow_html=True)
|
177 |
-
|
178 |
-
# Render Fund Monitoring page
|
179 |
-
def render_fund_monitoring():
|
180 |
-
"""Render the AI-powered fund monitoring page"""
|
181 |
-
if not st.session_state.current_startup or st.session_state.current_startup not in st.session_state.startups:
|
182 |
-
st.warning("No startup selected. Please upload data or select a sample startup.")
|
183 |
-
render_upload_page()
|
184 |
-
return
|
185 |
-
|
186 |
-
# Get the selected startup data
|
187 |
-
transactions_df = st.session_state.startups[st.session_state.current_startup]['transactions']
|
188 |
-
|
189 |
-
st.markdown("<h1 class='main-header'>Investor Fund Monitoring</h1>", unsafe_allow_html=True)
|
190 |
-
st.markdown("<p class='sub-header'>AI-powered fraud detection and spending analysis</p>", unsafe_allow_html=True)
|
191 |
-
|
192 |
-
# How AI helps with fund monitoring
|
193 |
-
with st.expander("ℹ️ How AI enhances fund monitoring"):
|
194 |
-
st.markdown("""
|
195 |
-
### How AI Powers Your Fund Monitoring
|
196 |
-
|
197 |
-
The fund monitoring system uses AI to help maintain investor trust and optimize spending:
|
198 |
-
|
199 |
-
- **Anomaly Detection**: Our AI models identify unusual transactions that don't match typical startup spending patterns
|
200 |
-
- **Risk Scoring**: Each transaction is assigned a risk score based on multiple factors like amount, category, vendor, and description
|
201 |
-
- **Pattern Recognition**: The system identifies potentially concerning spending trends across categories over time
|
202 |
-
- **Fraud Prevention**: AI algorithms flag transactions that match known patterns of misuse before they become issues
|
203 |
-
- **Investor-Ready Reporting**: Generate reports that demonstrate responsible financial stewardship to investors
|
204 |
-
|
205 |
-
This helps founders maintain investor trust, prevent misuse of funds, and create transparency in financial operations.
|
206 |
-
""")
|
207 |
-
|
208 |
-
st.write("Monitor your startup's spending to maintain investor trust and ensure proper fund usage. Our AI algorithms automatically flag suspicious transactions and identify spending patterns.")
|
209 |
-
|
210 |
-
# AI insights for fund monitoring
|
211 |
-
insights_key = f"fund_monitoring_{date.today().isoformat()}"
|
212 |
-
if insights_key not in st.session_state.insights_cache:
|
213 |
-
insights = generate_ai_response("""
|
214 |
-
You are a financial fraud detection expert. Provide 2-3 critical spending patterns that investors typically look for when monitoring startup fund usage.
|
215 |
-
Format as brief bullet points focused on maintaining investor trust.
|
216 |
-
""")
|
217 |
-
st.session_state.insights_cache[insights_key] = insights
|
218 |
-
|
219 |
-
with st.expander("🔍 AI Monitoring Insights", expanded=True):
|
220 |
-
st.markdown("<span class='ai-badge'>AI-Generated Insights</span>", unsafe_allow_html=True)
|
221 |
-
st.markdown(st.session_state.insights_cache[insights_key])
|
222 |
-
|
223 |
-
# Process transactions to detect suspicious ones with AI enhancement
|
224 |
-
processed_df = detect_suspicious_transactions(transactions_df)
|
225 |
-
|
226 |
-
# Summary metrics
|
227 |
-
total_transactions = len(processed_df)
|
228 |
-
suspicious_transactions = processed_df[processed_df['Suspicious']].copy()
|
229 |
-
suspicious_count = len(suspicious_transactions)
|
230 |
-
suspicious_amount = suspicious_transactions['Amount'].sum()
|
231 |
-
total_amount = processed_df['Amount'].sum()
|
232 |
-
|
233 |
-
col1, col2, col3, col4 = st.columns(4)
|
234 |
-
|
235 |
-
with col1:
|
236 |
-
st.markdown(f"""
|
237 |
-
<div class='metric-card'>
|
238 |
-
<p class='metric-label'>Total Transactions</p>
|
239 |
-
<p class='metric-value'>{total_transactions}</p>
|
240 |
-
</div>
|
241 |
-
""", unsafe_allow_html=True)
|
242 |
-
|
243 |
-
with col2:
|
244 |
-
flagged_percent = suspicious_count/total_transactions*100 if total_transactions > 0 else 0
|
245 |
-
status = "danger-metric" if flagged_percent > 10 else ("warning-metric" if flagged_percent > 5 else "good-metric")
|
246 |
-
st.markdown(f"""
|
247 |
-
<div class='metric-card'>
|
248 |
-
<p class='metric-label'>Flagged Transactions</p>
|
249 |
-
<p class='metric-value {status}'>{suspicious_count} ({flagged_percent:.1f}%)</p>
|
250 |
-
</div>
|
251 |
-
""", unsafe_allow_html=True)
|
252 |
-
|
253 |
-
with col3:
|
254 |
-
amount_percent = suspicious_amount/total_amount*100 if total_amount > 0 else 0
|
255 |
-
status = "danger-metric" if amount_percent > 15 else ("warning-metric" if amount_percent > 7 else "good-metric")
|
256 |
-
st.markdown(f"""
|
257 |
-
<div class='metric-card'>
|
258 |
-
<p class='metric-label'>Flagged Amount</p>
|
259 |
-
<p class='metric-value {status}'>${suspicious_amount:,.0f} ({amount_percent:.1f}%)</p>
|
260 |
-
</div>
|
261 |
-
""", unsafe_allow_html=True)
|
262 |
-
|
263 |
-
with col4:
|
264 |
-
avg_risk = suspicious_transactions['Risk_Score'].mean() if not suspicious_transactions.empty else 0
|
265 |
-
status = "danger-metric" if avg_risk > 50 else ("warning-metric" if avg_risk > 30 else "good-metric")
|
266 |
-
st.markdown(f"""
|
267 |
-
<div class='metric-card'>
|
268 |
-
<p class='metric-label'>Average Risk Score</p>
|
269 |
-
<p class='metric-value {status}'>{avg_risk:.1f}/100</p>
|
270 |
-
</div>
|
271 |
-
""", unsafe_allow_html=True)
|
272 |
-
|
273 |
-
# Tabs for different views
|
274 |
-
tab1, tab2 = st.tabs(["Flagged Transactions", "All Transactions"])
|
275 |
-
|
276 |
-
with tab1:
|
277 |
-
if suspicious_count > 0:
|
278 |
-
# Add risk score visualization (color coded)
|
279 |
-
suspicious_view = suspicious_transactions.copy()
|
280 |
-
|
281 |
-
# Display dataframe
|
282 |
-
st.dataframe(
|
283 |
-
suspicious_view[['Date', 'Category', 'Vendor', 'Amount', 'Description', 'Risk_Score', 'Reason']],
|
284 |
-
use_container_width=True
|
285 |
-
)
|
286 |
-
|
287 |
-
# Get AI analysis of suspicious transactions
|
288 |
-
fraud_key = f"fraud_{date.today().isoformat()}"
|
289 |
-
if fraud_key not in st.session_state.insights_cache:
|
290 |
-
suspicious_text = "\n".join([
|
291 |
-
f"- {row['Date']}: {row['Vendor']} (${row['Amount']:.2f}) - {row['Description']}"
|
292 |
-
for _, row in suspicious_transactions.head(5).iterrows()
|
293 |
-
])
|
294 |
-
|
295 |
-
fraud_analysis = generate_ai_response(f"""
|
296 |
-
You are a financial fraud detection expert. Review these flagged suspicious transactions:
|
297 |
-
|
298 |
-
{suspicious_text}
|
299 |
-
|
300 |
-
Provide a detailed analysis:
|
301 |
-
1. Identify concerning patterns in these transactions
|
302 |
-
2. Recommend specific actions to address these issues
|
303 |
-
3. Suggest preventive measures to avoid similar issues in the future
|
304 |
-
|
305 |
-
Format your response with clear sections and actionable recommendations.
|
306 |
-
""")
|
307 |
-
st.session_state.insights_cache[fraud_key] = fraud_analysis
|
308 |
-
|
309 |
-
st.markdown("<div class='advisor-card'>", unsafe_allow_html=True)
|
310 |
-
st.markdown("<span class='ai-badge'>AI Fraud Analysis</span>", unsafe_allow_html=True)
|
311 |
-
st.markdown(f"<p class='advice-text'>{st.session_state.insights_cache[fraud_key]}</p>", unsafe_allow_html=True)
|
312 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
313 |
-
|
314 |
-
# Action buttons
|
315 |
-
st.subheader("Recommended Actions")
|
316 |
-
|
317 |
-
col1, col2, col3 = st.columns(3)
|
318 |
-
with col1:
|
319 |
-
if st.button("🔍 Investigate All Flagged"):
|
320 |
-
st.session_state.investigation_started = True
|
321 |
-
with col2:
|
322 |
-
if st.button("📝 Generate Investor Report"):
|
323 |
-
st.session_state.report_generated = True
|
324 |
-
with col3:
|
325 |
-
if st.button("✅ Mark Reviewed"):
|
326 |
-
st.session_state.marked_reviewed = True
|
327 |
-
|
328 |
-
# Simulate action responses
|
329 |
-
if 'investigation_started' in st.session_state and st.session_state.investigation_started:
|
330 |
-
st.success("Investigation initiated for all flagged transactions. Your financial team will be notified.")
|
331 |
-
|
332 |
-
if 'report_generated' in st.session_state and st.session_state.report_generated:
|
333 |
-
st.success("Investor report generated and ready for review before sending.")
|
334 |
-
|
335 |
-
if 'marked_reviewed' in st.session_state and st.session_state.marked_reviewed:
|
336 |
-
st.success("All transactions marked as reviewed. Status will be updated in the system.")
|
337 |
-
else:
|
338 |
-
st.success("No suspicious transactions detected by our AI system. Your spending appears to be normal for a startup at your stage.")
|
339 |
-
|
340 |
-
with tab2:
|
341 |
-
st.dataframe(processed_df[['Date', 'Category', 'Vendor', 'Amount', 'Description', 'Suspicious', 'Risk_Score']],
|
342 |
-
use_container_width=True)
|
343 |
-
|
344 |
-
# Spending patterns
|
345 |
-
st.subheader("Spending Pattern Analysis")
|
346 |
-
|
347 |
-
# Category breakdown
|
348 |
-
category_spending = processed_df.groupby('Category')['Amount'].sum().reset_index()
|
349 |
-
|
350 |
-
col1, col2 = st.columns(2)
|
351 |
-
|
352 |
-
with col1:
|
353 |
-
fig = px.bar(category_spending, x='Category', y='Amount',
|
354 |
-
title="Spending by Category",
|
355 |
-
labels={'Amount': 'Total Spent ($)'},
|
356 |
-
color='Amount',
|
357 |
-
color_continuous_scale='Blues')
|
358 |
-
fig.update_layout(
|
359 |
-
height=400,
|
360 |
-
plot_bgcolor='rgba(240,247,255,0.8)',
|
361 |
-
xaxis_title="Category",
|
362 |
-
yaxis_title="Amount Spent ($)",
|
363 |
-
font=dict(family="Arial, sans-serif", size=12),
|
364 |
-
margin=dict(l=20, r=20, t=40, b=20),
|
365 |
-
)
|
366 |
-
st.plotly_chart(fig, use_container_width=True)
|
367 |
-
|
368 |
-
with col2:
|
369 |
-
# AI spending pattern analysis
|
370 |
-
spending_key = f"spending_pattern_{date.today().isoformat()}"
|
371 |
-
if spending_key not in st.session_state.insights_cache:
|
372 |
-
spending_pattern_analysis = generate_ai_response("""
|
373 |
-
You are a startup spending analyst. Review the spending patterns and provide 3 key insights about:
|
374 |
-
1. Categories that appear to have unusually high spending
|
375 |
-
2. Potential areas where spending could be optimized
|
376 |
-
3. Changes in spending patterns that investors might find concerning
|
377 |
-
|
378 |
-
Format as concise, actionable bullet points.
|
379 |
-
""")
|
380 |
-
st.session_state.insights_cache[spending_key] = spending_pattern_analysis
|
381 |
-
|
382 |
-
st.markdown("<div class='insight-card'>", unsafe_allow_html=True)
|
383 |
-
st.markdown("<span class='ai-badge'>AI Spending Analysis</span>", unsafe_allow_html=True)
|
384 |
-
st.markdown(st.session_state.insights_cache[spending_key])
|
385 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
386 |
-
|
387 |
-
# AI-powered spending controls recommendation
|
388 |
-
st.subheader("AI-Recommended Spending Controls")
|
389 |
-
|
390 |
-
# Get AI recommendations for spending controls
|
391 |
-
controls_key = f"spending_controls_{date.today().isoformat()}"
|
392 |
-
if controls_key not in st.session_state.insights_cache:
|
393 |
-
controls_recommendations = generate_ai_response("""
|
394 |
-
You are a financial controls expert for startups. Based on the spending patterns and suspicious transactions,
|
395 |
-
recommend 3-4 specific spending controls that the startup should implement to prevent misuse of funds.
|
396 |
-
|
397 |
-
For each control, provide:
|
398 |
-
1. A clear policy statement
|
399 |
-
2. Implementation steps
|
400 |
-
3. Expected impact
|
401 |
-
|
402 |
-
Format as concise, actionable recommendations.
|
403 |
-
""")
|
404 |
-
st.session_state.insights_cache[controls_key] = controls_recommendations
|
405 |
-
|
406 |
-
st.markdown("<div class='advisor-card'>", unsafe_allow_html=True)
|
407 |
-
st.markdown("<span class='ai-badge'>AI Control Recommendations</span>", unsafe_allow_html=True)
|
408 |
-
st.markdown(f"<p class='advice-text'>{st.session_state.insights_cache[controls_key]}</p>", unsafe_allow_html=True)
|
409 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
410 |
-
|
411 |
-
# Call-to-action
|
412 |
-
st.info("📅 Need help implementing financial controls? Schedule a session with our AI financial advisor.")
|
413 |
-
|
414 |
-
# Render AI Financial Advisor page
|
415 |
-
def render_ai_financial_advisor():
|
416 |
-
"""Render the AI financial advisor page with voice chat capabilities"""
|
417 |
-
if not st.session_state.current_startup or st.session_state.current_startup not in st.session_state.startups:
|
418 |
-
st.warning("No startup selected. Please upload data or select a sample startup.")
|
419 |
-
render_upload_page()
|
420 |
-
return
|
421 |
-
|
422 |
-
# Get the selected startup data
|
423 |
-
startup_data = st.session_state.startups[st.session_state.current_startup]['profile']
|
424 |
-
|
425 |
-
st.markdown("<h1 class='main-header'>AI Financial Advisor</h1>", unsafe_allow_html=True)
|
426 |
-
st.markdown("<p class='sub-header'>Get expert financial guidance through our AI-powered advisor</p>", unsafe_allow_html=True)
|
427 |
-
|
428 |
-
# How AI helps with financial advisory
|
429 |
-
with st.expander("ℹ️ How AI powers your financial advisor"):
|
430 |
-
st.markdown("""
|
431 |
-
### How AI Powers Your Financial Advisor
|
432 |
-
|
433 |
-
Our AI financial advisor combines advanced language models with financial expertise:
|
434 |
-
|
435 |
-
- **Natural Language Understanding**: The system interprets complex financial questions in plain English
|
436 |
-
- **Domain-Specific Knowledge**: Our AI is trained on startup finance, venture capital, and financial modeling
|
437 |
-
- **Context-Aware Responses**: The advisor takes into account your specific financial situation and history
|
438 |
-
- **Voice Synthesis**: ElevenLabs voice technology creates natural, high-quality voice responses
|
439 |
-
- **Customized Guidance**: AI tailors advice specifically to your stage, industry, and financial position
|
440 |
-
|
441 |
-
This gives founders 24/7 access to high-quality financial guidance without the high cost of consultants.
|
442 |
-
""")
|
443 |
-
|
444 |
-
# Chat container
|
445 |
-
st.markdown("<div style='background-color: #f8f9fa; padding: 20px; border-radius: 10px; margin-bottom: 20px;'>", unsafe_allow_html=True)
|
446 |
-
|
447 |
-
# Display chat history
|
448 |
-
st.subheader("Chat with your Financial Advisor")
|
449 |
-
|
450 |
-
# Display chat messages
|
451 |
-
for message in st.session_state.chat_history:
|
452 |
-
if message["role"] == "user":
|
453 |
-
st.markdown(f"<div style='background-color: #e6f7ff; padding: 10px; border-radius: 10px; margin-bottom: 10px;'><strong>You:</strong> {message['content']}</div>", unsafe_allow_html=True)
|
454 |
-
else:
|
455 |
-
st.markdown(f"<div style='background-color: #f0f7ff; padding: 10px; border-radius: 10px; margin-bottom: 10px;'><strong>Financial Advisor:</strong> {message['content']}</div>", unsafe_allow_html=True)
|
456 |
-
|
457 |
-
# Show play button for voice if it exists
|
458 |
-
if 'audio' in message and message['audio']:
|
459 |
-
st.audio(message['audio'], format='audio/mp3')
|
460 |
-
|
461 |
-
# Input for new message
|
462 |
-
col1, col2 = st.columns([5, 1])
|
463 |
-
|
464 |
-
with col1:
|
465 |
-
user_input = st.text_input("Ask a financial question", key="user_question")
|
466 |
-
|
467 |
-
with col2:
|
468 |
-
use_voice = st.checkbox("Enable voice", value=True)
|
469 |
-
|
470 |
-
# Common financial questions
|
471 |
-
st.markdown("### Common Questions")
|
472 |
-
question_cols = st.columns(3)
|
473 |
-
|
474 |
-
common_questions = [
|
475 |
-
"How much runway do we have at our current burn rate?",
|
476 |
-
"Should we increase our marketing spend given our growth rate?",
|
477 |
-
"When should we start preparing for our next fundraising round?",
|
478 |
-
"How can we optimize our burn rate without impacting growth?",
|
479 |
-
"What metrics should we focus on improving right now?",
|
480 |
-
"How do our unit economics compare to similar startups?"
|
481 |
-
]
|
482 |
-
|
483 |
-
selected_question = None
|
484 |
-
|
485 |
-
for i, question in enumerate(common_questions):
|
486 |
-
with question_cols[i % 3]:
|
487 |
-
if st.button(question, key=f"q_{i}"):
|
488 |
-
selected_question = question
|
489 |
-
|
490 |
-
# Process user input (either from text input or selected question)
|
491 |
-
if user_input or selected_question:
|
492 |
-
question = user_input or selected_question
|
493 |
-
|
494 |
-
# Add user message to chat history
|
495 |
-
st.session_state.chat_history.append({"role": "user", "content": question})
|
496 |
-
|
497 |
-
# Get AI response
|
498 |
-
response = generate_ai_response(f"""
|
499 |
-
You are a strategic financial advisor for startups. A founder asks:
|
500 |
-
"{question}"
|
501 |
-
|
502 |
-
Here's their current financial situation:
|
503 |
-
- Stage: {startup_data['stage']}
|
504 |
-
- Current cash: ${startup_data['cash']}
|
505 |
-
- Monthly burn rate: ${startup_data['burn_rate']}
|
506 |
-
- Monthly revenue: ${startup_data['revenue']}
|
507 |
-
- Monthly growth rate: {startup_data['growth_rate'] * 100}%
|
508 |
-
- Last funding: {startup_data['last_funding']}
|
509 |
-
- Team size: {startup_data['employees']}
|
510 |
-
|
511 |
-
Provide detailed, actionable advice addressing their question. Include:
|
512 |
-
1. Clear assessment of their current situation
|
513 |
-
2. 3-5 specific, actionable recommendations with expected outcomes
|
514 |
-
3. Relevant metrics they should track
|
515 |
-
4. Industry benchmarks for comparison
|
516 |
-
5. Timeline for implementation and results
|
517 |
-
|
518 |
-
Be specific with numbers, timeframes, and expected outcomes.
|
519 |
-
""")
|
520 |
-
|
521 |
-
# Generate voice response if enabled
|
522 |
-
audio_data = None
|
523 |
-
if use_voice:
|
524 |
-
audio_data = generate_voice_response(response)
|
525 |
-
|
526 |
-
# Add AI response to chat history
|
527 |
-
st.session_state.chat_history.append({
|
528 |
-
"role": "assistant",
|
529 |
-
"content": response,
|
530 |
-
"audio": audio_data
|
531 |
-
})
|
532 |
-
|
533 |
-
# Rerun to display updated chat
|
534 |
-
st.rerun()
|
535 |
-
|
536 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
537 |
-
|
538 |
-
# Advanced tools
|
539 |
-
st.subheader("Advanced Financial Tools")
|
540 |
-
|
541 |
-
tool_cols = st.columns(3)
|
542 |
-
|
543 |
-
with tool_cols[0]:
|
544 |
-
st.markdown("""
|
545 |
-
<div style='background-color: white; padding: 15px; border-radius: 10px; height: 200px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);'>
|
546 |
-
<h4>Financial Model Review</h4>
|
547 |
-
<p>Upload your financial model for AI analysis and recommendations.</p>
|
548 |
-
<div style='position: absolute; bottom: 15px;'>
|
549 |
-
<button disabled style="background-color: #E6F3FF; color: #0066cc; border-radius: 5px; padding: 5px 10px; border: none;">Coming Soon</button>
|
550 |
-
</div>
|
551 |
-
</div>
|
552 |
-
""", unsafe_allow_html=True)
|
553 |
-
|
554 |
-
with tool_cols[1]:
|
555 |
-
st.markdown("""
|
556 |
-
<div style='background-color: white; padding: 15px; border-radius: 10px; height: 200px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);'>
|
557 |
-
<h4>Investor Pitch Review</h4>
|
558 |
-
<p>Get AI feedback on your investor pitch deck and financial projections.</p>
|
559 |
-
<div style='position: absolute; bottom: 15px;'>
|
560 |
-
<button disabled style="background-color: #E6F3FF; color: #0066cc; border-radius: 5px; padding: 5px 10px; border: none;">Coming Soon</button>
|
561 |
-
</div>
|
562 |
-
</div>
|
563 |
-
""", unsafe_allow_html=True)
|
564 |
-
|
565 |
-
with tool_cols[2]:
|
566 |
-
st.markdown("""
|
567 |
-
<div style='background-color: white; padding: 15px; border-radius: 10px; height: 200px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);'>
|
568 |
-
<h4>Fundraising Strategy</h4>
|
569 |
-
<p>Generate a customized fundraising strategy based on your metrics.</p>
|
570 |
-
<div style='position: absolute; bottom: 15px;'>
|
571 |
-
<button disabled style="background-color: #E6F3FF; color: #0066cc; border-radius: 5px; padding: 5px 10px; border: none;">Coming Soon</button>
|
572 |
-
</div>
|
573 |
-
</div>
|
574 |
-
""", unsafe_allow_html=True)
|
575 |
-
|
576 |
-
# Main function
|
577 |
-
def main():
|
578 |
-
# Initialize Gemini API
|
579 |
-
initialize_gemini()
|
580 |
-
|
581 |
-
# Create sidebar navigation
|
582 |
-
create_sidebar()
|
583 |
-
|
584 |
-
# Render the correct page based on session state
|
585 |
-
if st.session_state.current_page == 'upload':
|
586 |
-
render_upload_page()
|
587 |
-
elif st.session_state.current_page == 'dashboard':
|
588 |
-
render_financial_dashboard()
|
589 |
-
elif st.session_state.current_page == 'simulator':
|
590 |
-
render_decision_simulator()
|
591 |
-
elif st.session_state.current_page == 'monitoring':
|
592 |
-
render_fund_monitoring()
|
593 |
-
elif st.session_state.current_page == 'advisor':
|
594 |
-
render_ai_financial_advisor()
|
595 |
-
|
596 |
-
if __name__ == "__main__":
|
597 |
-
main()
|
598 |
-
import streamlit as st
|
599 |
-
import pandas as pd
|
600 |
-
import numpy as np
|
601 |
-
import plotly.express as px
|
602 |
-
import plotly.graph_objects as go
|
603 |
-
from datetime import datetime, timedelta, date
|
604 |
-
import time
|
605 |
-
import io
|
606 |
-
import base64
|
607 |
-
import requests
|
608 |
-
import google.generativeai as genai
|
609 |
-
|
610 |
-
# Constants
|
611 |
-
DEFAULT_GROWTH_RATE = 0.08 # 8% monthly growth
|
612 |
-
DEFAULT_BURN_RATE = 85000 # $85,000 monthly burn
|
613 |
-
ENGINEER_SALARY = 10000 # $10,000 monthly cost per engineer ($120K/year)
|
614 |
-
|
615 |
-
# Initialize session state variables
|
616 |
-
if 'startups' not in st.session_state:
|
617 |
-
st.session_state.startups = {} # Dictionary to store multiple startup data
|
618 |
-
if 'current_startup' not in st.session_state:
|
619 |
-
st.session_state.current_startup = None # Currently selected startup
|
620 |
-
if 'current_page' not in st.session_state:
|
621 |
-
st.session_state.current_page = 'upload' # Default page
|
622 |
-
if 'insights_cache' not in st.session_state:
|
623 |
-
st.session_state.insights_cache = {}
|
624 |
-
if 'chat_history' not in st.session_state:
|
625 |
-
st.session_state.chat_history = [
|
626 |
-
{"role": "assistant", "content": "Hi there! I'm your AI financial advisor. How can I help with your startup's finances today?"}
|
627 |
-
]
|
628 |
-
|
629 |
-
# Configure Google GenerativeAI (Gemini)
|
630 |
-
def initialize_gemini():
|
631 |
-
"""Initialize Google's GenerativeAI (Gemini) with API key"""
|
632 |
-
try:
|
633 |
-
# In production, get this from st.secrets or environment variables
|
634 |
-
api_key = st.secrets.get("GEMINI_API_KEY", None)
|
635 |
-
if api_key:
|
636 |
-
genai.configure(api_key=api_key)
|
637 |
-
return True
|
638 |
-
else:
|
639 |
-
st.warning("Gemini API key not found. Using simulated AI responses.")
|
640 |
-
return False
|
641 |
-
except Exception as e:
|
642 |
-
st.error(f"Failed to initialize Gemini AI: {e}")
|
643 |
-
return False
|
644 |
-
|
645 |
-
def generate_ai_response(prompt, simulate=True):
|
646 |
-
"""Generate text using Google's GenerativeAI (Gemini)"""
|
647 |
-
if simulate:
|
648 |
-
# Return a generic response for simulation
|
649 |
-
return """
|
650 |
-
Based on your financial situation, I recommend focusing on these key areas:
|
651 |
-
|
652 |
-
1. **Extend Your Runway**: With your current burn rate, consider reducing non-essential expenses by 15-20%. Focus particularly on optimizing marketing efficiency while maintaining growth activities.
|
653 |
-
|
654 |
-
2. **Accelerate Revenue Growth**: Your current monthly growth is good, but increasing it would significantly improve your cash position. Consider focusing sales efforts on higher-value customers with shorter sales cycles.
|
655 |
-
|
656 |
-
3. **Prepare for Fundraising**: Begin conversations with existing investors about potential bridge funding. Prepare updated metrics showing clear progress on unit economics and customer acquisition.
|
657 |
-
|
658 |
-
I recommend reviewing your expense categories weekly and tracking your burn rate closely.
|
659 |
-
"""
|
660 |
-
else:
|
661 |
-
try:
|
662 |
-
# Initialize Gemini model
|
663 |
-
model = genai.GenerativeModel('gemini-pro')
|
664 |
-
response = model.generate_content(prompt)
|
665 |
-
return response.text
|
666 |
-
except Exception as e:
|
667 |
-
st.error(f"Error generating AI response: {e}")
|
668 |
-
return "Sorry, I couldn't generate a response at this time."
|
669 |
-
|
670 |
-
def generate_voice_response(text, simulate=True):
|
671 |
-
"""Generate voice response using ElevenLabs API"""
|
672 |
-
if simulate:
|
673 |
-
# Return empty audio data for simulation
|
674 |
-
return None
|
675 |
-
else:
|
676 |
-
try:
|
677 |
-
# Get API key from secrets
|
678 |
-
api_key = st.secrets.get("ELEVENLABS_API_KEY", None)
|
679 |
-
if not api_key:
|
680 |
-
st.warning("ElevenLabs API key not found. Voice response not available.")
|
681 |
-
return None
|
682 |
-
|
683 |
-
# ElevenLabs API endpoint
|
684 |
-
url = "https://api.elevenlabs.io/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM" # Rachel voice ID
|
685 |
-
|
686 |
-
# Headers and payload
|
687 |
-
headers = {
|
688 |
-
"Accept": "audio/mpeg",
|
689 |
-
"Content-Type": "application/json",
|
690 |
-
"xi-api-key": api_key
|
691 |
-
}
|
692 |
-
|
693 |
-
data = {
|
694 |
-
"text": text,
|
695 |
-
"model_id": "eleven_monolingual_v1",
|
696 |
-
"voice_settings": {
|
697 |
-
"stability": 0.5,
|
698 |
-
"similarity_boost": 0.5
|
699 |
-
}
|
700 |
-
}
|
701 |
-
|
702 |
-
# Make the API call
|
703 |
-
response = requests.post(url, json=data, headers=headers)
|
704 |
-
|
705 |
-
if response.status_code == 200:
|
706 |
-
return response.content
|
707 |
-
else:
|
708 |
-
st.error(f"Error with ElevenLabs API: {response.status_code}")
|
709 |
-
return None
|
710 |
-
|
711 |
-
except Exception as e:
|
712 |
-
st.error(f"Error generating voice response: {e}")
|
713 |
-
return None
|
714 |
-
|
715 |
-
def switch_page(page_name):
|
716 |
-
"""Function to switch between pages"""
|
717 |
-
st.session_state.current_page = page_name
|
718 |
-
st.rerun()
|
719 |
-
|
720 |
-
# Calculate runway for business decisions
|
721 |
-
def calculate_runway(cash, burn_rate, revenue, growth_rate, months=24):
|
722 |
-
"""
|
723 |
-
Calculate runway based on cash, burn, revenue and growth
|
724 |
-
Returns runway in months and dataframe with projections
|
725 |
-
"""
|
726 |
-
# Create date range
|
727 |
-
current_date = datetime.now()
|
728 |
-
date_range = [current_date + timedelta(days=30*i) for i in range(months)]
|
729 |
-
|
730 |
-
# Initialize data structures
|
731 |
-
cash_flow = []
|
732 |
-
remaining_cash = cash
|
733 |
-
monthly_revenue = revenue
|
734 |
-
|
735 |
-
# Calculate cash flow for each month
|
736 |
-
for i in range(months):
|
737 |
-
# Calculate cash flow for this month
|
738 |
-
net_burn = burn_rate - monthly_revenue
|
739 |
-
cash_flow.append(net_burn)
|
740 |
-
|
741 |
-
# Update remaining cash
|
742 |
-
remaining_cash -= net_burn
|
743 |
-
|
744 |
-
# Update revenue with growth
|
745 |
-
monthly_revenue *= (1 + growth_rate)
|
746 |
-
|
747 |
-
# Create dataframe
|
748 |
-
df = pd.DataFrame({
|
749 |
-
'Net_Burn': cash_flow,
|
750 |
-
'Cumulative_Cash': [cash - sum(cash_flow[:i+1]) for i in range(len(cash_flow))]
|
751 |
-
}, index=date_range)
|
752 |
-
|
753 |
-
# Calculate runway (when cumulative cash goes negative)
|
754 |
-
negative_cash = df[df['Cumulative_Cash'] < 0]
|
755 |
-
if len(negative_cash) > 0:
|
756 |
-
runway_months = (negative_cash.index[0] - current_date).days // 30
|
757 |
-
else:
|
758 |
-
runway_months = months
|
759 |
-
|
760 |
-
return runway_months, df
|
761 |
-
|
762 |
-
# Simulate decisions
|
763 |
-
def simulate_decision(cash, burn_rate, revenue, growth_rate,
|
764 |
-
additional_expenses, new_hires, marketing_increase, growth_impact):
|
765 |
-
"""
|
766 |
-
Simulate the financial impact of a business decision
|
767 |
-
"""
|
768 |
-
# Current projection
|
769 |
-
current_runway, current_df = calculate_runway(
|
770 |
-
cash, burn_rate, revenue, growth_rate
|
771 |
-
)
|
772 |
-
|
773 |
-
# New projection with decision impact
|
774 |
-
new_burn_rate = burn_rate + additional_expenses + (new_hires * ENGINEER_SALARY) + marketing_increase
|
775 |
-
new_growth_rate = growth_rate + growth_impact
|
776 |
-
|
777 |
-
new_runway, new_df = calculate_runway(
|
778 |
-
cash, new_burn_rate, revenue, new_growth_rate
|
779 |
-
)
|
780 |
-
|
781 |
-
return current_runway, new_runway, current_df, new_df
|
782 |
-
|
783 |
-
# Detect suspicious transactions
|
784 |
-
def detect_suspicious_transactions(transactions_df):
|
785 |
-
"""AI-enhanced suspicious transaction detection."""
|
786 |
-
df = transactions_df.copy()
|
787 |
-
|
788 |
-
# Define thresholds for each category
|
789 |
-
category_thresholds = {
|
790 |
-
"Travel": 3000,
|
791 |
-
"Marketing": 10000,
|
792 |
-
"Office": 7000,
|
793 |
-
"Software": 6000,
|
794 |
-
"Consulting": 5000,
|
795 |
-
"Legal": 6000
|
796 |
-
}
|
797 |
-
|
798 |
-
# Define suspicious terms
|
799 |
-
suspicious_terms = ['luxury', 'cruise', 'premium', 'personal', 'gift']
|
800 |
-
|
801 |
-
# Add suspicious column
|
802 |
-
df['Suspicious'] = False
|
803 |
-
df['Reason'] = ""
|
804 |
-
df['Risk_Score'] = 0
|
805 |
-
|
806 |
-
# Check for suspicious patterns
|
807 |
-
for idx, row in df.iterrows():
|
808 |
-
reasons = []
|
809 |
-
risk_score = 0
|
810 |
-
|
811 |
-
# Check if amount exceeds category threshold
|
812 |
-
if row['Category'] in category_thresholds:
|
813 |
-
if row['Amount'] > category_thresholds[row['Category']]:
|
814 |
-
reasons.append(f"Amount exceeds typical spending for {row['Category']}")
|
815 |
-
risk_score += 30
|
816 |
-
|
817 |
-
# Higher risk for significantly exceeding threshold
|
818 |
-
excess_percentage = (row['Amount'] - category_thresholds[row['Category']]) / category_thresholds[row['Category']] * 100
|
819 |
-
if excess_percentage > 100: # More than double the threshold
|
820 |
-
risk_score += 20
|
821 |
-
|
822 |
-
# Check for suspicious vendors or descriptions
|
823 |
-
if any(term in str(row['Vendor']).lower() for term in suspicious_terms):
|
824 |
-
reasons.append(f"Vendor name contains suspicious term")
|
825 |
-
risk_score += 25
|
826 |
-
|
827 |
-
if any(term in str(row['Description']).lower() for term in suspicious_terms):
|
828 |
-
reasons.append(f"Description contains suspicious term")
|
829 |
-
risk_score += 20
|
830 |
-
|
831 |
-
# Check for rounded amounts (potential indicator of estimation/fabrication)
|
832 |
-
if row['Amount'] % 1000 == 0 and row['Amount'] > 3000:
|
833 |
-
reasons.append(f"Suspiciously round amount")
|
834 |
-
risk_score += 15
|
835 |
-
|
836 |
-
# Mark as suspicious if risk score is high enough
|
837 |
-
if risk_score >= 30:
|
838 |
-
df.at[idx, 'Suspicious'] = True
|
839 |
-
df.at[idx, 'Reason'] = "; ".join(reasons)
|
840 |
-
df.at[idx, 'Risk_Score'] = risk_score
|
841 |
-
|
842 |
-
# Sort by risk score
|
843 |
-
df = df.sort_values(by='Risk_Score', ascending=False)
|
844 |
-
|
845 |
-
return df
|
846 |
-
|
847 |
-
# Parse CSV file to dataframe
|
848 |
-
def parse_csv_to_df(file):
|
849 |
-
"""Parse uploaded CSV file to Pandas DataFrame"""
|
850 |
-
try:
|
851 |
-
df = pd.read_csv(file)
|
852 |
-
return df, None
|
853 |
-
except Exception as e:
|
854 |
-
return None, f"Error parsing CSV: {e}"
|
855 |
-
|
856 |
-
# Page config
|
857 |
-
st.set_page_config(
|
858 |
-
page_title="StartupFinancePilot",
|
859 |
-
page_icon="💰",
|
860 |
-
layout="wide",
|
861 |
-
initial_sidebar_state="expanded"
|
862 |
-
)
|
863 |
-
|
864 |
-
# Custom CSS
|
865 |
-
st.markdown("""
|
866 |
-
<style>
|
867 |
-
#MainMenu {visibility: hidden;}
|
868 |
-
footer {visibility: hidden;}
|
869 |
-
.stDeployButton {display:none;}
|
870 |
-
|
871 |
-
.main-header {
|
872 |
-
font-size: 2.5rem;
|
873 |
-
color: #0066cc;
|
874 |
-
margin-bottom: 0.5rem;
|
875 |
-
}
|
876 |
-
.sub-header {
|
877 |
-
font-size: 1.5rem;
|
878 |
-
color: #5c5c5c;
|
879 |
-
margin-bottom: 1.5rem;
|
880 |
-
}
|
881 |
-
.metric-card {
|
882 |
-
background-color: #f8f9fa;
|
883 |
-
border-radius: 10px;
|
884 |
-
padding: 20px;
|
885 |
-
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
886 |
-
}
|
887 |
-
.metric-label {
|
888 |
-
font-size: 1rem;
|
889 |
-
color: #5c5c5c;
|
890 |
-
}
|
891 |
-
.metric-value {
|
892 |
-
font-size: 1.8rem;
|
893 |
-
color: #0066cc;
|
894 |
-
font-weight: bold;
|
895 |
-
}
|
896 |
-
.good-metric {
|
897 |
-
color: #28a745;
|
898 |
-
}
|
899 |
-
.warning-metric {
|
900 |
-
color: #ffc107;
|
901 |
-
}
|
902 |
-
.danger-metric {
|
903 |
-
color: #dc3545;
|
904 |
-
}
|
905 |
-
|
906 |
-
/* Style for sidebar buttons */
|
907 |
-
div.stButton > button {
|
908 |
-
width: 100%;
|
909 |
-
padding: 10px 10px;
|
910 |
-
border: none;
|
911 |
-
background-color: #E6F3FF;
|
912 |
-
color: #0066cc;
|
913 |
-
border-radius: 10px;
|
914 |
-
text-align: left;
|
915 |
-
margin: 5px 0;
|
916 |
-
font-weight: bold;
|
917 |
-
}
|
918 |
-
|
919 |
-
div.stButton > button:hover {
|
920 |
-
background-color: #CCE5FF;
|
921 |
-
color: #004080;
|
922 |
-
}
|
923 |
-
|
924 |
-
/* Style for title box */
|
925 |
-
.title-box {
|
926 |
-
background: linear-gradient(45deg, #0066cc, #66b3ff);
|
927 |
-
padding: 20px;
|
928 |
-
border-radius: 10px;
|
929 |
-
margin-bottom: 20px;
|
930 |
-
text-align: center;
|
931 |
-
color: white;
|
932 |
-
cursor: pointer;
|
933 |
-
}
|
934 |
-
|
935 |
-
.ai-badge {
|
936 |
-
display: inline-block;
|
937 |
-
background-color: #0066cc;
|
938 |
-
color: white;
|
939 |
-
border-radius: 4px;
|
940 |
-
padding: 2px 6px;
|
941 |
-
font-size: 0.7rem;
|
942 |
-
font-weight: bold;
|
943 |
-
margin-bottom: 8px;
|
944 |
-
}
|
945 |
-
|
946 |
-
.insight-card, .advisor-card {
|
947 |
-
background-color: #f8f9fa;
|
948 |
-
border-radius: 10px;
|
949 |
-
padding: 15px;
|
950 |
-
margin-bottom: 20px;
|
951 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
952 |
-
}
|
953 |
-
|
954 |
-
.advice-text {
|
955 |
-
margin-top: 10px;
|
956 |
-
line-height: 1.6;
|
957 |
-
}
|
958 |
-
</style>
|
959 |
-
""", unsafe_allow_html=True)
|
960 |
-
|
961 |
-
# Create sidebar navigation
|
962 |
-
def create_sidebar():
|
963 |
-
with st.sidebar:
|
964 |
-
# Title box that works as home button
|
965 |
-
st.markdown("""
|
966 |
-
<div class="title-box">
|
967 |
-
<h1>💰 StartupFinancePilot</h1>
|
968 |
-
<p>AI-powered financial assistant for startups</p>
|
969 |
-
</div>
|
970 |
-
""", unsafe_allow_html=True)
|
971 |
-
|
972 |
-
# Startup selector (if there are startups in the session state)
|
973 |
-
if st.session_state.startups:
|
974 |
-
st.subheader("Selected Startup")
|
975 |
-
startup_names = list(st.session_state.startups.keys())
|
976 |
-
selected_startup = st.selectbox(
|
977 |
-
"Choose Startup",
|
978 |
-
startup_names,
|
979 |
-
index=startup_names.index(st.session_state.current_startup) if st.session_state.current_startup in startup_names else 0
|
980 |
-
)
|
981 |
-
st.session_state.current_startup = selected_startup
|
982 |
-
|
983 |
-
# Show basic startup info
|
984 |
-
if selected_startup in st.session_state.startups:
|
985 |
-
startup_data = st.session_state.startups[selected_startup]['profile']
|
986 |
-
st.markdown(f"""
|
987 |
-
**Stage:** {startup_data['stage']}
|
988 |
-
**Cash:** ${startup_data['cash']:,}
|
989 |
-
**Monthly Burn:** ${startup_data['burn_rate']:,}
|
990 |
-
**Monthly Revenue:** ${startup_data['revenue']:,}
|
991 |
-
""")
|
992 |
-
|
993 |
-
st.markdown("<hr>", unsafe_allow_html=True) # Divider
|
994 |
-
|
995 |
-
# Upload data button at the top
|
996 |
-
if st.button("📤 Upload Startup Data", use_container_width=True):
|
997 |
-
switch_page('upload')
|
998 |
-
|
999 |
-
# Navigation buttons
|
1000 |
-
if st.button("📊 Financial Dashboard", use_container_width=True):
|
1001 |
-
switch_page('dashboard')
|
1002 |
-
|
1003 |
-
if st.button("🔮 Decision Simulator", use_container_width=True):
|
1004 |
-
switch_page('simulator')
|
1005 |
-
|
1006 |
-
if st.button("🕵️ Fund Monitoring", use_container_width=True):
|
1007 |
-
switch_page('monitoring')
|
1008 |
-
|
1009 |
-
if st.button("🤖 AI Financial Advisor", use_container_width=True):
|
1010 |
-
switch_page('advisor')
|
1011 |
-
|
1012 |
-
# Upload and process financial data files
|
1013 |
-
def render_upload_page():
|
1014 |
-
"""Render the upload page for startup data"""
|
1015 |
-
st.markdown("<h1 class='main-header'>Upload Your Startup Data</h1>", unsafe_allow_html=True)
|
1016 |
-
st.markdown("<p class='sub-header'>Upload CSV files or use sample data to get started</p>", unsafe_allow_html=True)
|
1017 |
-
|
1018 |
-
with st.expander("Upload Instructions", expanded=False):
|
1019 |
-
st.markdown("""
|
1020 |
-
### How to Upload Your Startup Data
|
1021 |
-
|
1022 |
-
You can upload three types of files:
|
1023 |
-
|
1024 |
-
1. **Company Profile** - A CSV with basic information about your startup including:
|
1025 |
-
- name, stage, founded, employees, last_funding, cash, burn_rate, revenue, growth_rate
|
1026 |
-
|
1027 |
-
2. **Cash Flow Data** - A CSV with monthly cash flow data with columns:
|
1028 |
-
- Month, Revenue, Payroll, Marketing, Office, Software, Travel, Legal, Misc
|
1029 |
-
|
1030 |
-
3. **Transaction Data** - A CSV with transaction details:
|
1031 |
-
- Date, Category, Vendor, Amount, Description, Flag
|
1032 |
-
|
1033 |
-
If you don't have these files ready, you can use our sample data.
|
1034 |
-
""")
|
1035 |
-
|
1036 |
-
col1, col2 = st.columns(2)
|
1037 |
-
|
1038 |
-
with col1:
|
1039 |
-
startup_name = st.text_input("Startup Name", value="My Startup")
|
1040 |
-
|
1041 |
-
profile_file = st.file_uploader("Upload Company Profile (CSV)", type=['csv'])
|
1042 |
-
cash_flow_file = st.file_uploader("Upload Cash Flow Data (CSV)", type=['csv'])
|
1043 |
-
transactions_file = st.file_uploader("Upload Transactions Data (CSV)", type=['csv'])
|
1044 |
-
|
1045 |
-
with col2:
|
1046 |
-
st.markdown("""
|
1047 |
-
<div style="background-color: #f0f7ff; padding: 15px; border-radius: 10px; height: 90%;">
|
1048 |
-
<h4>Why Upload Your Data?</h4>
|
1049 |
-
<p>By uploading your actual financial data, you'll get:</p>
|
1050 |
-
<ul>
|
1051 |
-
<li>Personalized AI insights tailored to your startup</li>
|
1052 |
-
<li>Accurate runway projections based on your real spending patterns</li>
|
1053 |
-
<li>Custom recommendations to optimize your burn rate</li>
|
1054 |
-
<li>More realistic decision simulations</li>
|
1055 |
-
</ul>
|
1056 |
-
<p>All data is processed securely and never stored permanently.</p>
|
1057 |
-
</div>
|
1058 |
-
""", unsafe_allow_html=True)
|
1059 |
-
|
1060 |
-
# Process the files if uploaded
|
1061 |
-
if st.button("Process Data"):
|
1062 |
-
# Initialize with default values
|
1063 |
-
startup_data = {
|
1064 |
-
"name": startup_name,
|
1065 |
-
"stage": "Seed",
|
1066 |
-
"founded": "12 months ago",
|
1067 |
-
"employees": 5,
|
1068 |
-
"last_funding": "Not specified",
|
1069 |
-
"cash": 100000,
|
1070 |
-
"burn_rate": 20000,
|
1071 |
-
"revenue": 5000,
|
1072 |
-
"growth_rate": 0.05
|
1073 |
-
}
|
1074 |
-
|
1075 |
-
cash_flow_df = None
|
1076 |
-
transactions_df = None
|
1077 |
-
|
1078 |
-
# Parse company profile
|
1079 |
-
if profile_file:
|
1080 |
-
try:
|
1081 |
-
profile_df, error = parse_csv_to_df(profile_file)
|
1082 |
-
if error:
|
1083 |
-
st.error(error)
|
1084 |
-
else:
|
1085 |
-
# Get the first row as a dictionary
|
1086 |
-
if len(profile_df) > 0:
|
1087 |
-
startup_data.update(profile_df.iloc[0].to_dict())
|
1088 |
-
st.success(f"Successfully loaded company profile for {startup_data['name']}")
|
1089 |
-
except Exception as e:
|
1090 |
-
st.error(f"Error processing company profile: {e}")
|
1091 |
-
|
1092 |
-
# Parse cash flow data
|
1093 |
-
if cash_flow_file:
|
1094 |
-
cash_flow_df, error = parse_csv_to_df(cash_flow_file)
|
1095 |
-
if error:
|
1096 |
-
st.error(error)
|
1097 |
-
else:
|
1098 |
-
# Add calculated fields if not present
|
1099 |
-
if "Total_Expenses" not in cash_flow_df.columns:
|
1100 |
-
expense_columns = [col for col in cash_flow_df.columns if col not in ["Month", "Revenue", "Total_Expenses", "Net_Burn"]]
|
1101 |
-
cash_flow_df["Total_Expenses"] = cash_flow_df[expense_columns].sum(axis=1)
|
1102 |
-
|
1103 |
-
if "Net_Burn" not in cash_flow_df.columns:
|
1104 |
-
cash_flow_df["Net_Burn"] = cash_flow_df["Total_Expenses"] - cash_flow_df["Revenue"]
|
1105 |
-
|
1106 |
-
st.success("Successfully loaded cash flow data")
|
1107 |
-
|
1108 |
-
# Parse transactions data
|
1109 |
-
if transactions_file:
|
1110 |
-
transactions_df, error = parse_csv_to_df(transactions_file)
|
1111 |
-
if error:
|
1112 |
-
st.error(error)
|
1113 |
-
else:
|
1114 |
-
# Ensure transactions data has required columns
|
1115 |
-
required_columns = ["Date", "Category", "Vendor", "Amount", "Description"]
|
1116 |
-
if all(col in transactions_df.columns for col in required_columns):
|
1117 |
-
if "Flag" not in transactions_df.columns:
|
1118 |
-
transactions_df["Flag"] = "Normal" # Default flag
|
1119 |
-
|
1120 |
-
st.success("Successfully loaded transactions data")
|
1121 |
-
else:
|
1122 |
-
st.error("Transactions file is missing required columns")
|
1123 |
-
|
1124 |
-
# If any files were processed, save the data to session state
|
1125 |
-
if profile_file or cash_flow_file or transactions_file:
|
1126 |
-
# Create a sample cash flow dataframe if none was uploaded
|
1127 |
-
if cash_flow_df is None:
|
1128 |
-
cash_flow_df = create_sample_cash_flow(startup_data)
|
1129 |
-
|
1130 |
-
# Create a sample transactions dataframe if none was uploaded
|
1131 |
-
if transactions_df is None:
|
1132 |
-
transactions_df = create_sample_transactions(startup_data)
|
1133 |
-
|
1134 |
-
# Store in session state
|
1135 |
-
st.session_state.startups[startup_data['name']] = {
|
1136 |
-
'profile': startup_data,
|
1137 |
-
'cash_flow': cash_flow_df,
|
1138 |
-
'transactions': transactions_df
|
1139 |
-
}
|
1140 |
-
|
1141 |
-
# Set as current startup
|
1142 |
-
st.session_state.current_startup = startup_data['name']
|
1143 |
-
|
1144 |
-
st.success(f"Successfully added {startup_data['name']} to your startups")
|
1145 |
-
st.info("You can now analyze this startup's data in the dashboard")
|
1146 |
-
|
1147 |
-
# Redirect to dashboard
|
1148 |
-
switch_page('dashboard')
|
1149 |
-
|
1150 |
-
# Sample data options
|
1151 |
-
st.subheader("Or Use Sample Data")
|
1152 |
-
|
1153 |
-
sample_col1, sample_col2 = st.columns(2)
|
1154 |
-
|
1155 |
-
with sample_col1:
|
1156 |
-
if st.button("Use TechHealth AI Sample"):
|
1157 |
-
# Load sample data (function would generate or load from file)
|
1158 |
-
load_sample_data("TechHealth AI")
|
1159 |
-
st.success("Successfully loaded TechHealth AI sample data")
|
1160 |
-
# Redirect to dashboard
|
1161 |
-
switch_page('dashboard')
|
1162 |
-
|
1163 |
-
with sample_col2:
|
1164 |
-
if st.button("Use GreenTech Innovations Sample"):
|
1165 |
-
# Load another sample (function would generate or load from file)
|
1166 |
-
load_sample_data("GreenTech Innovations")
|
1167 |
-
st.success("Successfully loaded GreenTech Innovations sample data")
|
1168 |
-
# Redirect to dashboard
|
1169 |
-
switch_page('dashboard')
|
1170 |
-
|
1171 |
-
def create_sample_cash_flow(startup_data):
|
1172 |
-
"""Create a sample cash flow dataframe for a startup"""
|
1173 |
-
cash_flow_data = {
|
1174 |
-
"Month": [f"Month {i}" for i in range(1, 7)],
|
1175 |
-
"Revenue": [startup_data['revenue'] * (1 + startup_data['growth_rate'])**i for i in range(6)],
|
1176 |
-
"Payroll": [startup_data['burn_rate'] * 0.7] * 6,
|
1177 |
-
"Marketing": [startup_data['burn_rate'] * 0.15] * 6,
|
1178 |
-
"Office": [startup_data['burn_rate'] * 0.05] * 6,
|
1179 |
-
"Software": [startup_data['burn_rate'] * 0.03] * 6,
|
1180 |
-
"Travel": [startup_data['burn_rate'] * 0.02] * 6,
|
1181 |
-
"Legal": [startup_data['burn_rate'] * 0.01] * 6,
|
1182 |
-
"Misc": [startup_data['burn_rate'] * 0.04] * 6
|
1183 |
-
}
|
1184 |
-
cash_flow_df = pd.DataFrame(cash_flow_data)
|
1185 |
-
cash_flow_df["Total_Expenses"] = cash_flow_df[["Payroll", "Marketing", "Office", "Software", "Travel", "Legal", "Misc"]].sum(axis=1)
|
1186 |
-
cash_flow_df["Net_Burn"] = cash_flow_df["Total_Expenses"] - cash_flow_df["Revenue"]
|
1187 |
-
return cash_flow_df
|
1188 |
-
|
1189 |
-
def create_sample_transactions(startup_data):
|
1190 |
-
"""Create sample transaction data for a startup"""
|
1191 |
-
transactions_data = {
|
1192 |
-
"Date": [(datetime.now() - timedelta(days=i*5)).strftime("%Y-%m-%d") for i in range(10)],
|
1193 |
-
"Category": ["Payroll", "Marketing", "Office", "Software", "Travel", "Legal", "Misc", "Payroll", "Marketing", "Office"],
|
1194 |
-
"Vendor": ["Payroll Provider", "Facebook Ads", "Office Rent", "AWS", "Travel Agency", "Legal Firm", "Miscellaneous", "Payroll Provider", "Google Ads", "Office Supplies"],
|
1195 |
-
"Amount": [startup_data['burn_rate'] * 0.7, startup_data['burn_rate'] * 0.15, startup_data['burn_rate'] * 0.05, startup_data['burn_rate'] * 0.03, startup_data['burn_rate'] * 0.02, startup_data['burn_rate'] * 0.01, startup_data['burn_rate'] * 0.04, startup_data['burn_rate'] * 0.7, startup_data['burn_rate'] * 0.15, startup_data['burn_rate'] * 0.05],
|
1196 |
-
"Description": ["Monthly Payroll", "Ad Campaign", "Monthly Rent", "Cloud Services", "Business Travel", "Legal Services", "Miscellaneous Expenses", "Monthly Payroll", "Ad Campaign", "Office Supplies"],
|
1197 |
-
"Flag": ["Normal", "Normal", "Normal", "Normal", "Normal", "Normal", "Normal", "Normal", "Normal", "Normal"]
|
1198 |
-
}
|
1199 |
-
return pd.DataFrame(transactions_data)
|
1200 |
-
|
1201 |
-
def load_sample_data(sample_name):
|
1202 |
-
"""Load sample data for demonstration"""
|
1203 |
-
if sample_name == "TechHealth AI":
|
1204 |
-
# Create TechHealth AI sample
|
1205 |
-
startup_data = {
|
1206 |
-
"name": "TechHealth AI",
|
1207 |
-
"stage": "Seed",
|
1208 |
-
"founded": "18 months ago",
|
1209 |
-
"employees": 12,
|
1210 |
-
"last_funding": "$1.2M seed round 10 months ago",
|
1211 |
-
"cash": 320000,
|
1212 |
-
"burn_rate": 85000,
|
1213 |
-
"revenue": 15000,
|
1214 |
-
"growth_rate": 0.08
|
1215 |
-
}
|
1216 |
-
else:
|
1217 |
-
# Create GreenTech Innovations sample
|
1218 |
-
startup_data = {
|
1219 |
-
"name": "GreenTech Innovations",
|
1220 |
-
"stage": "Series A",
|
1221 |
-
"founded": "3 years ago",
|
1222 |
-
"employees": 25,
|
1223 |
-
"last_funding": "$4.5M Series A 8 months ago",
|
1224 |
-
"cash": 2800000,
|
1225 |
-
"burn_rate": 220000,
|
1226 |
-
"revenue": 75000,
|
1227 |
-
"growth_rate": 0.12
|
1228 |
-
}
|
1229 |
-
|
1230 |
-
# Generate cash flow and transaction data
|
1231 |
-
cash_flow_df = create_sample_cash_flow(startup_data)
|
1232 |
-
transactions_df = create_sample_transactions(startup_data)
|
1233 |
-
|
1234 |
-
# Add some suspicious transactions for the sample
|
1235 |
-
if sample_name == "TechHealth AI":
|
1236 |
-
suspicious_transactions = pd.DataFrame([
|
1237 |
-
{"Date": "2023-11-05", "Category": "Travel", "Vendor": "Caribbean Cruises", "Amount": 8500, "Description": "Team Retreat Planning", "Flag": "Suspicious"},
|
1238 |
-
{"Date": "2023-11-12", "Category": "Marketing", "Vendor": "LuxuryGifts Inc", "Amount": 4200, "Description": "Client Appreciation", "Flag": "Suspicious"},
|
1239 |
-
{"Date": "2023-11-22", "Category": "Office", "Vendor": "Premium Furniture", "Amount": 12000, "Description": "Office Upgrades", "Flag": "Suspicious"}
|
1240 |
-
])
|
1241 |
-
transactions_df = pd.concat([suspicious_transactions, transactions_df], ignore_index=True)
|
1242 |
-
|
1243 |
-
# Store in session state
|
1244 |
-
st.session_state.startups[startup_data['name']] = {
|
1245 |
-
'profile': startup_data,
|
1246 |
-
'cash_flow': cash_flow_df,
|
1247 |
-
'transactions': transactions_df
|
1248 |
-
}
|
1249 |
-
|
1250 |
-
# Set as current startup
|
1251 |
-
st.session_state.current_startup = startup_data['name']
|
1252 |
-
|
1253 |
-
# Render Financial Dashboard
|
1254 |
-
def render_financial_dashboard():
|
1255 |
-
"""Render the AI-powered financial dashboard page"""
|
1256 |
-
if not st.session_state.current_startup or st.session_state.current_startup not in st.session_state.startups:
|
1257 |
-
st.warning("No startup selected. Please upload data or select a sample startup.")
|
1258 |
-
render_upload_page()
|
1259 |
-
return
|
1260 |
-
|
1261 |
-
# Get the selected startup data
|
1262 |
-
startup_data = st.session_state.startups[st.session_state.current_startup]['profile']
|
1263 |
-
cash_flow_df = st.session_state.startups[st.session_state.current_startup]['cash_flow']
|
1264 |
-
|
1265 |
-
st.markdown("<h1 class='main-header'>Financial Dashboard</h1>", unsafe_allow_html=True)
|
1266 |
-
st.markdown("<p class='sub-header'>AI-powered financial insights at a glance</p>", unsafe_allow_html=True)
|
1267 |
-
|
1268 |
-
# How AI helps with financial dashboards
|
1269 |
-
with st.expander("ℹ️ How AI enhances your financial dashboard"):
|
1270 |
-
st.markdown("""
|
1271 |
-
### How AI Powers Your Financial Dashboard
|
1272 |
-
|
1273 |
-
The financial dashboard uses AI to transform raw financial data into actionable intelligence:
|
1274 |
-
|
1275 |
-
- **Automated Analysis**: Instead of manually calculating runway and burn rates, our AI model analyzes your data and highlights critical trends
|
1276 |
-
- **Predictive Forecasting**: AI forecasts your runway using pattern recognition and predictive analytics to account for varying growth rates
|
1277 |
-
- **Anomaly Detection**: The system identifies unusual spending patterns or concerning financial trends that human analysis might miss
|
1278 |
-
- **Strategic Recommendations**: Based on your specific financial situation, the AI provides tailored recommendations to optimize your runway
|
1279 |
-
- **Benchmark Comparison**: Your metrics are automatically compared against industry standards for startups at your funding stage
|
1280 |
-
|
1281 |
-
This helps founders save time, catch financial issues early, and make data-driven decisions without needing financial expertise.
|
1282 |
-
""")
|
1283 |
-
|
1284 |
-
# AI Insights Summary
|
1285 |
-
insights_key = f"dashboard_{date.today().isoformat()}"
|
1286 |
-
if insights_key not in st.session_state.insights_cache:
|
1287 |
-
insights = generate_ai_response(f"""
|
1288 |
-
You are a financial advisor for startups. Based on this startup's data:
|
1289 |
-
- Current cash: ${startup_data['cash']}
|
1290 |
-
- Monthly burn rate: ${startup_data['burn_rate']}
|
1291 |
-
- Monthly revenue: ${startup_data['revenue']}
|
1292 |
-
- Monthly growth rate: {startup_data['growth_rate'] * 100}%
|
1293 |
-
|
1294 |
-
Provide the top 3 most important financial insights that the founder should know today.
|
1295 |
-
Format each insight as a brief, action-oriented bullet point.
|
1296 |
-
""")
|
1297 |
-
st.session_state.insights_cache[insights_key] = insights
|
1298 |
-
|
1299 |
-
with st.expander("📊 AI Financial Insights", expanded=True):
|
1300 |
-
st.markdown("<span class='ai-badge'>AI-Generated Insights</span>", unsafe_allow_html=True)
|
1301 |
-
st.markdown(st.session_state.insights_cache[insights_key])
|
1302 |
-
|
1303 |
-
# Key metrics
|
1304 |
-
col1, col2, col3, col4 = st.columns(4)
|
1305 |
-
|
1306 |
-
# Calculate runway
|
1307 |
-
runway_months, runway_df = calculate_runway(
|
1308 |
-
startup_data['cash'],
|
1309 |
-
startup_data['burn_rate'],
|
1310 |
-
startup_data['revenue'],
|
1311 |
-
startup_data['growth_rate']
|
1312 |
-
)
|
1313 |
-
|
1314 |
-
# Determine status colors based on financial health indicators
|
1315 |
-
runway_status = "danger-metric" if runway_months < 6 else ("warning-metric" if runway_months < 9 else "good-metric")
|
1316 |
-
burn_status = "danger-metric" if startup_data['burn_rate'] > 100000 else ("warning-metric" if startup_data['burn_rate'] > 80000 else "good-metric")
|
1317 |
-
revenue_status = "good-metric" if startup_data['revenue'] > 20000 else ("warning-metric" if startup_data['revenue'] > 10000 else "danger-metric")
|
1318 |
-
|
1319 |
-
with col1:
|
1320 |
-
st.markdown(f"""
|
1321 |
-
<div class='metric-card'>
|
1322 |
-
<p class='metric-label'>Current Cash</p>
|
1323 |
-
<p class='metric-value'>${startup_data['cash']:,}</p>
|
1324 |
-
</div>
|
1325 |
-
""", unsafe_allow_html=True)
|
1326 |
-
|
1327 |
-
with col2:
|
1328 |
-
st.markdown(f"""
|
1329 |
-
<div class='metric-card'>
|
1330 |
-
<p class='metric-label'>Monthly Burn</p>
|
1331 |
-
<p class='metric-value {burn_status}'>${startup_data['burn_rate']:,}</p>
|
1332 |
-
</div>
|
1333 |
-
""", unsafe_allow_html=True)
|
1334 |
-
|
1335 |
-
with col3:
|
1336 |
-
st.markdown(f"""
|
1337 |
-
<div class='metric-card'>
|
1338 |
-
<p class='metric-label'>Monthly Revenue</p>
|
1339 |
-
<p class='metric-value {revenue_status}'>${startup_data['revenue']:,}</p>
|
1340 |
-
</div>
|
1341 |
-
""", unsafe_allow_html=True)
|
1342 |
-
|
1343 |
-
with col4:
|
1344 |
-
st.markdown(f"""
|
1345 |
-
<div class='metric-card'>
|
1346 |
-
<p class='metric-label'>Runway</p>
|
1347 |
-
<p class='metric-value {runway_status}'>{runway_months} months</p>
|
1348 |
-
</div>
|
1349 |
-
""", unsafe_allow_html=True)
|
1350 |
-
|
1351 |
-
# Financial charts
|
1352 |
-
st.subheader("Financial Overview")
|
1353 |
-
|
1354 |
-
tab1, tab2, tab3 = st.tabs(["Runway Projection", "Revenue vs. Expenses", "Burn Rate Trend"])
|
1355 |
-
|
1356 |
-
with tab1:
|
1357 |
-
# Runway projection chart
|
1358 |
-
fig = px.line(runway_df.reset_index(), x='index', y='Cumulative_Cash',
|
1359 |
-
title="Cash Runway Projection",
|
1360 |
-
labels={'index': 'Date', 'Cumulative_Cash': 'Remaining Cash ($)'},
|
1361 |
-
color_discrete_sequence=['#0066cc'])
|
1362 |
-
fig.add_hline(y=0, line_dash="dash", line_color="red", annotation_text="Out of Cash")
|
1363 |
-
fig.update_layout(
|
1364 |
-
height=400,
|
1365 |
-
plot_bgcolor='rgba(240,247,255,0.8)',
|
1366 |
-
xaxis_title="Date",
|
1367 |
-
yaxis_title="Cash Balance ($)",
|
1368 |
-
font=dict(family="Arial, sans-serif", size=12),
|
1369 |
-
margin=dict(l=20, r=20, t=40, b=20),
|
1370 |
-
)
|
1371 |
-
st.plotly_chart(fig, use_container_width=True)
|
1372 |
-
|
1373 |
-
# Get analysis from AI
|
1374 |
-
with st.expander("🔍 AI Financial Analysis", expanded=True):
|
1375 |
-
# Use cache to avoid repeated API calls
|
1376 |
-
analysis_key = f"runway_{date.today().isoformat()}"
|
1377 |
-
if analysis_key not in st.session_state.insights_cache:
|
1378 |
-
analysis = generate_ai_response(f"""
|
1379 |
-
You are a financial advisor for startups. Analyze this startup's financial data:
|
1380 |
-
- Current cash: ${startup_data['cash']}
|
1381 |
-
- Monthly burn rate: ${startup_data['burn_rate']}
|
1382 |
-
- Monthly revenue: ${startup_data['revenue']}
|
1383 |
-
- Monthly growth rate: {startup_data['growth_rate'] * 100}%
|
1384 |
-
|
1385 |
-
Provide a detailed analysis of their runway and financial health. Include:
|
1386 |
-
1. Exact runway calculation in months
|
1387 |
-
2. Assessment of financial health (critical, concerning, stable, or healthy)
|
1388 |
-
3. Benchmarks compared to similar seed-stage startups
|
1389 |
-
4. Three specific, actionable recommendations to improve runway
|
1390 |
-
5. Key metrics they should focus on
|
1391 |
-
|
1392 |
-
Format your response in a structured, easy-to-read format with clear sections and bullet points.
|
1393 |
-
""")
|
1394 |
-
st.session_state.insights_cache[analysis_key] = analysis
|
1395 |
-
|
1396 |
-
st.markdown("<span class='ai-badge'>AI Financial Analysis</span>", unsafe_allow_html=True)
|
1397 |
-
st.markdown(st.session_state.insights_cache[analysis_key])
|
1398 |
-
|
1399 |
-
with tab2:
|
1400 |
-
# Revenue vs Expenses chart
|
1401 |
-
rev_exp_df = cash_flow_df.copy()
|
1402 |
-
fig = px.bar(rev_exp_df, x='Month', y=['Revenue', 'Total_Expenses'],
|
1403 |
-
title="Revenue vs. Expenses",
|
1404 |
-
barmode='group',
|
1405 |
-
labels={'value': 'Amount ($)', 'variable': 'Category'},
|
1406 |
-
color_discrete_sequence=['#28a745', '#dc3545'])
|
1407 |
-
fig.update_layout(
|
1408 |
-
height=400,
|
1409 |
-
plot_bgcolor='rgba(240,247,255,0.8)',
|
1410 |
-
xaxis_title="Month",
|
1411 |
-
yaxis_title="Amount ($)",
|
1412 |
-
font=dict(family="Arial, sans-serif", size=12),
|
1413 |
-
legend_title="",
|
1414 |
-
margin=dict(l=20, r=20, t=40, b=20),
|
1415 |
-
)
|
1416 |
-
st.plotly_chart(fig, use_container_width=True)
|
1417 |
-
|
1418 |
-
# Calculate revenue growth
|
1419 |
-
revenue_growth = [(cash_flow_df['Revenue'].iloc[i] / cash_flow_df['Revenue'].iloc[i-1] - 1) * 100 if i > 0 else 0
|
1420 |
-
for i in range(len(cash_flow_df))]
|
1421 |
-
avg_growth = sum(revenue_growth[1:]) / len(revenue_growth[1:])
|
1422 |
-
|
1423 |
-
col1, col2 = st.columns(2)
|
1424 |
-
with col1:
|
1425 |
-
st.metric("Average Monthly Revenue Growth", f"{avg_growth:.1f}%")
|
1426 |
-
with col2:
|
1427 |
-
expense_growth = (cash_flow_df['Total_Expenses'].iloc[-1] / cash_flow_df['Total_Expenses'].iloc[0] - 1) * 100
|
1428 |
-
st.metric("Total Expense Growth", f"{expense_growth:.1f}%", delta=f"{expense_growth - avg_growth:.1f}%", delta_color="inverse")
|
1429 |
-
|
1430 |
-
with tab3:
|
1431 |
-
# Burn rate trend
|
1432 |
-
fig = px.line(cash_flow_df, x='Month', y='Net_Burn',
|
1433 |
-
title="Monthly Net Burn Trend",
|
1434 |
-
labels={'Net_Burn': 'Net Burn ($)'},
|
1435 |
-
color_discrete_sequence=['#dc3545'])
|
1436 |
-
fig.update_layout(
|
1437 |
-
height=400,
|
1438 |
-
plot_bgcolor='rgba(240,247,255,0.8)',
|
1439 |
-
xaxis_title="Month",
|
1440 |
-
yaxis_title="Net Burn ($)",
|
1441 |
-
font=dict(family="Arial, sans-serif", size=12),
|
1442 |
-
margin=dict(l=20, r=20, t=40, b=20),
|
1443 |
-
)
|
1444 |
-
|
1445 |
-
# Add efficiency ratio as a second y-axis
|
1446 |
-
efficiency_ratio = [cash_flow_df['Revenue'].iloc[i] / cash_flow_df['Total_Expenses'].iloc[i] * 100
|
1447 |
-
for i in range(len(cash_flow_df))]
|
1448 |
-
|
1449 |
-
fig.add_trace(go.Scatter(
|
1450 |
-
x=cash_flow_df['Month'],
|
1451 |
-
y=efficiency_ratio,
|
1452 |
-
name='Efficiency Ratio (%)',
|
1453 |
-
yaxis='y2',
|
1454 |
-
line=dict(color='#0066cc', width=2, dash='dot')
|
1455 |
-
))
|
1456 |
-
|
1457 |
-
fig.update_layout(
|
1458 |
-
yaxis2=dict(
|
1459 |
-
title='Efficiency Ratio (%)',
|
1460 |
-
overlaying='y',
|
1461 |
-
side='right',
|
1462 |
-
range=[0, max(efficiency_ratio) * 1.2]
|
1463 |
-
)
|
1464 |
-
)
|
1465 |
-
|
1466 |
-
st.plotly_chart(fig, use_container_width=True)
|
1467 |
-
|
1468 |
-
with st.expander("🔎 Understanding Efficiency Ratio"):
|
1469 |
-
st.info("The efficiency ratio measures how efficiently your startup is generating revenue relative to expenses. A higher percentage means you're getting more revenue per dollar spent. Venture-backed startups typically aim for at least 40% before Series B funding.")
|
1470 |
-
|
1471 |
-
# Expense breakdown
|
1472 |
-
st.subheader("Expense Breakdown")
|
1473 |
-
|
1474 |
-
# Last month expenses
|
1475 |
-
last_month = cash_flow_df.iloc[-1]
|
1476 |
-
expense_categories = ['Payroll', 'Marketing', 'Office', 'Software', 'Travel', 'Legal', 'Misc']
|
1477 |
-
expense_values = [last_month[cat] for cat in expense_categories]
|
1478 |
-
|
1479 |
-
col1, col2 = st.columns([2, 1])
|
1480 |
-
|
1481 |
-
with col1:
|
1482 |
-
fig = px.pie(values=expense_values, names=expense_categories,
|
1483 |
-
title="Current Month Expense Breakdown",
|
1484 |
-
color_discrete_sequence=px.colors.sequential.Blues_r)
|
1485 |
-
fig.update_layout(
|
1486 |
-
height=400,
|
1487 |
-
font=dict(family="Arial, sans-serif", size=12),
|
1488 |
-
margin=dict(l=20, r=20, t=40, b=20),
|
1489 |
-
)
|
1490 |
-
fig.update_traces(textposition='inside', textinfo='percent+label')
|
1491 |
-
st.plotly_chart(fig, use_container_width=True)
|
1492 |
-
|
1493 |
-
with col2:
|
1494 |
-
# Expense analysis
|
1495 |
-
st.markdown("<h4>Expense Analysis</h4>", unsafe_allow_html=True)
|
1496 |
-
|
1497 |
-
# Calculate industry benchmarks (simulated)
|
1498 |
-
benchmarks = {
|
1499 |
-
"Payroll": "70-80%",
|
1500 |
-
"Marketing": "10-15%",
|
1501 |
-
"Office": "5-8%",
|
1502 |
-
"Software": "3-5%"
|
1503 |
-
}
|
1504 |
-
|
1505 |
-
# Create a table with expense categories, amounts, and % of total
|
1506 |
-
expense_df = pd.DataFrame({
|
1507 |
-
"Category": expense_categories,
|
1508 |
-
"Amount": expense_values,
|
1509 |
-
"% of Total": [v / sum(expense_values) * 100 for v in expense_values]
|
1510 |
-
})
|
1511 |
-
|
1512 |
-
# Add benchmark column
|
1513 |
-
expense_df["Industry Benchmark"] = expense_df["Category"].map(
|
1514 |
-
lambda x: benchmarks.get(x, "N/A")
|
1515 |
-
)
|
1516 |
-
|
1517 |
-
# Format the dataframe for display
|
1518 |
-
formatted_df = expense_df.copy()
|
1519 |
-
formatted_df["Amount"] = formatted_df["Amount"].apply(lambda x: f"${x:,.0f}")
|
1520 |
-
formatted_df["% of Total"] = formatted_df["% of Total"].apply(lambda x: f"{x:.1f}%")
|
1521 |
-
|
1522 |
-
st.table(formatted_df)
|
1523 |
-
|
1524 |
-
# AI-powered spending optimization
|
1525 |
-
with st.expander("💡 AI Spending Optimization"):
|
1526 |
-
st.markdown("<span class='ai-badge'>AI Recommendation</span>", unsafe_allow_html=True)
|
1527 |
-
|
1528 |
-
# Use cache to avoid repeated API calls
|
1529 |
-
spending_key = f"spending_{date.today().isoformat()}"
|
1530 |
-
if spending_key not in st.session_state.insights_cache:
|
1531 |
-
spending_recommendation = generate_ai_response("""
|
1532 |
-
Based on your expense breakdown, recommend 2-3 specific ways to optimize spending to extend runway.
|
1533 |
-
Focus on industry best practices for seed-stage startups.
|
1534 |
-
""")
|
1535 |
-
st.session_state.insights_cache[spending_key] = spending_recommendation
|
1536 |
-
|
1537 |
-
st.markdown(st.session_state.insights_cache[spending_key])
|
1538 |
-
|
1539 |
-
# Fundraising Readiness Assessment
|
1540 |
-
st.subheader("Fundraising Readiness")
|
1541 |
-
|
1542 |
-
# Get AI analysis of fundraising readiness
|
1543 |
-
fundraising_key = f"fundraising_{date.today().isoformat()}"
|
1544 |
-
if fundraising_key not in st.session_state.insights_cache:
|
1545 |
-
# Calculate metrics for assessment
|
1546 |
-
runway_calc = startup_data['cash'] / (startup_data['burn_rate'] - startup_data['revenue'])
|
1547 |
-
|
1548 |
-
# Calculate some example metrics
|
1549 |
-
try:
|
1550 |
-
mrr_growth = (cash_flow_df['Revenue'].iloc[-1] / cash_flow_df['Revenue'].iloc[-2] - 1) * 100
|
1551 |
-
gross_margin = (cash_flow_df['Revenue'].iloc[-1] - cash_flow_df['Total_Expenses'].iloc[-1] / 2) / cash_flow_df['Revenue'].iloc[-1] * 100
|
1552 |
-
except:
|
1553 |
-
mrr_growth = 5.0
|
1554 |
-
gross_margin = 60.0
|
1555 |
-
|
1556 |
-
metrics = {
|
1557 |
-
"MRR Growth": f"{mrr_growth:.1f}%",
|
1558 |
-
"Gross Margin": f"{gross_margin:.1f}%",
|
1559 |
-
"CAC": "$950", # Example value
|
1560 |
-
"LTV": "$4,500", # Example value
|
1561 |
-
"Churn": "3.2%", # Example value
|
1562 |
-
}
|
1563 |
-
|
1564 |
-
metrics_text = "\n".join([f"- {k}: {v}" for k, v in metrics.items()])
|
1565 |
-
|
1566 |
-
fundraising_analysis = generate_ai_response(f"""
|
1567 |
-
You are a startup fundraising advisor. Analyze this startup's readiness for their next funding round:
|
1568 |
-
|
1569 |
-
Company Profile:
|
1570 |
-
- Stage: {startup_data['stage']}
|
1571 |
-
- Last Funding: {startup_data['last_funding']}
|
1572 |
-
- Current Cash: ${startup_data['cash']}
|
1573 |
-
- Monthly Burn: ${startup_data['burn_rate']}
|
1574 |
-
- Runway: {runway_calc:.1f} months
|
1575 |
-
|
1576 |
-
Key Metrics:
|
1577 |
-
{metrics_text}
|
1578 |
-
|
1579 |
-
Provide a comprehensive fundraising readiness assessment:
|
1580 |
-
1. Overall fundraising readiness score (0-10)
|
1581 |
-
2. Assessment of current metrics compared to investor expectations for next round
|
1582 |
-
3. Identify the 3 most critical metrics to improve before fundraising
|
1583 |
-
4. Recommend specific targets for each key metric
|
1584 |
-
5. Suggest timeline and specific milestones for fundraising preparation
|
1585 |
-
6. Estimate reasonable valuation range based on metrics and market conditions
|
1586 |
-
|
1587 |
-
Be specific with numbers, timelines, and actionable targets.
|
1588 |
-
""")
|
1589 |
-
st.session_state.insights_cache[fundraising_key] = fundraising_analysis
|
1590 |
-
|
1591 |
-
st.markdown("<div class='advisor-card'>", unsafe_allow_html=True)
|
1592 |
-
st.markdown("<span class='ai-badge'>AI Fundraising Assessment</span>", unsafe_allow_html=True)
|
1593 |
-
st.markdown(f"<p class='advice-text'>{st.session_state.insights_cache[fundraising_key]}</p>", unsafe_allow_html=True)
|
1594 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
1595 |
-
|
1596 |
-
# Call-to-action for advisor
|
1597 |
-
st.info("📅 Need personalized guidance on fundraising? Schedule a session with our AI financial advisor to get detailed recommendations.")
|
1598 |
-
|
1599 |
-
# Render Decision Simulator page
|
1600 |
-
def render_decision_simulator():
|
1601 |
-
"""Render the AI-powered decision simulator page"""
|
1602 |
-
if not st.session_state.current_startup or st.session_state.current_startup not in st.session_state.startups:
|
1603 |
-
st.warning("No startup selected. Please upload data or select a sample startup.")
|
1604 |
-
render_upload_page()
|
1605 |
-
return
|
1606 |
-
|
1607 |
-
# Get the selected startup data
|
1608 |
-
startup_data = st.session_state.startups[st.session_state.current_startup]['profile']
|
1609 |
-
|
1610 |
-
st.markdown("<h1 class='main-header'>Decision Simulator</h1>", unsafe_allow_html=True)
|
1611 |
-
st.markdown("<p class='sub-header'>AI-powered analysis of business decisions</p>", unsafe_allow_html=True)
|
1612 |
-
|
1613 |
-
# How AI helps with decision-making
|
1614 |
-
with st.expander("ℹ️ How AI enhances your decision-making"):
|
1615 |
-
st.markdown("""
|
1616 |
-
### How AI Powers Your Decision Simulator
|
1617 |
-
|
1618 |
-
The decision simulator uses AI to help you make better strategic decisions:
|
1619 |
-
|
1620 |
-
- **Scenario Analysis**: Our AI model simulates multiple financial scenarios based on your input variables
|
1621 |
-
- **Risk Assessment**: The system automatically evaluates risk levels based on your cash runway and growth metrics
|
1622 |
-
- **Return Prediction**: AI algorithms predict potential returns on investments like hiring or marketing
|
1623 |
-
- **Opportunity Cost Analysis**: The model compares different allocations of capital to maximize growth
|
1624 |
-
- **Personalized Recommendations**: Based on your specific situation, the AI provides tailored alternatives
|
1625 |
-
|
1626 |
-
This helps founders make data-driven decisions with less guesswork, avoid costly mistakes, and optimize resource allocation.
|
1627 |
-
""")
|
1628 |
-
|
1629 |
-
st.write("Test the financial impact of key business decisions before implementing them. Our AI advisor will analyze the risks and benefits.")
|
1630 |
-
|
1631 |
-
# Quick decision templates
|
1632 |
-
st.subheader("Common Scenarios")
|
1633 |
-
|
1634 |
-
decision_templates = {
|
1635 |
-
"Hiring Engineering Team": {
|
1636 |
-
"description": "Evaluate the impact of growing your engineering team",
|
1637 |
-
"new_hires": 3,
|
1638 |
-
"new_marketing": 0,
|
1639 |
-
"other_expenses": 2000,
|
1640 |
-
"growth_impact": 0.02,
|
1641 |
-
"question": "We're considering hiring 3 more engineers to accelerate product development. How will this affect our runway and what growth impact should we expect to justify this investment?"
|
1642 |
-
},
|
1643 |
-
"Marketing Expansion": {
|
1644 |
-
"description": "Test increasing your marketing budget",
|
1645 |
-
"new_hires": 0,
|
1646 |
-
"new_marketing": 15000,
|
1647 |
-
"other_expenses": 0,
|
1648 |
-
"growth_impact": 0.04,
|
1649 |
-
"question": "We want to increase our marketing spend by $15K/month to drive growth. What growth rate would we need to achieve to make this financially viable?"
|
1650 |
-
},
|
1651 |
-
"Office Expansion": {
|
1652 |
-
"description": "Analyze the cost of moving to a larger office",
|
1653 |
-
"new_hires": 0,
|
1654 |
-
"new_marketing": 0,
|
1655 |
-
"other_expenses": 8000,
|
1656 |
-
"growth_impact": 0.01,
|
1657 |
-
"question": "We're considering moving to a larger office space that would add $8K/month to our expenses. Is this justified at our current stage?"
|
1658 |
-
},
|
1659 |
-
"Custom Scenario": {
|
1660 |
-
"description": "Create your own custom scenario",
|
1661 |
-
"new_hires": 0,
|
1662 |
-
"new_marketing": 0,
|
1663 |
-
"other_expenses": 0,
|
1664 |
-
"growth_impact": 0.0,
|
1665 |
-
"question": ""
|
1666 |
-
}
|
1667 |
-
}
|
1668 |
-
|
1669 |
-
# Template selection
|
1670 |
-
template_cols = st.columns(4)
|
1671 |
-
selected_template = None
|
1672 |
-
|
1673 |
-
for i, (template_name, template) in enumerate(decision_templates.items()):
|
1674 |
-
with template_cols[i]:
|
1675 |
-
if st.button(f"{template_name}\n{template['description']}", key=f"template_{i}"):
|
1676 |
-
selected_template = template_name"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|