ahmednoorx commited on
Commit
59cd773
·
verified ·
1 Parent(s): 80d5a59

Test basic Streamlit app functionality

Browse files
Files changed (1) hide show
  1. app.py +7 -389
app.py CHANGED
@@ -1,399 +1,17 @@
1
  import streamlit as st
2
- import pandas as pd
3
- import sqlite3
4
- import os
5
- from datetime import datetime
6
- import time
7
- from scraper import LinkedInScraper
8
- from email_gen import EmailGenerator
9
 
10
- # Configure Streamlit page
11
  st.set_page_config(
12
  page_title="Cold Email Outreach Assistant",
13
  page_icon="📧",
14
  layout="wide"
15
  )
16
 
17
- def init_database():
18
- """Initialize SQLite database for caching"""
19
- conn = sqlite3.connect('leads.db')
20
- cursor = conn.cursor()
21
-
22
- cursor.execute('''
23
- CREATE TABLE IF NOT EXISTS scraped_data (
24
- id INTEGER PRIMARY KEY AUTOINCREMENT,
25
- linkedin_url TEXT UNIQUE,
26
- company_name TEXT,
27
- description TEXT,
28
- industry TEXT,
29
- website TEXT,
30
- scraped_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
31
- )
32
- ''')
33
-
34
- cursor.execute('''
35
- CREATE TABLE IF NOT EXISTS generated_emails (
36
- id INTEGER PRIMARY KEY AUTOINCREMENT,
37
- linkedin_url TEXT,
38
- recipient_name TEXT,
39
- recipient_email TEXT,
40
- company_name TEXT,
41
- subject_line TEXT,
42
- email_body TEXT,
43
- tone TEXT,
44
- creativity REAL,
45
- quality_score REAL,
46
- generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
47
- FOREIGN KEY (linkedin_url) REFERENCES scraped_data (linkedin_url)
48
- )
49
- ''')
50
-
51
- conn.commit()
52
- conn.close()
53
 
54
- @st.cache_resource
55
- def load_email_generator():
56
- """Load the email generator with caching"""
57
- try:
58
- return EmailGenerator()
59
- except Exception as e:
60
- st.error(f"❌ Failed to load AI model: {str(e)}")
61
- st.info("💡 The model will be downloaded automatically on first run. Please ensure you have a stable internet connection.")
62
- return None
63
 
