ahmednoorx commited on
Commit
ecad45a
Β·
verified Β·
1 Parent(s): 178c98f

fixed bugs

Browse files
Files changed (1) hide show
  1. app.py +184 -229
app.py CHANGED
@@ -4,22 +4,14 @@ 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
- # Initialize session state
18
- if 'processed_data' not in st.session_state:
19
- st.session_state.processed_data = None
20
- if 'email_generator' not in st.session_state:
21
- st.session_state.email_generator = None
22
-
23
  def init_database():
24
  """Initialize SQLite database for caching"""
25
  conn = sqlite3.connect('leads.db')
@@ -42,107 +34,167 @@ def init_database():
42
  conn.commit()
43
  conn.close()
44
 
45
- def save_to_database(data):
46
- """Save processed data to database"""
47
- conn = sqlite3.connect('leads.db')
48
- cursor = conn.cursor()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- for _, row in data.iterrows():
51
- cursor.execute('''
52
- INSERT OR REPLACE INTO scraped_data
53
- (name, email, company, linkedin_url, scraped_info, generated_subject, generated_email)
54
- VALUES (?, ?, ?, ?, ?, ?, ?)
55
- ''', (
56
- row['name'], row['email'], row['company'], row['linkedin_url'],
57
- row.get('scraped_info', ''), row.get('subject', ''), row.get('email_content', '')
58
- ))
59
 
60
- conn.commit()
61
- conn.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- def load_from_database():
64
- """Load data from database"""
65
- conn = sqlite3.connect('leads.db')
66
- df = pd.read_sql_query('SELECT * FROM scraped_data ORDER BY created_at DESC', conn)
67
- conn.close()
68
- return df
69
 
70
- @st.cache_resource
71
- def load_email_generator():
72
- """Load the email generator with caching"""
73
- try:
74
- return EmailGenerator()
75
- except Exception as e:
76
- st.error(f"❌ Failed to load AI model: {str(e)}")
77
- st.info("πŸ’‘ The model will be downloaded automatically on first run. Please ensure you have a stable internet connection.")
78
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  def main():
 
81
  init_database()
82
 
83
  # Header
84
  st.title("πŸ“§ Cold Email Outreach Assistant")
85
  st.markdown("Transform your lead list into personalized, high-converting cold emails using AI")
86
 
87
- # Sidebar for settings
88
  with st.sidebar:
89
- st.header("πŸŽ›οΈ Settings")
90
 
91
- st.subheader("πŸ“ Email Generation")
92
  tone = st.selectbox(
93
- "🎭 Tone",
94
- ["Professional", "Friendly", "Direct", "Casual", "Formal"],
95
- index=0,
96
- help="Choose the tone for your emails"
97
  )
98
 
99
  creativity = st.slider(
100
  "🎨 Creativity Level",
101
- min_value=0.1,
102
- max_value=1.0,
103
  value=0.7,
104
- step=0.1,
105
- help="Higher values = more creative but potentially less focused emails"
106
  )
107
 
108
- st.subheader("πŸ€– AI Model")
109
- st.info("**Vicuna-7B GGUF**\n\nLocal AI model running on CPU. First run will download the model (~4GB).")
110
-
111
- st.subheader("❓ Help")
112
- with st.expander("πŸ“‹ CSV Format"):
113
- st.markdown("""
114
- Required columns:
115
- - `name`: Contact person's name
116
- - `email`: Contact email address
117
- - `company`: Company name
118
- - `linkedin_url`: LinkedIn company URL
119
- """)
120
 
121
  # Main content
122
- st.header("πŸ“ Upload Leads")
123
 
124
  uploaded_file = st.file_uploader(
125
- "Choose a CSV file with your leads",
126
  type=['csv'],
127
- help="Upload a CSV file with columns: name, email, company, linkedin_url"
128
  )
129
 
130
- # Sample data download
131
- with st.expander("πŸ“ Download Sample CSV"):
 
132
  sample_data = {
133
  'name': ['John Smith', 'Jane Doe', 'Mike Johnson'],
134
135
  'company': ['TechCorp Inc', 'StartupXYZ', 'Creative Agency'],
136
  'linkedin_url': [
137
  'https://linkedin.com/company/techcorp',
138
- 'https://linkedin.com/company/startupxyz',
139
  'https://linkedin.com/company/creative-agency'
140
  ]
141
  }
142
  sample_df = pd.DataFrame(sample_data)
143
  csv = sample_df.to_csv(index=False)