64
- def process_csv_data(df, tone, creativity, num_variations):
65
- """Process CSV data and generate emails"""
66
- scraper = LinkedInScraper()
67
- email_gen = load_email_generator()
68
-
69
- if email_gen is None:
70
- return None
71
-
72
- results = []
73
-
74
- # Progress tracking
75
- progress_bar = st.progress(0)
76
- status_text = st.empty()
77
-
78
- for idx, row in df.iterrows():
79
- progress = (idx + 1) / len(df)
80
- progress_bar.progress(progress)
81
- status_text.text(f"Processing {idx + 1}/{len(df)}: {row['name']} at {row['company']}")
82
-
83
- # Scrape company data
84
- with st.spinner(f"🔍 Scraping data for {row['company']}..."):
85
- company_data = scraper.scrape_company_data(row['linkedin_url'])
86
-
87
- # Generate emails with multiple variations
88
- variations = []
89
- for i in range(num_variations):
90
- with st.spinner(f"✍️ Generating email variation {i+1}/{num_variations}..."):
91
- email_data = email_gen.generate_email(
92
- company_data=company_data,
93
- recipient_name=row['name'],
94
- recipient_email=row['email'],
95
- tone=tone,
96
- creativity=creativity
97
- )
98
- if email_data:
99
- variations.append(email_data)
100
-
101
- # Combine all data
102
- result = {
103
- 'name': row['name'],
104
- 'email': row['email'],
105
- 'company': row['company'],
106
- 'linkedin_url': row['linkedin_url'],
107
- 'company_description': company_data.get('description', 'N/A'),
108
- 'industry': company_data.get('industry', 'N/A'),
109
- 'website': company_data.get('website', 'N/A'),
110
- 'variations': variations,
111
- 'best_variation': max(variations, key=lambda x: x.get('quality_score', 0)) if variations else None
112
- }
113
- results.append(result)
114
-
115
- # Small delay to be respectful to servers
116
- time.sleep(1)
117
-
118
- progress_bar.progress(1.0)
119
- status_text.text("✅ Processing complete!")
120
-
121
- return results
122
-
123
- def display_results(results, tone, creativity):
124
- """Display results in an interactive table format"""
125
- if not results:
126
- st.warning("No results to display.")
127
- return
128
-
129
- st.subheader("📊 Results Overview")
130
-
131
- # Summary metrics
132
- col1, col2, col3, col4 = st.columns(4)
133
-
134
- total_leads = len(results)
135
- successful_generations = sum(1 for r in results if r['best_variation'])
136
- avg_quality = sum(r['best_variation']['quality_score'] for r in results if r['best_variation']) / max(successful_generations, 1)
137
-
138
- col1.metric("Total Leads", total_leads)
139
- col2.metric("Successful Generations", successful_generations)
140
- col3.metric("Average Quality Score", f"{avg_quality:.1f}/10")
141
- col4.metric("Success Rate", f"{(successful_generations/total_leads)*100:.1f}%")
142
-
143
- # Results table
144
- st.subheader("📋 Generated Emails")
145
-
146
- for idx, result in enumerate(results):
147
- with st.expander(f"📧 {result['name']} - {result['company']}", expanded=False):
148
- if not result['best_variation']:
149
- st.error("❌ Failed to generate email for this contact")
150
- continue
151
-
152
- best = result['best_variation']
153
-
154
- # Three columns layout
155
- col1, col2, col3 = st.columns([1, 2, 1])
156
-
157
- with col1:
158
- st.write("**Contact Info:**")
159
- st.write(f"👤 **Name:** {result['name']}")
160
- st.write(f"📧 **Email:** {result['email']}")
161
- st.write(f"🏢 **Company:** {result['company']}")
162
- st.write(f"🏭 **Industry:** {result['industry']}")
163
- if result['website'] != 'N/A':
164
- st.write(f"🌐 **Website:** {result['website']}")
165
-
166
- # Quality indicator
167
- quality = best['quality_score']
168
- if quality >= 8:
169
- st.success(f"🌟 Quality: {quality:.1f}/10")
170
- elif quality >= 6:
171
- st.warning(f"⚡ Quality: {quality:.1f}/10")
172
- else:
173
- st.error(f"⚠️ Quality: {quality:.1f}/10")
174
-
175
- with col2:
176
- st.write("**📧 Generated Email:**")
177
-
178
- # Subject line
179
- st.write("**Subject:**")
180
- st.code(best['subject_line'], language=None)
181
-
182
- # Email body
183
- st.write("**Email Body:**")
184
- st.text_area(
185
- "Email Content",
186
- best['email_body'],
187
- height=300,
188
- key=f"email_body_{idx}",
189
- label_visibility="collapsed"
190
- )
191
-
192
- with col3:
193
- st.write("**🎯 Generation Settings:**")
194
- st.write(f"**Tone:** {tone}")
195
- st.write(f"**Creativity:** {creativity}")
196
-
197
- st.write("**📊 Company Data:**")
198
- if result['company_description'] != 'N/A':
199
- with st.expander("📄 Description", expanded=False):
200
- st.write(result['company_description'][:200] + "..." if len(result['company_description']) > 200 else result['company_description'])
201
-
202
- # Show all variations if multiple
203
- if len(result['variations']) > 1:
204
- st.write(f"**🔄 Variations ({len(result['variations'])}):**")
205
- for i, var in enumerate(result['variations']):
206
- quality_color = "🌟" if var['quality_score'] >= 8 else "⚡" if var['quality_score'] >= 6 else "⚠️"
207
- st.write(f"{quality_color} Variation {i+1}: {var['quality_score']:.1f}/10")
208
-
209
- def export_to_csv(results):
210
- """Export results to CSV format"""
211
- if not results:
212
- return None
213
-
214
- export_data = []
215
- for result in results:
216
- if result['best_variation']:
217
- best = result['best_variation']
218
- export_data.append({
219
- 'name': result['name'],
220
- 'email': result['email'],
221
- 'company': result['company'],
222
- 'industry': result['industry'],
223
- 'website': result['website'],
224
- 'subject_line': best['subject_line'],
225
- 'email_body': best['email_body'],
226
- 'quality_score': best['quality_score'],
227
- 'linkedin_url': result['linkedin_url'],
228
- 'company_description': result['company_description']
229
- })
230
-
231
- return pd.DataFrame(export_data)
232
-
233
- def main():
234
- # Initialize database
235
- init_database()
236
-
237
- # Header
238
- st.title("📧 Cold Email Outreach Assistant")
239
- st.markdown("Transform your lead list into personalized, high-converting cold emails using AI")
240
-
241
- # Sidebar for settings
242
- with st.sidebar:
243
- st.header("🎛️ Settings")
244
-
245
- # Email generation settings
246
- st.subheader("📝 Email Generation")
247
- tone = st.selectbox(
248
- "🎭 Tone",
249
- ["Professional", "Friendly", "Direct", "Casual", "Formal"],
250
- index=0,
251
- help="Choose the tone for your emails"
252
- )
253
-
254
- creativity = st.slider(
255
- "🎨 Creativity Level",
256
- min_value=0.1,
257
- max_value=1.0,
258
- value=0.7,
259
- step=0.1,
260
- help="Higher values = more creative but potentially less focused emails"
261
- )
262
-
263
- num_variations = st.selectbox(
264
- "🔄 Email Variations",
265
- [1, 2, 3],
266
- index=1,
267
- help="Number of email variations to generate per contact"
268
- )
269
-
270
- st.markdown("---")
271
-
272
- # Model info
273
- st.subheader("🤖 AI Model")
274
- st.info("**Vicuna-7B GGUF**\n\nOptimized for quality cold email generation")
275
-
276
- # File upload
277
- st.subheader("📁 Upload Your Lead List")
278
- uploaded_file = st.file_uploader(
279
- "Choose a CSV file",
280
- type=['csv'],
281
- help="Upload a CSV file with columns: name, email, company, linkedin_url"
282
- )
283
-
284
- # Sample CSV download
285
- col1, col2 = st.columns(2)
286
- with col1:
287
- if st.button("📥 Download Sample CSV"):
288
- sample_data = {
289
- 'name': ['John Smith', 'Jane Doe', 'Mike Johnson'],
290
291
- 'company': ['TechCorp Inc', 'StartupXYZ', 'Creative Agency'],
292
- 'linkedin_url': [
293
- 'https://linkedin.com/company/techcorp',
294
- 'https://linkedin.com/company/startupxyz',
295
- 'https://linkedin.com/company/creative-agency'
296
- ]
297
- }
298
- sample_df = pd.DataFrame(sample_data)
299
- csv = sample_df.to_csv(index=False)
300
- st.download_button(
301
- "📄 sample_leads.csv",
302
- csv,
303
- "sample_leads.csv",
304
- "text/csv"
305
- )
306
-
307
- # Initialize results variable
308
- results = None
309
-
310
- if uploaded_file is not None:
311
- try:
312
- # Load and validate CSV
313
- df = pd.read_csv(uploaded_file)
314
-
315
- st.success(f"✅ Loaded {len(df)} leads from CSV")
316
-
317
- # Validate required columns
318
- required_columns = ['name', 'email', 'company', 'linkedin_url']
319
- missing_columns = [col for col in required_columns if col not in df.columns]
320
-
321
- if missing_columns:
322
- st.error(f"❌ Missing required columns: {missing_columns}")
323
- st.info("Required columns: name, email, company, linkedin_url")
324
- return
325
-
326
- # Display preview
327
- with st.expander("👀 Preview Data", expanded=True):
328
- st.dataframe(df.head())
329
-
330
- # Validate data
331
- issues = []
332
- if df['email'].isnull().any():
333
- issues.append("Some emails are missing")
334
- if df['linkedin_url'].isnull().any():
335
- issues.append("Some LinkedIn URLs are missing")
336
-
337
- if issues:
338
- st.warning("⚠️ Data Issues Found:")
339
- for issue in issues:
340
- st.write(f"- {issue}")
341
-
342
- if not st.checkbox("Continue anyway?"):
343
- return
344
-
345
- # Process button
346
- if st.button("🚀 Generate Cold Emails", type="primary"):
347
- if len(df) > 10:
348
- st.warning("⚠️ Processing more than 10 leads may take several minutes. Consider processing in smaller batches.")
349
- if not st.checkbox("I understand this may take a while"):
350
- return
351
-
352
- # Process the data
353
- with st.spinner("🔄 Processing leads and generating emails..."):
354
- results = process_csv_data(df, tone, creativity, num_variations)
355
-
356
- if results:
357
- st.success("✅ Email generation complete!")
358
-
359
- # Display results immediately
360
- st.markdown("---")
361
- display_results(results, tone, creativity)
362
-
363
- # Export functionality
364
- st.markdown("---")
365
- st.subheader("📤 Export Results")
366
-
367
- export_df = export_to_csv(results)
368
- if export_df is not None:
369
- csv = export_df.to_csv(index=False)
370
- st.download_button(
371
- "📥 Download Results as CSV",
372
- csv,
373
- f"cold_emails_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
374
- "text/csv",
375
- help="Download all generated emails in CSV format"
376
- )
377
-
378
- st.info(f"📊 Ready to export {len(export_df)} successful email generations")
379
- else:
380
- st.error("❌ Failed to process leads. Please try again.")
381
-
382
- except Exception as e:
383
- st.error(f"❌ Error reading CSV file: {str(e)}")
384
- st.info("Please ensure your CSV file has the correct format and encoding.")
385
-
386
- # Footer
387
- st.markdown("---")
388
- st.markdown(
389
- """
390
- <div style='text-align: center; color: #666;'>
391
- <p>🚀 Cold Email Outreach Assistant | Built with Streamlit & Vicuna-7B</p>
392
- <p>💡 Tip: Use specific, researched LinkedIn company URLs for best results</p>
393
- </div>
394
- """,
395
- unsafe_allow_html=True
396
- )
397
-
398
- if __name__ == "__main__":
399
- main()
 
1
  import streamlit as st
 
 
 
 
 
 
 
2
 
 
3
  st.set_page_config(
4
  page_title="Cold Email Outreach Assistant",
5
  page_icon="📧",
6
  layout="wide"
7
  )
8
 
9
+ st.title("📧 Cold Email Outreach Assistant")
10
+ st.write("🎉 Basic Streamlit app is working!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # Test file upload
13
+ uploaded_file = st.file_uploader("Test CSV Upload", type=['csv'])
14
+ if uploaded_file:
15
+ st.success("File upload works!")
 
 
 
 
 
16
 
17
+ st.write("If you can see this, the basic app is running correctly.")