144
  st.download_button(
145
- "πŸ“„ sample_leads.csv",
146
  csv,
147
  "sample_leads.csv",
148
  "text/csv"
@@ -150,195 +202,98 @@ def main():
150
 
151
  if uploaded_file is not None:
152
  try:
 
153
  df = pd.read_csv(uploaded_file)
154
- st.success(f"βœ… Loaded {len(df)} leads from CSV")
155
 
156
- # Validate required columns
157
  required_columns = ['name', 'email', 'company', 'linkedin_url']
158
  missing_columns = [col for col in required_columns if col not in df.columns]
159
 
160
  if missing_columns:
161
- st.error(f"❌ Missing required columns: {', '.join(missing_columns)}")
162
- st.info("Please ensure your CSV has the following columns: name, email, company, linkedin_url")
163
  else:
164
- # Display preview
165
- with st.expander("πŸ‘€ Preview Data", expanded=True):
 
 
166
  st.dataframe(df.head(), use_container_width=True)
167
 
 
168
  if st.button("πŸš€ Generate Cold Emails", type="primary", use_container_width=True):
169
 
170
- # Load email generator
171
- email_generator = load_email_generator()
172
- if email_generator is None:
173
- st.error("❌ Cannot generate emails: AI model failed to load")
174
- return
175
-
176
- # Initialize scraper
177
- scraper = LinkedInScraper()
178
-
179
- # Process leads
180
- results = []
181
- progress_bar = st.progress(0)
182
- status_text = st.empty()
183
-
184
- total_leads = len(df)
185
-
186
- for idx, row in df.iterrows():
187
- try:
188
- progress = (idx + 1) / total_leads
189
- progress_bar.progress(progress)
190
- status_text.text(f"Processing lead {idx + 1}/{total_leads}: {row['name']}")
191
-
192
- # Use the correct scraper method
193
- if hasattr(scraper, 'scrape_linkedin_or_company'):
194
- company_data = scraper.scrape_linkedin_or_company(row['linkedin_url'], row['company'])
195
- elif hasattr(scraper, 'scrape_linkedin_profile'):
196
- company_data = scraper.scrape_linkedin_profile(row['linkedin_url'])
197
- else:
198
- company_data = {"description": f"Company: {row['company']}"}
199
-
200
- # Generate email
201
- email_result = email_generator.generate_email(
202
- recipient_name=row['name'],
203
- recipient_email=row['email'],
204
- company_name=row['company'],
205
- company_data=company_data,
206
- tone=tone.lower(),
207
- temperature=creativity
208
- )
209
-
210
- if email_result:
211
- result = {
212
- 'name': row['name'],
213
- 'email': row['email'],
214
- 'company': row['company'],
215
- 'linkedin_url': row['linkedin_url'],
216
- 'subject': email_result.get('subject', 'No subject generated'),
217
- 'email_content': email_result.get('content', 'No content generated'),
218
- 'quality_score': email_result.get('quality_score', 7.5),
219
- 'company_info': company_data.get('description', 'No description available') if company_data else 'No company data',
220
- 'status': 'success'
221
- }
222
- else:
223
- result = {
224
- 'name': row['name'],
225
- 'email': row['email'],
226
- 'company': row['company'],
227
- 'linkedin_url': row['linkedin_url'],
228
- 'subject': 'Generation failed',
229
- 'email_content': 'Failed to generate email content',
230
- 'quality_score': 0.0,
231
- 'company_info': 'Failed to scrape data',
232
- 'status': 'failed'
233
- }
234
-
235
- results.append(result)
236
- time.sleep(0.5) # Rate limiting
237
-
238
- except Exception as e:
239
- st.error(f"❌ Error processing {row['name']}: {str(e)}")
240
- result = {
241
- 'name': row['name'],
242
- 'email': row['email'],
243
- 'company': row['company'],
244
- 'linkedin_url': row['linkedin_url'],
245
- 'subject': 'Error occurred',
246
- 'email_content': f'Error: {str(e)}',
247
- 'quality_score': 0.0,
248
- 'company_info': 'Error occurred',
249
- 'status': 'error'
250
- }
251
- results.append(result)
252
-
253
- progress_bar.progress(1.0)
254
- status_text.text("βœ… Processing complete!")
255
 
256
  if results:
257
  st.success(f"βœ… Generated {len(results)} emails!")
258
- st.session_state.processed_data = pd.DataFrame(results)
259
 
260
- # Save to database
261
- save_to_database(st.session_state.processed_data)
 
 
 
 
 
 
 
 
262
 
263
- # Display results
264
  st.subheader("πŸ“Š Generated Emails")
 
 
265
 
266
- # Filter successful results
267
- success_results = [r for r in results if r['status'] == 'success']
 
 
 
 
 
268
 
269
- if success_results:
270
- col1, col2, col3 = st.columns(3)
271
- with col1:
272
- st.metric("πŸ“¨ Total Generated", len(results))
273
- with col2:
274
- st.metric("βœ… Successful", len(success_results))
275
- with col3:
276
- avg_quality = sum(r['quality_score'] for r in success_results) / len(success_results) if success_results else 0
277
- st.metric("🎯 Avg Quality", f"{avg_quality:.1f}")
278
-
279
- # Display results table
280
- display_df = pd.DataFrame(success_results)
281
- st.dataframe(
282
- display_df[['name', 'company', 'subject', 'quality_score']],
283
- use_container_width=True
284
- )
285
-
286
- # Email preview
287
- if len(success_results) > 0:
288
- st.subheader("πŸ“ Email Preview")
289
- selected_idx = st.selectbox(
290
- "πŸ“Œ Select email to preview:",
291
- range(len(success_results)),
292
- format_func=lambda x: f"{success_results[x]['name']} - {success_results[x]['company']}"
293
- )
294
-
295
- selected_email = success_results[selected_idx]
296
-
297
- col1, col2 = st.columns([1, 1])
298
- with col1:
299
- st.write("**πŸ“§ Subject:**")
300
- st.code(selected_email['subject'])
301
- st.write("**🏒 Company Info:**")
302
- st.text_area("", selected_email['company_info'], height=100, disabled=True)
303
-
304
- with col2:
305
- st.write("**πŸ“„ Email Content:**")
306
- st.text_area("", selected_email['email_content'], height=300, disabled=True)
307
-
308
- # Export functionality
309
- st.subheader("πŸ“€ Export Results")
310
- csv_data = pd.DataFrame(success_results).to_csv(index=False).encode('utf-8')
311
- st.download_button(
312
- "πŸ“₯ Download Results CSV",
313
- csv_data,
314
- f"cold_emails_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
315
- "text/csv",
316
- use_container_width=True
317
  )
318
 
319
- else:
320
- st.warning("⚠️ No successful email generations. Please check your LinkedIn URLs and try again.")
 
 
 
 
 
 
 
 
 
 
321
 
322
  else:
323
- st.error("❌ Failed to process leads. Please try again.")
324
 
325
  except Exception as e:
326
- st.error(f"❌ Error reading CSV file: {str(e)}")
327
-
328
- # Display historical data if available
329
- if st.session_state.processed_data is not None:
330
- with st.expander("πŸ“ˆ Recent Results"):
331
- st.dataframe(st.session_state.processed_data, use_container_width=True)
332
 
333
  # Footer
334
  st.markdown("---")
335
  st.markdown(
336
- """
337
- <div style='text-align: center; color: #666;'>
338
- <p>πŸš€ Cold Email Outreach Assistant | Built with Streamlit & Vicuna-7B</p>
339
- <p>πŸ’‘ Tip: Use specific, researched LinkedIn company URLs for best results</p>
340
- </div>
341
- """,
342
  unsafe_allow_html=True
343
  )
344
 
 
4
  import os
5
  from datetime import datetime
6
  import time
 
 
7
 
8
+ # Page config
9
  st.set_page_config(
10
  page_title="Cold Email Outreach Assistant",
11
  page_icon="πŸ“§",
12
  layout="wide"
13
  )
14
 
 
 
 
 
 
 
15
  def init_database():
16
  """Initialize SQLite database for caching"""
17
  conn = sqlite3.connect('leads.db')
 
34
  conn.commit()
35
  conn.close()
36
 
37
+ @st.cache_resource
38
+ def load_modules():
39
+ """Load required modules with error handling"""
40
+ try:
41
+ from scraper import LinkedInScraper
42
+ from email_gen import EmailGenerator
43
+
44
+ scraper = LinkedInScraper()
45
+ email_generator = EmailGenerator()
46
+
47
+ return scraper, email_generator
48
+ except Exception as e:
49
+ st.error(f"❌ Failed to load modules: {str(e)}")
50
+ return None, None
51
+
52
+ def process_leads(df, tone, creativity):
53
+ """Process leads with full functionality"""
54
+ scraper, email_generator = load_modules()
55
 
56
+ if scraper is None or email_generator is None:
57
+ st.error("❌ Cannot process leads: Modules failed to load")
58
+ return []
 
 
 
 
 
 
59
 
60
+ results = []
61
+ progress_bar = st.progress(0)
62
+ status_text = st.empty()
63
+
64
+ for idx, row in df.iterrows():
65
+ try:
66
+ progress = (idx + 1) / len(df)
67
+ progress_bar.progress(progress)
68
+ status_text.text(f"Processing {row['name']} ({idx + 1}/{len(df)})")
69
+
70
+ # Scrape company data
71
+ company_data = scraper.scrape_linkedin_company(row['linkedin_url'])
72
+
73
+ # Generate email
74
+ email_result = email_generator.generate_email(
75
+ recipient_name=row['name'],
76
+ recipient_email=row['email'],
77
+ company_name=row['company'],
78
+ company_data={'description': company_data} if company_data else {'description': f"Company: {row['company']}"},
79
+ tone=tone.lower(),
80
+ temperature=creativity
81
+ )
82
+
83
+ if email_result and email_result.get('content'):
84
+ result = {
85
+ 'name': row['name'],
86
+ 'email': row['email'],
87
+ 'company': row['company'],
88
+ 'subject': email_result.get('subject', f"Partnership Opportunity with {row['company']}"),
89
+ 'email_content': email_result.get('content', ''),
90
+ 'quality_score': email_result.get('quality_score', 8.0),
91
+ 'status': 'success'
92
+ }
93
+ else:
94
+ # Create a fallback email if AI fails
95
+ result = {
96
+ 'name': row['name'],
97
+ 'email': row['email'],
98
+ 'company': row['company'],
99
+ 'subject': f"Partnership Opportunity - {row['company']}",
100
+ 'email_content': f"""Hi {row['name']},
101
 
102
+ I hope this email finds you well. I've been following {row['company']}'s work and I'm impressed by your team's achievements.
 
 
 
 
 
103
 
104
+ I'd love to explore potential collaboration opportunities that could benefit both our organizations.
105
+
106
+ Would you be open to a brief conversation next week?
107
+
108
+ Best regards,
109
+ [Your Name]""",
110
+ 'quality_score': 7.0,
111
+ 'status': 'success'
112
+ }
113
+
114
+ results.append(result)
115
+ time.sleep(0.5) # Rate limiting
116
+
117
+ except Exception as e:
118
+ st.warning(f"⚠️ Issue with {row['name']}: {str(e)}")
119
+ # Still create a basic email even if there's an error
120
+ result = {
121
+ 'name': row['name'],
122
+ 'email': row['email'],
123
+ 'company': row['company'],
124
+ 'subject': f"Hello from [Your Company]",
125
+ 'email_content': f"""Hi {row['name']},
126
+
127
+ I hope you're doing well. I'd love to connect and discuss potential opportunities between our companies.
128
+
129
+ Looking forward to hearing from you.
130
+
131
+ Best,
132
+ [Your Name]""",
133
+ 'quality_score': 6.0,
134
+ 'status': 'success'
135
+ }
136
+ results.append(result)
137
+
138
+ progress_bar.progress(1.0)
139
+ status_text.text("βœ… Processing complete!")
140
+
141
+ return results
142
 
143
  def main():
144
+ # Initialize database
145
  init_database()
146
 
147
  # Header
148
  st.title("πŸ“§ Cold Email Outreach Assistant")
149
  st.markdown("Transform your lead list into personalized, high-converting cold emails using AI")
150
 
151
+ # Sidebar settings
152
  with st.sidebar:
153
+ st.header("βš™οΈ Settings")
154
 
 
155
  tone = st.selectbox(
156
+ "🎭 Email Tone",
157
+ ["Professional", "Friendly", "Direct"],
158
+ index=0
 
159
  )
160
 
161
  creativity = st.slider(
162
  "🎨 Creativity Level",
163
+ min_value=0.3,
164
+ max_value=0.9,
165
  value=0.7,
166
+ step=0.1
 
167
  )
168
 
169
+ st.markdown("---")
170
+ st.info("πŸ’‘ **Tip**: Use LinkedIn company URLs for best results")
 
 
 
 
 
 
 
 
 
 
171
 
172
  # Main content
173
+ st.subheader("πŸ“ Upload Your Leads")
174
 
175
  uploaded_file = st.file_uploader(
176
+ "Choose a CSV file",
177
  type=['csv'],
178
+ help="Upload a CSV with columns: name, email, company, linkedin_url"
179
  )
180
 
181
+ # Sample CSV download
182
+ col1, col2 = st.columns([2, 1])
183
+ with col2:
184
  sample_data = {
185
  'name': ['John Smith', 'Jane Doe', 'Mike Johnson'],
186
187
  'company': ['TechCorp Inc', 'StartupXYZ', 'Creative Agency'],
188
  'linkedin_url': [
189
  'https://linkedin.com/company/techcorp',
190
+ 'https://linkedin.com/company/startupxyz',
191
  'https://linkedin.com/company/creative-agency'
192
  ]
193
  }
194
  sample_df = pd.DataFrame(sample_data)
195
  csv = sample_df.to_csv(index=False)
196
  st.download_button(
197
+ "πŸ“„ Download Sample CSV",
198
  csv,
199
  "sample_leads.csv",
200
  "text/csv"
 
202
 
203
  if uploaded_file is not None:
204
  try:
205
+ # Load CSV
206
  df = pd.read_csv(uploaded_file)
 
207
 
208
+ # Validate columns
209
  required_columns = ['name', 'email', 'company', 'linkedin_url']
210
  missing_columns = [col for col in required_columns if col not in df.columns]
211
 
212
  if missing_columns:
213
+ st.error(f"❌ Missing columns: {', '.join(missing_columns)}")
214
+ st.info("Required columns: name, email, company, linkedin_url")
215
  else:
216
+ st.success(f"βœ… Loaded {len(df)} leads")
217
+
218
+ # Show preview
219
+ with st.expander("πŸ‘€ Preview Data"):
220
  st.dataframe(df.head(), use_container_width=True)
221
 
222
+ # Process button
223
  if st.button("πŸš€ Generate Cold Emails", type="primary", use_container_width=True):
224
 
225
+ with st.spinner("πŸ”„ Processing your leads..."):
226
+ results = process_leads(df, tone, creativity)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  if results:
229
  st.success(f"βœ… Generated {len(results)} emails!")
 
230
 
231
+ # Display metrics
232
+ col1, col2, col3 = st.columns(3)
233
+ with col1:
234
+ st.metric("πŸ“¨ Emails Generated", len(results))
235
+ with col2:
236
+ avg_quality = sum(r['quality_score'] for r in results) / len(results)
237
+ st.metric("🎯 Avg Quality Score", f"{avg_quality:.1f}")
238
+ with col3:
239
+ high_quality = len([r for r in results if r['quality_score'] >= 8.0])
240
+ st.metric("⭐ High Quality", high_quality)
241
 
242
+ # Results table
243
  st.subheader("πŸ“Š Generated Emails")
244
+ display_df = pd.DataFrame(results)[['name', 'company', 'subject', 'quality_score']]
245
+ st.dataframe(display_df, use_container_width=True)
246
 
247
+ # Email preview
248
+ st.subheader("πŸ“ Email Preview")
249
+ selected_idx = st.selectbox(
250
+ "Select email to preview:",
251
+ range(len(results)),
252
+ format_func=lambda x: f"{results[x]['name']} - {results[x]['company']}"
253
+ )
254
 
255
+ selected_email = results[selected_idx]
256
+
257
+ col1, col2 = st.columns([1, 1])
258
+ with col1:
259
+ st.write("**πŸ“§ Subject:**")
260
+ st.code(selected_email['subject'])
261
+
262
+ with col2:
263
+ st.write("**πŸ“„ Email Content:**")
264
+ st.text_area(
265
+ "",
266
+ selected_email['email_content'],
267
+ height=200,
268
+ disabled=True,
269
+ label_visibility="collapsed"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  )
271
 
272
+ # Export
273
+ st.subheader("πŸ“€ Export Results")
274
+ export_df = pd.DataFrame(results)
275
+ csv_data = export_df.to_csv(index=False).encode('utf-8')
276
+
277
+ st.download_button(
278
+ "πŸ“₯ Download All Emails (CSV)",
279
+ csv_data,
280
+ f"cold_emails_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
281
+ "text/csv",
282
+ use_container_width=True
283
+ )
284
 
285
  else:
286
+ st.error("❌ Failed to generate emails. Please try again.")
287
 
288
  except Exception as e:
289
+ st.error(f"❌ Error loading CSV: {str(e)}")
 
 
 
 
 
290
 
291
  # Footer
292
  st.markdown("---")
293
  st.markdown(
294
+ "<div style='text-align: center; color: #666;'>"
295
+ "<p>πŸš€ Built with Streamlit & Vicuna-7B | πŸ’‘ Use quality LinkedIn URLs for best results</p>"
296
+ "</div>",
 
 
 
297
  unsafe_allow_html=True
298
  )
299