AdithyaSNair commited on
Commit
0175ebc
·
verified ·
1 Parent(s): 787308b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1109 -965
app.py CHANGED
@@ -11,11 +11,14 @@ import re
11
  import pandas as pd
12
  import sqlite3
13
  from datetime import datetime, timedelta
14
- from streamlit_chat import message # Importing streamlit-chat
15
-
16
- GROQ_API_KEY = "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"
17
- RAPIDAPI_KEY = "2a4a8a38a9msh97ce530a89589a6p1d0106jsn1acc0a5ea6bc"
18
 
 
 
 
19
 
20
  llm = ChatGroq(
21
  temperature=0,
@@ -23,1010 +26,1151 @@ llm = ChatGroq(
23
  model_name="llama-3.1-70b-versatile"
24
  )
25
 
26
- def extract_text_from_pdf(pdf_file):
27
- """
28
- Extracts text from an uploaded PDF file.
29
- """
30
- text = ""
31
- try:
32
- with fitz.open(stream=pdf_file.read(), filetype="pdf") as doc:
33
- for page in doc:
34
- text += page.get_text()
35
- return text
36
- except Exception as e:
37
- st.error(f"Error extracting text from PDF: {e}")
38
- return ""
39
-
40
- def extract_job_description(job_link):
41
- """
42
- Fetches and extracts job description text from a given URL.
43
- """
44
- try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  headers = {
46
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
 
47
  }
48
- response = requests.get(job_link, headers=headers)
49
- response.raise_for_status()
50
- soup = BeautifulSoup(response.text, 'html.parser')
51
- # You might need to adjust the selectors based on the website's structure
52
- job_description = soup.get_text(separator='\n')
53
- return job_description.strip()
54
- except Exception as e:
55
- st.error(f"Error fetching job description: {e}")
56
- return ""
57
-
58
- def extract_requirements(job_description):
59
- """
60
- Uses Groq to extract job requirements from the job description.
61
- """
62
- prompt = f"""
63
- The following is a job description:
64
-
65
- {job_description}
66
-
67
- Extract the list of job requirements, qualifications, and skills from the job description. Provide them as a numbered list.
68
-
69
- Requirements:
70
- """
71
-
72
- try:
73
- response = llm.invoke(prompt)
74
- requirements = response.content.strip()
75
- return requirements
76
- except Exception as e:
77
- st.error(f"Error extracting requirements: {e}")
78
- return ""
79
-
80
- def generate_email(job_description, requirements, resume_text):
81
- """
82
- Generates a personalized cold email using Groq based on the job description, requirements, and resume.
83
- """
84
- prompt = f"""
85
- You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Craft a concise and professional cold email to a potential employer based on the following information:
86
-
87
- **Job Description:**
88
- {job_description}
89
-
90
- **Extracted Requirements:**
91
- {requirements}
92
-
93
- **Your Resume:**
94
- {resume_text}
95
-
96
- **Email Requirements:**
97
- - **Introduction:** Briefly introduce yourself and mention the specific job you are applying for.
98
- - **Body:** Highlight your relevant skills, projects, internships, and leadership experiences that align with the job requirements.
99
- - **Value Proposition:** Explain how your fresh perspective and recent academic knowledge can add value to the company.
100
- - **Closing:** Express enthusiasm for the opportunity, mention your willingness for an interview, and thank the recipient for their time.
101
-
102
- **Email:**
103
- """
104
-
105
- try:
106
- response = llm.invoke(prompt)
107
- email_text = response.content.strip()
108
- return email_text
109
- except Exception as e:
110
- st.error(f"Error generating email: {e}")
111
- return ""
112
-
113
- def generate_cover_letter(job_description, requirements, resume_text):
114
- """
115
- Generates a personalized cover letter using Groq based on the job description, requirements, and resume.
116
- """
117
- prompt = f"""
118
- You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Compose a personalized and professional cover letter based on the following information:
119
-
120
- **Job Description:**
121
- {job_description}
122
-
123
- **Extracted Requirements:**
124
- {requirements}
125
-
126
- **Your Resume:**
127
- {resume_text}
128
-
129
- **Cover Letter Requirements:**
130
- 1. **Greeting:** Address the hiring manager by name if available; otherwise, use a generic greeting such as "Dear Hiring Manager."
131
- 2. **Introduction:** Begin with an engaging opening that mentions the specific position you are applying for and conveys your enthusiasm.
132
- 3. **Body:**
133
- - **Skills and Experiences:** Highlight relevant technical skills, projects, internships, and leadership roles that align with the job requirements.
134
- - **Alignment:** Demonstrate how your academic background and hands-on experiences make you a suitable candidate for the role.
135
- 4. **Value Proposition:** Explain how your fresh perspective, recent academic knowledge, and eagerness to learn can contribute to the company's success.
136
- 5. **Conclusion:** End with a strong closing statement expressing your interest in an interview, your availability, and gratitude for the hiring manager’s time and consideration.
137
- 6. **Professional Tone:** Maintain a respectful and professional tone throughout the letter.
138
-
139
- **Cover Letter:**
140
- """
141
-
142
- try:
143
- response = llm.invoke(prompt)
144
- cover_letter = response.content.strip()
145
- return cover_letter
146
- except Exception as e:
147
- st.error(f"Error generating cover letter: {e}")
148
- return ""
149
-
150
- def extract_skills(text):
151
- """
152
- Extracts a list of skills from the resume text using Groq.
153
- """
154
- prompt = f"""
155
- Extract a comprehensive list of technical and soft skills from the following resume text. Provide the skills as a comma-separated list.
156
-
157
- Resume Text:
158
- {text}
159
-
160
- Skills:
161
- """
162
-
163
- try:
164
- response = llm.invoke(prompt)
165
- skills = response.content.strip()
166
- # Clean and split the skills
167
- skills_list = [skill.strip() for skill in re.split(',|\n', skills) if skill.strip()]
168
- return skills_list
169
- except Exception as e:
170
- st.error(f"Error extracting skills: {e}")
171
- return []
172
-
173
- def suggest_keywords(resume_text, job_description=None):
174
- """
175
- Suggests additional relevant keywords to enhance resume compatibility with ATS.
176
- """
177
- prompt = f"""
178
- Analyze the following resume text and suggest additional relevant keywords that can enhance its compatibility with Applicant Tracking Systems (ATS). If a job description is provided, tailor the keywords to align with the job requirements.
179
-
180
- Resume Text:
181
- {resume_text}
182
-
183
- Job Description:
184
- {job_description if job_description else "N/A"}
185
-
186
- Suggested Keywords:
187
- """
188
-
189
- try:
190
- response = llm.invoke(prompt)
191
- keywords = response.content.strip()
192
- keywords_list = [keyword.strip() for keyword in re.split(',|\n', keywords) if keyword.strip()]
193
- return keywords_list
194
- except Exception as e:
195
- st.error(f"Error suggesting keywords: {e}")
196
- return []
197
-
198
- def get_job_recommendations(job_title, location="India"):
199
- """
200
- Fetches salary estimates using the Job Salary Data API based on the job title and location.
201
- """
202
- url = "https://job-salary-data.p.rapidapi.com/job-salary"
203
- querystring = {
204
- "job_title": job_title.strip(),
205
- "location": location.strip(),
206
- "radius": "100" # Adjust radius as needed
207
- }
208
-
209
- headers = {
210
- "x-rapidapi-key": RAPIDAPI_KEY, # Securely access the API key
211
- "x-rapidapi-host": "job-salary-data.p.rapidapi.com"
212
- }
213
-
214
- try:
215
- response = requests.get(url, headers=headers, params=querystring)
216
- response.raise_for_status()
217
- salary_data = response.json()
218
-
219
- # Adjust the keys based on the API's response structure
220
- min_salary = salary_data.get("min_salary")
221
- avg_salary = salary_data.get("avg_salary")
222
- max_salary = salary_data.get("max_salary")
223
-
224
- if not all([min_salary, avg_salary, max_salary]):
225
- st.error("Incomplete salary data received from the API.")
226
  return {}
227
 
228
- return {
229
- "min_salary": min_salary,
230
- "avg_salary": avg_salary,
231
- "max_salary": max_salary
232
- }
233
- except requests.exceptions.HTTPError as http_err:
234
- st.error(f"HTTP error occurred: {http_err}")
235
- return {}
236
- except Exception as err:
237
- st.error(f"An error occurred: {err}")
238
- return {}
239
-
240
- def create_skill_distribution_chart(skills):
241
- """
242
- Creates a bar chart showing the distribution of skills.
243
- """
244
- skill_counts = {}
245
- for skill in skills:
246
- skill_counts[skill] = skill_counts.get(skill, 0) + 1
247
- df = pd.DataFrame(list(skill_counts.items()), columns=['Skill', 'Count'])
248
- fig = px.bar(df, x='Skill', y='Count', title='Skill Distribution')
249
- return fig
250
-
251
- def create_experience_timeline(resume_text):
252
- """
253
- Creates an experience timeline from the resume text.
254
- """
255
- # Extract work experience details using Groq
256
- prompt = f"""
257
- From the following resume text, extract the job titles, companies, and durations of employment. Provide the information in a table format with columns: Job Title, Company, Duration (in years).
258
-
259
- Resume Text:
260
- {resume_text}
261
-
262
- Table:
263
- """
264
-
265
- try:
266
- response = llm.invoke(prompt)
267
- table_text = response.content.strip()
268
- # Parse the table_text to create a DataFrame
269
- data = []
270
- for line in table_text.split('\n'):
271
- if line.strip() and not line.lower().startswith("job title"):
272
- parts = line.split('|')
273
- if len(parts) == 3:
274
- job_title = parts[0].strip()
275
- company = parts[1].strip()
276
- duration = parts[2].strip()
277
- # Convert duration to a float representing years
278
- duration_years = parse_duration(duration)
279
- data.append({"Job Title": job_title, "Company": company, "Duration (years)": duration_years})
280
- df = pd.DataFrame(data)
281
- if not df.empty:
282
- # Create a cumulative duration for timeline
283
- df['Start Year'] = df['Duration (years)'].cumsum() - df['Duration (years)']
284
- df['End Year'] = df['Duration (years)'].cumsum()
285
- fig = px.timeline(df, x_start="Start Year", x_end="End Year", y="Job Title", color="Company", title="Experience Timeline")
286
- fig.update_yaxes(categoryorder="total ascending")
287
- return fig
288
- else:
289
  return None
290
- except Exception as e:
291
- st.error(f"Error creating experience timeline: {e}")
292
- return None
293
-
294
- def parse_duration(duration_str):
295
- """
296
- Parses duration strings like '2 years' or '6 months' into float years.
297
- """
298
- try:
299
- if 'year' in duration_str.lower():
300
- years = float(re.findall(r'\d+\.?\d*', duration_str)[0])
301
- return years
302
- elif 'month' in duration_str.lower():
303
- months = float(re.findall(r'\d+\.?\d*', duration_str)[0])
304
- return months / 12
305
- else:
306
  return 0
307
- except:
308
- return 0
309
-
310
- def init_db():
311
- """
312
- Initializes the SQLite database for application tracking.
313
- """
314
- conn = sqlite3.connect('applications.db')
315
- c = conn.cursor()
316
- c.execute('''
317
- CREATE TABLE IF NOT EXISTS applications (
318
- id INTEGER PRIMARY KEY AUTOINCREMENT,
319
- job_title TEXT,
320
- company TEXT,
321
- application_date TEXT,
322
- status TEXT,
323
- deadline TEXT,
324
- notes TEXT,
325
- job_description TEXT,
326
- resume_text TEXT,
327
- skills TEXT
328
- )
329
- ''')
330
- conn.commit()
331
- conn.close()
332
-
333
- def add_application(job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills):
334
- """
335
- Adds a new application to the database.
336
- """
337
- conn = sqlite3.connect('applications.db')
338
- c = conn.cursor()
339
- c.execute('''
340
- INSERT INTO applications (job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills)
341
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
342
- ''', (job_title, company, application_date, status, deadline, notes, job_description, resume_text, ', '.join(skills)))
343
- conn.commit()
344
- conn.close()
345
-
346
- def fetch_applications():
347
- """
348
- Fetches all applications from the database.
349
- """
350
- conn = sqlite3.connect('applications.db')
351
- c = conn.cursor()
352
- c.execute('SELECT * FROM applications')
353
- data = c.fetchall()
354
- conn.close()
355
- applications = []
356
- for app in data:
357
- applications.append({
358
- "ID": app[0],
359
- "Job Title": app[1],
360
- "Company": app[2],
361
- "Application Date": app[3],
362
- "Status": app[4],
363
- "Deadline": app[5],
364
- "Notes": app[6],
365
- "Job Description": app[7],
366
- "Resume Text": app[8],
367
- "Skills": app[9].split(', ') if app[9] else []
368
- })
369
- return applications
370
-
371
- def update_application_status(app_id, new_status):
372
- """
373
- Updates the status of an application.
374
- """
375
- conn = sqlite3.connect('applications.db')
376
- c = conn.cursor()
377
- c.execute('UPDATE applications SET status = ? WHERE id = ?', (new_status, app_id))
378
- conn.commit()
379
- conn.close()
380
-
381
- def delete_application(app_id):
382
- """
383
- Deletes an application from the database.
384
- """
385
- conn = sqlite3.connect('applications.db')
386
- c = conn.cursor()
387
- c.execute('DELETE FROM applications WHERE id = ?', (app_id,))
388
- conn.commit()
389
- conn.close()
390
-
391
- def generate_learning_path(career_goal, current_skills):
392
- """
393
- Generates a personalized learning path using Groq based on career goal and current skills.
394
- """
395
- prompt = f"""
396
- Based on the following career goal and current skills, create a personalized learning path that includes recommended courses, projects, and milestones to achieve the career goal.
397
-
398
- **Career Goal:**
399
- {career_goal}
400
-
401
- **Current Skills:**
402
- {current_skills}
403
-
404
- **Learning Path:**
405
- """
406
-
407
- try:
408
- response = llm.invoke(prompt)
409
- learning_path = response.content.strip()
410
- return learning_path
411
- except Exception as e:
412
- st.error(f"Error generating learning path: {e}")
413
- return ""
414
-
415
- # -------------------------------
416
- # Page Functions
417
- # -------------------------------
418
-
419
- def email_generator_page():
420
- st.header("Automated Email Generator")
421
-
422
- st.write("""
423
- Generate personalized cold emails based on job postings and your resume.
424
- """)
425
-
426
- # Input fields
427
- job_link = st.text_input("Enter the job link:")
428
- uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
429
-
430
- if st.button("Generate Email"):
431
- if not job_link:
432
- st.error("Please enter a job link.")
433
- return
434
- if not uploaded_file:
435
- st.error("Please upload your resume.")
436
- return
437
-
438
- with st.spinner("Processing..."):
439
- # Extract job description
440
- job_description = extract_job_description(job_link)
441
- if not job_description:
442
- st.error("Failed to extract job description.")
443
- return
444
 
445
- # Extract requirements
446
- requirements = extract_requirements(job_description)
447
- if not requirements:
448
- st.error("Failed to extract requirements.")
449
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
 
451
- # Extract resume text
452
- resume_text = extract_text_from_pdf(uploaded_file)
453
- if not resume_text:
454
- st.error("Failed to extract text from resume.")
455
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
 
457
- # Generate email
458
- email_text = generate_email(job_description, requirements, resume_text)
459
- if email_text:
460
- st.subheader("Generated Email:")
461
- st.write(email_text)
462
- # Provide download option
463
- st.download_button(
464
- label="Download Email",
465
- data=email_text,
466
- file_name="generated_email.txt",
467
- mime="text/plain"
468
- )
469
- else:
470
- st.error("Failed to generate email.")
471
-
472
- def cover_letter_generator_page():
473
- st.header("Automated Cover Letter Generator")
474
-
475
- st.write("""
476
- Generate personalized cover letters based on job postings and your resume.
477
- """)
478
-
479
- # Input fields
480
- job_link = st.text_input("Enter the job link:")
481
- uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
482
-
483
- if st.button("Generate Cover Letter"):
484
- if not job_link:
485
- st.error("Please enter a job link.")
486
- return
487
- if not uploaded_file:
488
- st.error("Please upload your resume.")
489
- return
490
-
491
- with st.spinner("Processing..."):
492
- # Extract job description
493
- job_description = extract_job_description(job_link)
494
- if not job_description:
495
- st.error("Failed to extract job description.")
496
  return
497
-
498
- # Extract requirements
499
- requirements = extract_requirements(job_description)
500
- if not requirements:
501
- st.error("Failed to extract requirements.")
502
  return
503
 
504
- # Extract resume text
505
- resume_text = extract_text_from_pdf(uploaded_file)
506
- if not resume_text:
507
- st.error("Failed to extract text from resume.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  return
509
 
510
- # Generate cover letter
511
- cover_letter = generate_cover_letter(job_description, requirements, resume_text)
512
- if cover_letter:
513
- st.subheader("Generated Cover Letter:")
514
- st.write(cover_letter)
515
- # Provide download option
516
- st.download_button(
517
- label="Download Cover Letter",
518
- data=cover_letter,
519
- file_name="generated_cover_letter.txt",
520
- mime="text/plain"
521
- )
522
- else:
523
- st.error("Failed to generate cover letter.")
524
-
525
- def resume_analysis_page():
526
- st.header("Resume Analysis and Optimization")
527
-
528
- uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
529
-
530
- if uploaded_file:
531
- resume_text = extract_text_from_pdf(uploaded_file)
532
- if resume_text:
533
- st.success("Resume uploaded successfully!")
534
- # Perform analysis
535
- st.subheader("Extracted Information")
536
- # Extracted skills
537
- skills = extract_skills(resume_text)
538
- st.write("**Skills:**", ', '.join(skills) if skills else "No skills extracted.")
539
- # Extract keywords
540
- keywords = suggest_keywords(resume_text)
541
- st.write("**Suggested Keywords for ATS Optimization:**", ', '.join(keywords) if keywords else "No keywords suggested.")
542
- # Provide optimization suggestions
543
- st.subheader("Optimization Suggestions")
544
- if keywords:
545
- st.write("- **Keyword Optimization:** Incorporate the suggested keywords to improve ATS compatibility.")
546
- else:
547
- st.write("- **Keyword Optimization:** No keywords suggested.")
548
- st.write("- **Formatting:** Ensure consistent formatting for headings and bullet points to enhance readability.")
549
- st.write("- **Experience Details:** Provide specific achievements and quantify your accomplishments where possible.")
550
-
551
- # Visual Resume Analytics
552
- st.subheader("Visual Resume Analytics")
553
- # Skill Distribution Chart
554
- if skills:
555
- st.write("**Skill Distribution:**")
556
- fig_skills = create_skill_distribution_chart(skills)
557
- st.plotly_chart(fig_skills)
558
- else:
559
- st.write("**Skill Distribution:** No skills to display.")
560
 
561
- # Experience Timeline (if applicable)
562
- fig_experience = create_experience_timeline(resume_text)
563
- if fig_experience:
564
- st.write("**Experience Timeline:**")
565
- st.plotly_chart(fig_experience)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
  else:
567
- st.write("**Experience Timeline:** Not enough data to generate a timeline.")
568
 
569
- # Save the resume and analysis to the database
570
- if st.button("Save Resume Analysis"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
  add_application(
572
- job_title="N/A",
573
- company="N/A",
574
- application_date=datetime.now().strftime("%Y-%m-%d"),
575
- status="N/A",
576
- deadline="N/A",
577
- notes="Resume Analysis",
578
- job_description="N/A",
579
  resume_text=resume_text,
580
  skills=skills
581
  )
582
- st.success("Resume analysis saved successfully!")
583
- else:
584
- st.error("Failed to extract text from resume.")
585
-
586
- def application_tracking_dashboard():
587
- st.header("Application Tracking Dashboard")
588
-
589
- # Initialize database
590
- init_db()
591
-
592
- # Form to add a new application
593
- st.subheader("Add New Application")
594
- with st.form("add_application"):
595
- job_title = st.text_input("Job Title")
596
- company = st.text_input("Company")
597
- application_date = st.date_input("Application Date", datetime.today())
598
- status = st.selectbox("Status", ["Applied", "Interviewing", "Offered", "Rejected"])
599
- deadline = st.date_input("Application Deadline", datetime.today() + timedelta(days=30))
600
- notes = st.text_area("Notes")
601
- uploaded_file = st.file_uploader("Upload Job Description (PDF)", type="pdf")
602
- uploaded_resume = st.file_uploader("Upload Resume (PDF)", type="pdf")
603
- submitted = st.form_submit_button("Add Application")
604
- if submitted:
605
- if uploaded_file:
606
- job_description = extract_text_from_pdf(uploaded_file)
607
- else:
608
- job_description = ""
609
- if uploaded_resume:
610
- resume_text = extract_text_from_pdf(uploaded_resume)
611
- skills = extract_skills(resume_text)
612
- else:
613
- resume_text = ""
614
- skills = []
615
- add_application(
616
- job_title=job_title,
617
- company=company,
618
- application_date=application_date.strftime("%Y-%m-%d"),
619
- status=status,
620
- deadline=deadline.strftime("%Y-%m-%d"),
621
- notes=notes,
622
- job_description=job_description,
623
- resume_text=resume_text,
624
- skills=skills
625
  )
626
- st.success("Application added successfully!")
627
-
628
- # Display applications
629
- st.subheader("Your Applications")
630
- applications = fetch_applications()
631
- if applications:
632
- df = pd.DataFrame(applications)
633
- df = df.drop(columns=["Job Description", "Resume Text", "Skills"])
634
- st.dataframe(df)
635
-
636
- # Actions: Update Status or Delete
637
- for app in applications:
638
- with st.expander(f"{app['Job Title']} at {app['Company']}"):
639
- st.write(f"**Application Date:** {app['Application Date']}")
640
- st.write(f"**Deadline:** {app['Deadline']}")
641
- st.write(f"**Status:** {app['Status']}")
642
- st.write(f"**Notes:** {app['Notes']}")
643
- if app['Job Description']:
644
- st.write("**Job Description:**")
645
- st.write(app['Job Description'][:500] + "...")
646
- if app['Skills']:
647
- st.write("**Skills:**", ', '.join(app['Skills']))
648
- # Update status
649
- new_status = st.selectbox("Update Status:", ["Applied", "Interviewing", "Offered", "Rejected"], key=f"status_{app['ID']}")
650
- if st.button("Update Status", key=f"update_{app['ID']}"):
651
- update_application_status(app['ID'], new_status)
652
- st.success("Status updated successfully!")
653
- # Delete application
654
- if st.button("Delete Application", key=f"delete_{app['ID']}"):
655
- delete_application(app['ID'])
656
- st.success("Application deleted successfully!")
657
- else:
658
- st.write("No applications found.")
659
-
660
- def interview_preparation_module():
661
- st.header("Interview Preparation")
662
-
663
- st.write("""
664
- Prepare for your interviews with tailored mock questions and expert tips.
665
- """)
666
-
667
- # Input fields
668
- job_title = st.text_input("Enter the job title you're applying for:")
669
- company = st.text_input("Enter the company name:")
670
-
671
- if st.button("Generate Mock Interview Questions"):
672
- if not job_title or not company:
673
- st.error("Please enter both job title and company name.")
674
- return
675
- with st.spinner("Generating questions..."):
676
- prompt = f"""
677
- Generate a list of 10 interview questions for a {job_title} position at {company}. Include a mix of technical and behavioral questions.
678
- """
679
- try:
680
- questions = llm.invoke(prompt).content.strip()
681
- st.subheader("Mock Interview Questions:")
682
- st.write(questions)
683
 
684
- # Optionally, provide sample answers or tips
685
- if st.checkbox("Show Sample Answers"):
686
- sample_prompt = f"""
687
- Provide sample answers for the following interview questions for a {job_title} position at {company}.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
 
689
- Questions:
690
- {questions}
691
 
692
- Sample Answers:
693
- """
694
- try:
695
- sample_answers = llm.invoke(sample_prompt).content.strip()
696
- st.subheader("Sample Answers:")
697
- st.write(sample_answers)
698
- except Exception as e:
699
- st.error(f"Error generating sample answers: {e}")
700
- except Exception as e:
701
- st.error(f"Error generating interview questions: {e}")
702
-
703
- def personalized_learning_paths_module():
704
- st.header("Personalized Learning Paths")
705
-
706
- st.write("""
707
- Receive tailored learning plans to help you acquire the skills needed for your desired career.
708
- """)
709
-
710
- # Input fields
711
- career_goal = st.text_input("Enter your career goal (e.g., Data Scientist, Machine Learning Engineer):")
712
- current_skills = st.text_input("Enter your current skills (comma-separated):")
713
-
714
- if st.button("Generate Learning Path"):
715
- if not career_goal or not current_skills:
716
- st.error("Please enter both career goal and current skills.")
717
- return
718
- with st.spinner("Generating your personalized learning path..."):
719
- learning_path = generate_learning_path(career_goal, current_skills)
720
- if learning_path:
721
- st.subheader("Your Personalized Learning Path:")
722
- st.write(learning_path)
723
- else:
724
- st.error("Failed to generate learning path.")
725
-
726
- def networking_opportunities_module():
727
- st.header("Networking Opportunities")
728
-
729
- st.write("""
730
- Expand your professional network by connecting with relevant industry peers and joining professional groups.
731
- """)
732
-
733
- user_skills = st.text_input("Enter your key skills (comma-separated):")
734
- industry = st.text_input("Enter your industry (e.g., Technology, Finance):")
735
-
736
- if st.button("Find Networking Opportunities"):
737
- if not user_skills or not industry:
738
- st.error("Please enter both key skills and industry.")
739
- return
740
- with st.spinner("Fetching networking opportunities..."):
741
- # Suggest LinkedIn groups or connections based on skills and industry
742
- prompt = f"""
743
- Based on the following skills: {user_skills}, and industry: {industry}, suggest relevant LinkedIn groups, professional organizations, and industry events for networking.
744
- """
745
- try:
746
- suggestions = llm.invoke(prompt).content.strip()
747
- st.subheader("Recommended Networking Groups and Events:")
748
- st.write(suggestions)
749
- except Exception as e:
750
- st.error(f"Error fetching networking opportunities: {e}")
751
-
752
- def salary_estimation_module():
753
- st.header("Salary Estimation and Negotiation Tips")
754
-
755
- st.write("""
756
- Understand the salary expectations for your desired roles and learn effective negotiation strategies.
757
- """)
758
-
759
- # Input fields
760
- job_title = st.text_input("Enter the job title:")
761
- location = st.text_input("Enter the location (e.g., New York, NY, USA):")
762
-
763
- if st.button("Get Salary Estimate"):
764
- if not job_title or not location:
765
- st.error("Please enter both job title and location.")
766
- return
767
- with st.spinner("Fetching salary data..."):
768
- # Job Salary Data API Integration
769
- salary_data = get_job_recommendations(job_title, location)
770
- if salary_data:
771
- min_salary = salary_data.get("min_salary")
772
- avg_salary = salary_data.get("avg_salary")
773
- max_salary = salary_data.get("max_salary")
774
-
775
- if min_salary and avg_salary and max_salary:
776
- st.subheader("Salary Estimate:")
777
- st.write(f"**Minimum Salary:** ${min_salary:,}")
778
- st.write(f"**Average Salary:** ${avg_salary:,}")
779
- st.write(f"**Maximum Salary:** ${max_salary:,}")
780
-
781
- # Visualization
782
- salary_df = pd.DataFrame({
783
- "Salary Range": ["Minimum", "Average", "Maximum"],
784
- "Amount": [min_salary, avg_salary, max_salary]
785
- })
786
-
787
- fig = px.bar(salary_df, x="Salary Range", y="Amount",
788
- title=f"Salary Estimates for {job_title} in {location}",
789
- labels={"Amount": "Salary (USD)"},
790
- text_auto=True)
791
- st.plotly_chart(fig)
792
- else:
793
- st.error("Salary data not available for the provided job title and location.")
794
 
795
- # Generate negotiation tips using Groq
796
- tips_prompt = f"""
797
- Provide a list of 5 effective tips for negotiating a salary for a {job_title} position in {location}.
 
 
 
 
 
 
 
 
 
 
 
798
  """
799
  try:
800
- tips = llm.invoke(tips_prompt).content.strip()
801
- st.subheader("Negotiation Tips:")
802
- st.write(tips)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
803
  except Exception as e:
804
- st.error(f"Error generating negotiation tips: {e}")
805
- else:
806
- st.error("Failed to retrieve salary data.")
807
 
808
- def feedback_and_improvement_module():
809
- st.header("Feedback and Continuous Improvement")
810
 
811
- st.write("""
812
- We value your feedback! Let us know how we can improve your experience.
813
- """)
814
 
815
- with st.form("feedback_form"):
816
- name = st.text_input("Your Name")
817
- email = st.text_input("Your Email")
818
- feedback_type = st.selectbox("Type of Feedback", ["Bug Report", "Feature Request", "General Feedback"])
819
- feedback = st.text_area("Your Feedback")
820
- submitted = st.form_submit_button("Submit")
821
 
822
- if submitted:
823
- if not name or not email or not feedback:
824
- st.error("Please fill in all the fields.")
825
- else:
826
- # Here you can implement logic to store feedback, e.g., in a database or send via email
827
- # For demonstration, we'll print to the console
828
- print(f"Feedback from {name} ({email}): {feedback_type} - {feedback}")
829
- st.success("Thank you for your feedback!")
830
-
831
- def gamification_module():
832
- st.header("Gamification and Achievements")
833
-
834
- st.write("""
835
- Stay motivated by earning badges and tracking your progress!
836
- """)
837
-
838
- # Initialize database
839
- init_db()
840
-
841
- # Example achievements
842
- applications = fetch_applications()
843
- num_apps = len(applications)
844
- achievements = {
845
- "First Application": num_apps >= 1,
846
- "5 Applications": num_apps >= 5,
847
- "10 Applications": num_apps >= 10,
848
- "Resume Optimized": any(app['Skills'] for app in applications),
849
- "Interview Scheduled": any(app['Status'] == 'Interviewing' for app in applications)
850
- }
851
-
852
- for achievement, earned in achievements.items():
853
- if earned:
854
- st.success(f"🎉 {achievement}")
855
- else:
856
- st.info(f"🔜 {achievement}")
857
-
858
- # Progress Bar
859
- progress = min(num_apps / 10, 1.0) # Ensure progress is between 0.0 and 1.0
860
- st.write("**Overall Progress:**")
861
- st.progress(progress)
862
- st.write(f"{progress * 100:.0f}% complete")
863
-
864
- def resource_library_page():
865
- st.header("Resource Library")
866
-
867
- st.write("""
868
- Access a collection of templates and guides to enhance your job search.
869
- """)
870
-
871
- resources = [
872
- {
873
- "title": "Resume Template",
874
- "description": "A professional resume template in DOCX format.",
875
- "file": "./resume_template.docx"
876
- },
877
- {
878
- "title": "Cover Letter Template",
879
- "description": "A customizable cover letter template.",
880
- "file": "./cover_letter_template.docx"
881
- },
882
- {
883
- "title": "Job Application Checklist",
884
- "description": "Ensure you have all the necessary steps covered during your job search.",
885
- "file": "./application_checklist.pdf"
886
- }
887
- ]
888
 
889
- for resource in resources:
890
- st.markdown(f"### {resource['title']}")
891
- st.write(resource['description'])
892
- try:
893
- with open(resource['file'], "rb") as file:
894
- btn = st.download_button(
895
- label="Download",
896
- data=file,
897
- file_name=resource['file'],
898
- mime="application/octet-stream"
899
- )
900
- except FileNotFoundError:
901
- st.error(f"File {resource['file']} not found. Please ensure the file is in the correct directory.")
902
- st.write("---")
903
-
904
- def success_stories_page():
905
- st.header("Success Stories")
906
-
907
- st.write("""
908
- Hear from our users who have successfully landed their dream jobs with our assistance!
909
- """)
910
-
911
- # Example testimonials
912
- testimonials = [
913
- {
914
- "name": "Rahul Sharma",
915
- "position": "Data Scientist at TechCorp",
916
- "testimonial": "This app transformed my job search process. The resume analysis and personalized emails were game-changers!",
917
- "image": "images/user1.jpg" # Replace with actual image paths
918
- },
919
- {
920
- "name": "Priya Mehta",
921
- "position": "Machine Learning Engineer at InnovateX",
922
- "testimonial": "The interview preparation module helped me ace my interviews with confidence. Highly recommended!",
923
- "image": "images/user2.jpg"
924
- }
925
- ]
926
 
927
- for user in testimonials:
928
- col1, col2 = st.columns([1, 3])
929
  with col1:
930
- try:
931
- st.image(user["image"], width=100)
932
- except:
933
- st.write("![User Image](https://via.placeholder.com/100)")
934
  with col2:
935
- st.write(f"**{user['name']}**")
936
- st.write(f"*{user['position']}*")
937
- st.write(f"\"{user['testimonial']}\"")
938
- st.write("---")
939
 
940
- def chatbot_support_page():
941
- st.header("AI-Powered Chatbot Support")
 
 
 
 
 
 
 
 
 
 
 
 
 
942
 
943
- st.write("""
944
- Have questions or need assistance? Chat with our AI-powered assistant!
945
- """)
946
 
947
- # Initialize session state for chatbot
948
- if 'chat_history' not in st.session_state:
949
- st.session_state['chat_history'] = []
950
 
951
- # User input
952
- user_input = st.text_input("You:", key="user_input")
 
 
 
 
953
 
954
- if st.button("Send"):
955
- if user_input:
956
- # Append user message to chat history
957
- st.session_state['chat_history'].append({"message": user_input, "is_user": True})
958
- prompt = f"""
959
- You are a helpful assistant for a Job Application Assistant app. Answer the user's query based on the following context:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
960
 
961
- {user_input}
962
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
963
  try:
964
- # Invoke the LLM to get a response
965
- response = llm.invoke(prompt).content.strip()
966
- # Append assistant response to chat history
967
- st.session_state['chat_history'].append({"message": response, "is_user": False})
968
- except Exception as e:
969
- error_message = "Sorry, I encountered an error while processing your request."
970
- st.session_state['chat_history'].append({"message": error_message, "is_user": False})
971
- st.error(f"Error in chatbot: {e}")
972
-
973
- # Display chat history using streamlit-chat
974
- for chat in st.session_state['chat_history']:
975
- if chat['is_user']:
976
- message(chat['message'], is_user=True, avatar_style="thumbs")
977
- else:
978
- message(chat['message'], is_user=False, avatar_style="bottts")
979
-
980
-
981
- # -------------------------------
982
- # Main App with Sidebar Navigation
983
- # -------------------------------
984
-
985
- def main():
986
- st.set_page_config(page_title="Job Application Assistant", layout="wide")
987
-
988
- # Initialize database early to ensure tables exist
989
- init_db()
990
-
991
- # Sidebar Navigation
992
- with st.sidebar:
993
- selected = option_menu(
994
- "Main Menu",
995
- ["Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking",
996
- "Interview Preparation", "Personalized Learning Paths", "Networking Opportunities",
997
- "Salary Estimation", "Feedback", "Gamification", "Resource Library", "Success Stories", "Chatbot Support"],
998
- icons=["envelope", "file-earmark-text", "file-person", "briefcase", "gear",
999
- "book", "people", "currency-dollar", "chat-left-text", "trophy", "collection", "star", "chat"],
1000
- menu_icon="cast",
1001
- default_index=0,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1002
  )
1003
 
1004
- if selected == "Email Generator":
1005
- email_generator_page()
1006
- elif selected == "Cover Letter Generator":
1007
- cover_letter_generator_page()
1008
- elif selected == "Resume Analysis":
1009
- resume_analysis_page()
1010
- elif selected == "Application Tracking":
1011
- application_tracking_dashboard()
1012
- elif selected == "Interview Preparation":
1013
- interview_preparation_module()
1014
- elif selected == "Personalized Learning Paths":
1015
- personalized_learning_paths_module()
1016
- elif selected == "Networking Opportunities":
1017
- networking_opportunities_module()
1018
- elif selected == "Salary Estimation":
1019
- salary_estimation_module()
1020
- elif selected == "Feedback":
1021
- feedback_and_improvement_module()
1022
- elif selected == "Gamification":
1023
- gamification_module()
1024
- elif selected == "Resource Library":
1025
- resource_library_page()
1026
- elif selected == "Success Stories":
1027
- success_stories_page()
1028
- elif selected == "Chatbot Support":
1029
- chatbot_support_page()
1030
-
1031
- if __name__ == "__main__":
1032
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  import pandas as pd
12
  import sqlite3
13
  from datetime import datetime, timedelta
14
+ from streamlit_chat import message
15
+ import streamlit_authenticator as stauth
16
+ import yaml
17
+ import os
18
 
19
+ # Ensure API keys are stored securely using secrets.toml
20
+ GROQ_API_KEY = st.secrets["GROQ_API_KEY"]
21
+ RAPIDAPI_KEY = st.secrets["RAPIDAPI_KEY"]
22
 
23
  llm = ChatGroq(
24
  temperature=0,
 
26
  model_name="llama-3.1-70b-versatile"
27
  )
28
 
29
+ def load_authentication():
30
+ # Load authentication config from secrets.toml
31
+ config = st.secrets["auth"]
32
+ return config
33
+
34
+ config = load_authentication()
35
+
36
+ authenticator = stauth.Authenticate(
37
+ config['credentials'],
38
+ config['cookie']['name'],
39
+ config['cookie']['key'],
40
+ config['cookie']['expiry_days']
41
+ )
42
+
43
+ name, authentication_status, username = authenticator.login('Login', 'main')
44
+
45
+ if authentication_status:
46
+ authenticator.logout('Logout', 'sidebar')
47
+ st.sidebar.success(f'Welcome *{name}*')
48
+
49
+
50
+ @st.cache_data(ttl=3600)
51
+ def extract_text_from_pdf(pdf_file):
52
+ """
53
+ Extracts text from an uploaded PDF file.
54
+ """
55
+ text = ""
56
+ try:
57
+ with fitz.open(stream=pdf_file.read(), filetype="pdf") as doc:
58
+ for page in doc:
59
+ text += page.get_text()
60
+ return text
61
+ except Exception as e:
62
+ st.error(f"Error extracting text from PDF: {e}")
63
+ return ""
64
+
65
+ @st.cache_data(ttl=3600)
66
+ def extract_job_description(job_link):
67
+ """
68
+ Fetches and extracts job description text from a given URL.
69
+ """
70
+ try:
71
+ headers = {
72
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
73
+ }
74
+ response = requests.get(job_link, headers=headers)
75
+ response.raise_for_status()
76
+ soup = BeautifulSoup(response.text, 'html.parser')
77
+ # You might need to adjust the selectors based on the website's structure
78
+ job_description = soup.get_text(separator='\n')
79
+ return job_description.strip()
80
+ except Exception as e:
81
+ st.error(f"Error fetching job description: {e}")
82
+ return ""
83
+
84
+ @st.cache_data(ttl=3600)
85
+ def extract_requirements(job_description):
86
+ """
87
+ Uses Groq to extract job requirements from the job description.
88
+ """
89
+ prompt = f"""
90
+ The following is a job description:
91
+
92
+ {job_description}
93
+
94
+ Extract the list of job requirements, qualifications, and skills from the job description. Provide them as a numbered list.
95
+
96
+ Requirements:
97
+ """
98
+
99
+ try:
100
+ response = llm.invoke(prompt)
101
+ requirements = response.content.strip()
102
+ return requirements
103
+ except Exception as e:
104
+ st.error(f"Error extracting requirements: {e}")
105
+ return ""
106
+
107
+ @st.cache_data(ttl=3600)
108
+ def generate_email(job_description, requirements, resume_text):
109
+ """
110
+ Generates a personalized cold email using Groq based on the job description, requirements, and resume.
111
+ """
112
+ prompt = f"""
113
+ You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Craft a concise and professional cold email to a potential employer based on the following information:
114
+
115
+ **Job Description:**
116
+ {job_description}
117
+
118
+ **Extracted Requirements:**
119
+ {requirements}
120
+
121
+ **Your Resume:**
122
+ {resume_text}
123
+
124
+ **Email Requirements:**
125
+ - **Introduction:** Briefly introduce yourself and mention the specific job you are applying for.
126
+ - **Body:** Highlight your relevant skills, projects, internships, and leadership experiences that align with the job requirements.
127
+ - **Value Proposition:** Explain how your fresh perspective and recent academic knowledge can add value to the company.
128
+ - **Closing:** Express enthusiasm for the opportunity, mention your willingness for an interview, and thank the recipient for their time.
129
+
130
+ **Email:**
131
+ """
132
+
133
+ try:
134
+ response = llm.invoke(prompt)
135
+ email_text = response.content.strip()
136
+ return email_text
137
+ except Exception as e:
138
+ st.error(f"Error generating email: {e}")
139
+ return ""
140
+
141
+ @st.cache_data(ttl=3600)
142
+ def generate_cover_letter(job_description, requirements, resume_text):
143
+ """
144
+ Generates a personalized cover letter using Groq based on the job description, requirements, and resume.
145
+ """
146
+ prompt = f"""
147
+ You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Compose a personalized and professional cover letter based on the following information:
148
+
149
+ **Job Description:**
150
+ {job_description}
151
+
152
+ **Extracted Requirements:**
153
+ {requirements}
154
+
155
+ **Your Resume:**
156
+ {resume_text}
157
+
158
+ **Cover Letter Requirements:**
159
+ 1. **Greeting:** Address the hiring manager by name if available; otherwise, use a generic greeting such as "Dear Hiring Manager."
160
+ 2. **Introduction:** Begin with an engaging opening that mentions the specific position you are applying for and conveys your enthusiasm.
161
+ 3. **Body:**
162
+ - **Skills and Experiences:** Highlight relevant technical skills, projects, internships, and leadership roles that align with the job requirements.
163
+ - **Alignment:** Demonstrate how your academic background and hands-on experiences make you a suitable candidate for the role.
164
+ 4. **Value Proposition:** Explain how your fresh perspective, recent academic knowledge, and eagerness to learn can contribute to the company's success.
165
+ 5. **Conclusion:** End with a strong closing statement expressing your interest in an interview, your availability, and gratitude for the hiring manager’s time and consideration.
166
+ 6. **Professional Tone:** Maintain a respectful and professional tone throughout the letter.
167
+
168
+ **Cover Letter:**
169
+ """
170
+
171
+ try:
172
+ response = llm.invoke(prompt)
173
+ cover_letter = response.content.strip()
174
+ return cover_letter
175
+ except Exception as e:
176
+ st.error(f"Error generating cover letter: {e}")
177
+ return ""
178
+
179
+ @st.cache_data(ttl=3600)
180
+ def extract_skills(text):
181
+ """
182
+ Extracts a list of skills from the resume text using Groq.
183
+ """
184
+ prompt = f"""
185
+ Extract a comprehensive list of technical and soft skills from the following resume text. Provide the skills as a comma-separated list.
186
+
187
+ Resume Text:
188
+ {text}
189
+
190
+ Skills:
191
+ """
192
+
193
+ try:
194
+ response = llm.invoke(prompt)
195
+ skills = response.content.strip()
196
+ # Clean and split the skills
197
+ skills_list = [skill.strip() for skill in re.split(',|\n', skills) if skill.strip()]
198
+ return skills_list
199
+ except Exception as e:
200
+ st.error(f"Error extracting skills: {e}")
201
+ return []
202
+
203
+ @st.cache_data(ttl=3600)
204
+ def suggest_keywords(resume_text, job_description=None):
205
+ """
206
+ Suggests additional relevant keywords to enhance resume compatibility with ATS.
207
+ """
208
+ prompt = f"""
209
+ Analyze the following resume text and suggest additional relevant keywords that can enhance its compatibility with Applicant Tracking Systems (ATS). If a job description is provided, tailor the keywords to align with the job requirements.
210
+
211
+ Resume Text:
212
+ {resume_text}
213
+
214
+ Job Description:
215
+ {job_description if job_description else "N/A"}
216
+
217
+ Suggested Keywords:
218
+ """
219
+
220
+ try:
221
+ response = llm.invoke(prompt)
222
+ keywords = response.content.strip()
223
+ keywords_list = [keyword.strip() for keyword in re.split(',|\n', keywords) if keyword.strip()]
224
+ return keywords_list
225
+ except Exception as e:
226
+ st.error(f"Error suggesting keywords: {e}")
227
+ return []
228
+
229
+ @st.cache_data(ttl=3600)
230
+ def get_job_recommendations(job_title, location="India"):
231
+ """
232
+ Fetches salary estimates using the Job Salary Data API based on the job title and location.
233
+ """
234
+ url = "https://job-salary-data.p.rapidapi.com/job-salary"
235
+ querystring = {
236
+ "job_title": job_title.strip(),
237
+ "location": location.strip(),
238
+ "radius": "100" # Adjust radius as needed
239
+ }
240
+
241
  headers = {
242
+ "x-rapidapi-key": RAPIDAPI_KEY, # Securely access the API key
243
+ "x-rapidapi-host": "job-salary-data.p.rapidapi.com"
244
  }
245
+
246
+ try:
247
+ response = requests.get(url, headers=headers, params=querystring)
248
+ response.raise_for_status()
249
+ salary_data = response.json()
250
+
251
+ # Adjust the keys based on the API's response structure
252
+ min_salary = salary_data.get("min_salary")
253
+ avg_salary = salary_data.get("avg_salary")
254
+ max_salary = salary_data.get("max_salary")
255
+
256
+ if not all([min_salary, avg_salary, max_salary]):
257
+ st.error("Incomplete salary data received from the API.")
258
+ return {}
259
+
260
+ return {
261
+ "min_salary": min_salary,
262
+ "avg_salary": avg_salary,
263
+ "max_salary": max_salary
264
+ }
265
+ except requests.exceptions.HTTPError as http_err:
266
+ st.error(f"HTTP error occurred: {http_err}")
267
+ return {}
268
+ except Exception as err:
269
+ st.error(f"An error occurred: {err}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  return {}
271
 
272
+ def create_skill_distribution_chart(skills):
273
+ """
274
+ Creates a bar chart showing the distribution of skills.
275
+ """
276
+ skill_counts = {}
277
+ for skill in skills:
278
+ skill_counts[skill] = skill_counts.get(skill, 0) + 1
279
+ df = pd.DataFrame(list(skill_counts.items()), columns=['Skill', 'Count'])
280
+ fig = px.bar(df, x='Skill', y='Count', title='Skill Distribution')
281
+ return fig
282
+
283
+ def create_experience_timeline(resume_text):
284
+ """
285
+ Creates an experience timeline from the resume text.
286
+ """
287
+ # Extract work experience details using Groq
288
+ prompt = f"""
289
+ From the following resume text, extract the job titles, companies, and durations of employment. Provide the information in a table format with columns: Job Title, Company, Duration (in years).
290
+
291
+ Resume Text:
292
+ {resume_text}
293
+
294
+ Table:
295
+ """
296
+
297
+ try:
298
+ response = llm.invoke(prompt)
299
+ table_text = response.content.strip()
300
+ # Parse the table_text to create a DataFrame
301
+ data = []
302
+ for line in table_text.split('\n'):
303
+ if line.strip() and not line.lower().startswith("job title"):
304
+ parts = line.split('|')
305
+ if len(parts) == 3:
306
+ job_title = parts[0].strip()
307
+ company = parts[1].strip()
308
+ duration = parts[2].strip()
309
+ # Convert duration to a float representing years
310
+ duration_years = parse_duration(duration)
311
+ data.append({"Job Title": job_title, "Company": company, "Duration (years)": duration_years})
312
+ df = pd.DataFrame(data)
313
+ if not df.empty:
314
+ # Create a cumulative duration for timeline
315
+ df['Start Year'] = df['Duration (years)'].cumsum() - df['Duration (years)']
316
+ df['End Year'] = df['Duration (years)'].cumsum()
317
+ fig = px.timeline(df, x_start="Start Year", x_end="End Year", y="Job Title", color="Company", title="Experience Timeline")
318
+ fig.update_yaxes(categoryorder="total ascending")
319
+ return fig
320
+ else:
321
+ return None
322
+ except Exception as e:
323
+ st.error(f"Error creating experience timeline: {e}")
 
 
 
 
 
 
 
 
 
324
  return None
325
+
326
+ def parse_duration(duration_str):
327
+ """
328
+ Parses duration strings like '2 years' or '6 months' into float years.
329
+ """
330
+ try:
331
+ if 'year' in duration_str.lower():
332
+ years = float(re.findall(r'\d+\.?\d*', duration_str)[0])
333
+ return years
334
+ elif 'month' in duration_str.lower():
335
+ months = float(re.findall(r'\d+\.?\d*', duration_str)[0])
336
+ return months / 12
337
+ else:
338
+ return 0
339
+ except:
 
340
  return 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
+ # -------------------------------
343
+ # Database Functions
344
+ # -------------------------------
345
+
346
+ def init_db():
347
+ """
348
+ Initializes the SQLite database for application tracking.
349
+ """
350
+ conn = sqlite3.connect('applications.db')
351
+ c = conn.cursor()
352
+ c.execute('''
353
+ CREATE TABLE IF NOT EXISTS applications (
354
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
355
+ job_title TEXT,
356
+ company TEXT,
357
+ application_date TEXT,
358
+ status TEXT,
359
+ deadline TEXT,
360
+ notes TEXT,
361
+ job_description TEXT,
362
+ resume_text TEXT,
363
+ skills TEXT
364
+ )
365
+ ''')
366
+ conn.commit()
367
+ conn.close()
368
+
369
+ def add_application(job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills):
370
+ """
371
+ Adds a new application to the database.
372
+ """
373
+ conn = sqlite3.connect('applications.db')
374
+ c = conn.cursor()
375
+ c.execute('''
376
+ INSERT INTO applications (job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills)
377
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
378
+ ''', (job_title, company, application_date, status, deadline, notes, job_description, resume_text, ', '.join(skills)))
379
+ conn.commit()
380
+ conn.close()
381
+
382
+ def fetch_applications():
383
+ """
384
+ Fetches all applications from the database.
385
+ """
386
+ conn = sqlite3.connect('applications.db')
387
+ c = conn.cursor()
388
+ c.execute('SELECT * FROM applications')
389
+ data = c.fetchall()
390
+ conn.close()
391
+ applications = []
392
+ for app in data:
393
+ applications.append({
394
+ "ID": app[0],
395
+ "Job Title": app[1],
396
+ "Company": app[2],
397
+ "Application Date": app[3],
398
+ "Status": app[4],
399
+ "Deadline": app[5],
400
+ "Notes": app[6],
401
+ "Job Description": app[7],
402
+ "Resume Text": app[8],
403
+ "Skills": app[9].split(', ') if app[9] else []
404
+ })
405
+ return applications
406
+
407
+ def update_application_status(app_id, new_status):
408
+ """
409
+ Updates the status of an application.
410
+ """
411
+ conn = sqlite3.connect('applications.db')
412
+ c = conn.cursor()
413
+ c.execute('UPDATE applications SET status = ? WHERE id = ?', (new_status, app_id))
414
+ conn.commit()
415
+ conn.close()
416
+
417
+ def delete_application(app_id):
418
+ """
419
+ Deletes an application from the database.
420
+ """
421
+ conn = sqlite3.connect('applications.db')
422
+ c = conn.cursor()
423
+ c.execute('DELETE FROM applications WHERE id = ?', (app_id,))
424
+ conn.commit()
425
+ conn.close()
426
+
427
+ def generate_learning_path(career_goal, current_skills):
428
+ """
429
+ Generates a personalized learning path using Groq based on career goal and current skills.
430
+ """
431
+ prompt = f"""
432
+ Based on the following career goal and current skills, create a personalized learning path that includes recommended courses, projects, and milestones to achieve the career goal.
433
+
434
+ **Career Goal:**
435
+ {career_goal}
436
+
437
+ **Current Skills:**
438
+ {current_skills}
439
+
440
+ **Learning Path:**
441
+ """
442
 
443
+ try:
444
+ response = llm.invoke(prompt)
445
+ learning_path = response.content.strip()
446
+ return learning_path
447
+ except Exception as e:
448
+ st.error(f"Error generating learning path: {e}")
449
+ return ""
450
+
451
+ # -------------------------------
452
+ # Page Functions
453
+ # -------------------------------
454
+
455
+ def email_generator_page():
456
+ st.header("Automated Email Generator")
457
+
458
+ st.write("""
459
+ Generate personalized cold emails based on job postings and your resume.
460
+ """)
461
+
462
+ # Create two columns for input fields
463
+ col1, col2 = st.columns(2)
464
+ with col1:
465
+ job_link = st.text_input("Enter the job link:")
466
+ with col2:
467
+ uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
468
 
469
+ if st.button("Generate Email"):
470
+ if not job_link:
471
+ st.error("Please enter a job link.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  return
473
+ if not uploaded_file:
474
+ st.error("Please upload your resume.")
 
 
 
475
  return
476
 
477
+ with st.spinner("Processing..."):
478
+ # Extract job description
479
+ job_description = extract_job_description(job_link)
480
+ if not job_description:
481
+ st.error("Failed to extract job description.")
482
+ return
483
+
484
+ # Extract requirements
485
+ requirements = extract_requirements(job_description)
486
+ if not requirements:
487
+ st.error("Failed to extract requirements.")
488
+ return
489
+
490
+ # Extract resume text
491
+ resume_text = extract_text_from_pdf(uploaded_file)
492
+ if not resume_text:
493
+ st.error("Failed to extract text from resume.")
494
+ return
495
+
496
+ # Generate email
497
+ email_text = generate_email(job_description, requirements, resume_text)
498
+ if email_text:
499
+ st.subheader("Generated Email:")
500
+ st.write(email_text)
501
+ # Provide download option
502
+ st.download_button(
503
+ label="Download Email",
504
+ data=email_text,
505
+ file_name="generated_email.txt",
506
+ mime="text/plain"
507
+ )
508
+ else:
509
+ st.error("Failed to generate email.")
510
+
511
+ def cover_letter_generator_page():
512
+ st.header("Automated Cover Letter Generator")
513
+
514
+ st.write("""
515
+ Generate personalized cover letters based on job postings and your resume.
516
+ """)
517
+
518
+ # Create two columns for input fields
519
+ col1, col2 = st.columns(2)
520
+ with col1:
521
+ job_link = st.text_input("Enter the job link:")
522
+ with col2:
523
+ uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
524
+
525
+ if st.button("Generate Cover Letter"):
526
+ if not job_link:
527
+ st.error("Please enter a job link.")
528
+ return
529
+ if not uploaded_file:
530
+ st.error("Please upload your resume.")
531
  return
532
 
533
+ with st.spinner("Processing..."):
534
+ # Extract job description
535
+ job_description = extract_job_description(job_link)
536
+ if not job_description:
537
+ st.error("Failed to extract job description.")
538
+ return
539
+
540
+ # Extract requirements
541
+ requirements = extract_requirements(job_description)
542
+ if not requirements:
543
+ st.error("Failed to extract requirements.")
544
+ return
545
+
546
+ # Extract resume text
547
+ resume_text = extract_text_from_pdf(uploaded_file)
548
+ if not resume_text:
549
+ st.error("Failed to extract text from resume.")
550
+ return
551
+
552
+ # Generate cover letter
553
+ cover_letter = generate_cover_letter(job_description, requirements, resume_text)
554
+ if cover_letter:
555
+ st.subheader("Generated Cover Letter:")
556
+ st.write(cover_letter)
557
+ # Provide download option
558
+ st.download_button(
559
+ label="Download Cover Letter",
560
+ data=cover_letter,
561
+ file_name="generated_cover_letter.txt",
562
+ mime="text/plain"
563
+ )
564
+ else:
565
+ st.error("Failed to generate cover letter.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
 
567
+ def resume_analysis_page():
568
+ st.header("Resume Analysis and Optimization")
569
+
570
+ uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
571
+
572
+ if uploaded_file:
573
+ resume_text = extract_text_from_pdf(uploaded_file)
574
+ if resume_text:
575
+ st.success("Resume uploaded successfully!")
576
+ # Perform analysis
577
+ st.subheader("Extracted Information")
578
+ # Extracted skills
579
+ skills = extract_skills(resume_text)
580
+ st.write("**Skills:**", ', '.join(skills) if skills else "No skills extracted.")
581
+ # Extract keywords
582
+ keywords = suggest_keywords(resume_text)
583
+ st.write("**Suggested Keywords for ATS Optimization:**", ', '.join(keywords) if keywords else "No keywords suggested.")
584
+ # Provide optimization suggestions
585
+ st.subheader("Optimization Suggestions")
586
+ if keywords:
587
+ st.write("- **Keyword Optimization:** Incorporate the suggested keywords to improve ATS compatibility.")
588
+ else:
589
+ st.write("- **Keyword Optimization:** No keywords suggested.")
590
+ st.write("- **Formatting:** Ensure consistent formatting for headings and bullet points to enhance readability.")
591
+ st.write("- **Experience Details:** Provide specific achievements and quantify your accomplishments where possible.")
592
+
593
+ # Visual Resume Analytics
594
+ st.subheader("Visual Resume Analytics")
595
+ # Skill Distribution Chart
596
+ if skills:
597
+ st.write("**Skill Distribution:**")
598
+ fig_skills = create_skill_distribution_chart(skills)
599
+ st.plotly_chart(fig_skills)
600
+ else:
601
+ st.write("**Skill Distribution:** No skills to display.")
602
+
603
+ # Experience Timeline (if applicable)
604
+ fig_experience = create_experience_timeline(resume_text)
605
+ if fig_experience:
606
+ st.write("**Experience Timeline:**")
607
+ st.plotly_chart(fig_experience)
608
+ else:
609
+ st.write("**Experience Timeline:** Not enough data to generate a timeline.")
610
+
611
+ # Save the resume and analysis to the database
612
+ if st.button("Save Resume Analysis"):
613
+ add_application(
614
+ job_title="N/A",
615
+ company="N/A",
616
+ application_date=datetime.now().strftime("%Y-%m-%d"),
617
+ status="N/A",
618
+ deadline="N/A",
619
+ notes="Resume Analysis",
620
+ job_description="N/A",
621
+ resume_text=resume_text,
622
+ skills=skills
623
+ )
624
+ st.success("Resume analysis saved successfully!")
625
  else:
626
+ st.error("Failed to extract text from resume.")
627
 
628
+ def application_tracking_dashboard():
629
+ st.header("Application Tracking Dashboard")
630
+
631
+ # Initialize database
632
+ init_db()
633
+
634
+ # Form to add a new application
635
+ st.subheader("Add New Application")
636
+ with st.form("add_application"):
637
+ job_title = st.text_input("Job Title")
638
+ company = st.text_input("Company")
639
+ application_date = st.date_input("Application Date", datetime.today())
640
+ status = st.selectbox("Status", ["Applied", "Interviewing", "Offered", "Rejected"])
641
+ deadline = st.date_input("Application Deadline", datetime.today() + timedelta(days=30))
642
+ notes = st.text_area("Notes")
643
+ uploaded_file = st.file_uploader("Upload Job Description (PDF)", type="pdf")
644
+ uploaded_resume = st.file_uploader("Upload Resume (PDF)", type="pdf")
645
+ submitted = st.form_submit_button("Add Application")
646
+ if submitted:
647
+ if uploaded_file:
648
+ job_description = extract_text_from_pdf(uploaded_file)
649
+ else:
650
+ job_description = ""
651
+ if uploaded_resume:
652
+ resume_text = extract_text_from_pdf(uploaded_resume)
653
+ skills = extract_skills(resume_text)
654
+ else:
655
+ resume_text = ""
656
+ skills = []
657
  add_application(
658
+ job_title=job_title,
659
+ company=company,
660
+ application_date=application_date.strftime("%Y-%m-%d"),
661
+ status=status,
662
+ deadline=deadline.strftime("%Y-%m-%d"),
663
+ notes=notes,
664
+ job_description=job_description,
665
  resume_text=resume_text,
666
  skills=skills
667
  )
668
+ st.success("Application added successfully!")
669
+
670
+ # Display applications
671
+ st.subheader("Your Applications")
672
+ applications = fetch_applications()
673
+ if applications:
674
+ df = pd.DataFrame(applications)
675
+ df = df.drop(columns=["Job Description", "Resume Text", "Skills"])
676
+ st.dataframe(df)
677
+
678
+ # Export Button
679
+ csv = df.to_csv(index=False).encode('utf-8')
680
+ st.download_button(
681
+ label="Download Applications as CSV",
682
+ data=csv,
683
+ file_name='applications.csv',
684
+ mime='text/csv',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
 
687
+ # Import Button
688
+ st.subheader("Import Applications")
689
+ uploaded_csv = st.file_uploader("Upload a CSV file", type="csv")
690
+ if uploaded_csv:
691
+ try:
692
+ imported_df = pd.read_csv(uploaded_csv)
693
+ # Validate required columns
694
+ required_columns = {"Job Title", "Company", "Application Date", "Status", "Deadline", "Notes"}
695
+ if not required_columns.issubset(imported_df.columns):
696
+ st.error("Uploaded CSV is missing required columns.")
697
+ else:
698
+ for index, row in imported_df.iterrows():
699
+ job_title = row.get("Job Title", "N/A")
700
+ company = row.get("Company", "N/A")
701
+ application_date = row.get("Application Date", datetime.now().strftime("%Y-%m-%d"))
702
+ status = row.get("Status", "Applied")
703
+ deadline = row.get("Deadline", "")
704
+ notes = row.get("Notes", "")
705
+ job_description = row.get("Job Description", "")
706
+ resume_text = row.get("Resume Text", "")
707
+ skills = row.get("Skills", "").split(', ') if row.get("Skills") else []
708
+ add_application(
709
+ job_title=job_title,
710
+ company=company,
711
+ application_date=application_date,
712
+ status=status,
713
+ deadline=deadline,
714
+ notes=notes,
715
+ job_description=job_description,
716
+ resume_text=resume_text,
717
+ skills=skills
718
+ )
719
+ st.success("Applications imported successfully!")
720
+ except Exception as e:
721
+ st.error(f"Error importing applications: {e}")
722
+
723
+ # Actions: Update Status or Delete
724
+ for app in applications:
725
+ with st.expander(f"{app['Job Title']} at {app['Company']}"):
726
+ st.write(f"**Application Date:** {app['Application Date']}")
727
+ st.write(f"**Deadline:** {app['Deadline']}")
728
+ st.write(f"**Status:** {app['Status']}")
729
+ st.write(f"**Notes:** {app['Notes']}")
730
+ if app['Job Description']:
731
+ st.write("**Job Description:**")
732
+ st.write(app['Job Description'][:500] + "...")
733
+ if app['Skills']:
734
+ st.write("**Skills:**", ', '.join(app['Skills']))
735
+ # Update status
736
+ new_status = st.selectbox("Update Status:", ["Applied", "Interviewing", "Offered", "Rejected"], key=f"status_{app['ID']}")
737
+ if st.button("Update Status", key=f"update_{app['ID']}"):
738
+ update_application_status(app['ID'], new_status)
739
+ st.success("Status updated successfully!")
740
+ # Delete application
741
+ if st.button("Delete Application", key=f"delete_{app['ID']}"):
742
+ delete_application(app['ID'])
743
+ st.success("Application deleted successfully!")
744
+ else:
745
+ st.write("No applications found.")
746
 
747
+ def interview_preparation_module():
748
+ st.header("Interview Preparation")
749
 
750
+ st.write("""
751
+ Prepare for your interviews with tailored mock questions and expert tips.
752
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
 
754
+ # Create two columns for input fields
755
+ col1, col2 = st.columns(2)
756
+ with col1:
757
+ job_title = st.text_input("Enter the job title you're applying for:")
758
+ with col2:
759
+ company = st.text_input("Enter the company name:")
760
+
761
+ if st.button("Generate Mock Interview Questions"):
762
+ if not job_title or not company:
763
+ st.error("Please enter both job title and company name.")
764
+ return
765
+ with st.spinner("Generating questions..."):
766
+ prompt = f"""
767
+ Generate a list of 10 interview questions for a {job_title} position at {company}. Include a mix of technical and behavioral questions.
768
  """
769
  try:
770
+ questions = llm.invoke(prompt).content.strip()
771
+ st.subheader("Mock Interview Questions:")
772
+ st.write(questions)
773
+
774
+ # Optionally, provide sample answers or tips
775
+ if st.checkbox("Show Sample Answers"):
776
+ sample_prompt = f"""
777
+ Provide sample answers for the following interview questions for a {job_title} position at {company}.
778
+
779
+ Questions:
780
+ {questions}
781
+
782
+ Sample Answers:
783
+ """
784
+ try:
785
+ sample_answers = llm.invoke(sample_prompt).content.strip()
786
+ st.subheader("Sample Answers:")
787
+ st.write(sample_answers)
788
+ except Exception as e:
789
+ st.error(f"Error generating sample answers: {e}")
790
  except Exception as e:
791
+ st.error(f"Error generating interview questions: {e}")
 
 
792
 
793
+ def personalized_learning_paths_module():
794
+ st.header("Personalized Learning Paths")
795
 
796
+ st.write("""
797
+ Receive tailored learning plans to help you acquire the skills needed for your desired career.
798
+ """)
799
 
800
+ # Create two columns for input fields
801
+ col1, col2 = st.columns(2)
802
+ with col1:
803
+ career_goal = st.text_input("Enter your career goal (e.g., Data Scientist, Machine Learning Engineer):")
804
+ with col2:
805
+ current_skills = st.text_input("Enter your current skills (comma-separated):")
806
 
807
+ if st.button("Generate Learning Path"):
808
+ if not career_goal or not current_skills:
809
+ st.error("Please enter both career goal and current skills.")
810
+ return
811
+ with st.spinner("Generating your personalized learning path..."):
812
+ learning_path = generate_learning_path(career_goal, current_skills)
813
+ if learning_path:
814
+ st.subheader("Your Personalized Learning Path:")
815
+ st.write(learning_path)
816
+ else:
817
+ st.error("Failed to generate learning path.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
 
819
+ def networking_opportunities_module():
820
+ st.header("Networking Opportunities")
821
+
822
+ st.write("""
823
+ Expand your professional network by connecting with relevant industry peers and joining professional groups.
824
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
825
 
826
+ # Create two columns for input fields
827
+ col1, col2 = st.columns(2)
828
  with col1:
829
+ user_skills = st.text_input("Enter your key skills (comma-separated):")
 
 
 
830
  with col2:
831
+ industry = st.text_input("Enter your industry (e.g., Technology, Finance):")
 
 
 
832
 
833
+ if st.button("Find Networking Opportunities"):
834
+ if not user_skills or not industry:
835
+ st.error("Please enter both key skills and industry.")
836
+ return
837
+ with st.spinner("Fetching networking opportunities..."):
838
+ # Suggest LinkedIn groups or connections based on skills and industry
839
+ prompt = f"""
840
+ Based on the following skills: {user_skills}, and industry: {industry}, suggest relevant LinkedIn groups, professional organizations, and industry events for networking.
841
+ """
842
+ try:
843
+ suggestions = llm.invoke(prompt).content.strip()
844
+ st.subheader("Recommended Networking Groups and Events:")
845
+ st.write(suggestions)
846
+ except Exception as e:
847
+ st.error(f"Error fetching networking opportunities: {e}")
848
 
849
+ def salary_estimation_module():
850
+ st.header("Salary Estimation and Negotiation Tips")
 
851
 
852
+ st.write("""
853
+ Understand the salary expectations for your desired roles and learn effective negotiation strategies.
854
+ """)
855
 
856
+ # Create two columns for input fields
857
+ col1, col2 = st.columns(2)
858
+ with col1:
859
+ job_title = st.text_input("Enter the job title:")
860
+ with col2:
861
+ location = st.text_input("Enter the location (e.g., New York, NY, USA):")
862
 
863
+ if st.button("Get Salary Estimate"):
864
+ if not job_title or not location:
865
+ st.error("Please enter both job title and location.")
866
+ return
867
+ with st.spinner("Fetching salary data..."):
868
+ # Job Salary Data API Integration
869
+ salary_data = get_job_recommendations(job_title, location)
870
+ if salary_data:
871
+ min_salary = salary_data.get("min_salary")
872
+ avg_salary = salary_data.get("avg_salary")
873
+ max_salary = salary_data.get("max_salary")
874
+
875
+ if min_salary and avg_salary and max_salary:
876
+ st.subheader("Salary Estimate:")
877
+ st.write(f"**Minimum Salary:** ${min_salary:,}")
878
+ st.write(f"**Average Salary:** ${avg_salary:,}")
879
+ st.write(f"**Maximum Salary:** ${max_salary:,}")
880
+
881
+ # Visualization
882
+ salary_df = pd.DataFrame({
883
+ "Salary Range": ["Minimum", "Average", "Maximum"],
884
+ "Amount": [min_salary, avg_salary, max_salary]
885
+ })
886
+
887
+ fig = px.bar(salary_df, x="Salary Range", y="Amount",
888
+ title=f"Salary Estimates for {job_title} in {location}",
889
+ labels={"Amount": "Salary (USD)"},
890
+ text_auto=True)
891
+ st.plotly_chart(fig)
892
+ else:
893
+ st.error("Salary data not available for the provided job title and location.")
894
+
895
+ # Generate negotiation tips using Groq
896
+ tips_prompt = f"""
897
+ Provide a list of 5 effective tips for negotiating a salary for a {job_title} position in {location}.
898
+ """
899
+ try:
900
+ tips = llm.invoke(tips_prompt).content.strip()
901
+ st.subheader("Negotiation Tips:")
902
+ st.write(tips)
903
+ except Exception as e:
904
+ st.error(f"Error generating negotiation tips: {e}")
905
+ else:
906
+ st.error("Failed to retrieve salary data.")
907
 
908
+ def feedback_and_improvement_module():
909
+ st.header("Feedback and Continuous Improvement")
910
+
911
+ st.write("""
912
+ We value your feedback! Let us know how we can improve your experience.
913
+ """)
914
+
915
+ with st.form("feedback_form"):
916
+ name = st.text_input("Your Name")
917
+ email = st.text_input("Your Email")
918
+ feedback_type = st.selectbox("Type of Feedback", ["Bug Report", "Feature Request", "General Feedback"])
919
+ feedback = st.text_area("Your Feedback")
920
+ submitted = st.form_submit_button("Submit")
921
+
922
+ if submitted:
923
+ if not name or not email or not feedback:
924
+ st.error("Please fill in all the fields.")
925
+ else:
926
+ # Here you can implement logic to store feedback, e.g., in a database or send via email
927
+ # For demonstration, we'll print to the console
928
+ print(f"Feedback from {name} ({email}): {feedback_type} - {feedback}")
929
+ st.success("Thank you for your feedback!")
930
+
931
+ def gamification_module():
932
+ st.header("Gamification and Achievements")
933
+
934
+ st.write("""
935
+ Stay motivated by earning badges and tracking your progress!
936
+ """)
937
+
938
+ # Initialize database
939
+ init_db()
940
+
941
+ # Example achievements
942
+ applications = fetch_applications()
943
+ num_apps = len(applications)
944
+ achievements = {
945
+ "First Application": num_apps >= 1,
946
+ "5 Applications": num_apps >= 5,
947
+ "10 Applications": num_apps >= 10,
948
+ "Resume Optimized": any(app['Skills'] for app in applications),
949
+ "Interview Scheduled": any(app['Status'] == 'Interviewing' for app in applications)
950
+ }
951
+
952
+ for achievement, earned in achievements.items():
953
+ if earned:
954
+ st.success(f"🎉 {achievement}")
955
+ else:
956
+ st.info(f"🔜 {achievement}")
957
+
958
+ # Progress Bar
959
+ progress = min(num_apps / 10, 1.0) # Ensure progress is between 0.0 and 1.0
960
+ st.write("**Overall Progress:**")
961
+ st.progress(progress)
962
+ st.write(f"{progress * 100:.0f}% complete")
963
+
964
+ def resource_library_page():
965
+ st.header("Resource Library")
966
+
967
+ st.write("""
968
+ Access a collection of templates and guides to enhance your job search.
969
+ """)
970
+
971
+ resources = [
972
+ {
973
+ "title": "Resume Template",
974
+ "description": "A professional resume template in DOCX format.",
975
+ "file": "./resume_template.docx"
976
+ },
977
+ {
978
+ "title": "Cover Letter Template",
979
+ "description": "A customizable cover letter template.",
980
+ "file": "./cover_letter_template.docx"
981
+ },
982
+ {
983
+ "title": "Job Application Checklist",
984
+ "description": "Ensure you have all the necessary steps covered during your job search.",
985
+ "file": "./application_checklist.pdf"
986
+ }
987
+ ]
988
+
989
+ for resource in resources:
990
+ st.markdown(f"### {resource['title']}")
991
+ st.write(resource['description'])
992
  try:
993
+ with open(resource['file'], "rb") as file:
994
+ btn = st.download_button(
995
+ label="Download",
996
+ data=file,
997
+ file_name=os.path.basename(resource['file']),
998
+ mime="application/octet-stream"
999
+ )
1000
+ except FileNotFoundError:
1001
+ st.error(f"File {resource['file']} not found. Please ensure the file is in the correct directory.")
1002
+ st.write("---")
1003
+
1004
+ def success_stories_page():
1005
+ st.header("Success Stories")
1006
+
1007
+ st.write("""
1008
+ Hear from our users who have successfully landed their dream jobs with our assistance!
1009
+ """)
1010
+
1011
+ # Example testimonials
1012
+ testimonials = [
1013
+ {
1014
+ "name": "Rahul Sharma",
1015
+ "position": "Data Scientist at TechCorp",
1016
+ "testimonial": "This app transformed my job search process. The resume analysis and personalized emails were game-changers!",
1017
+ "image": "images/user1.jpg" # Replace with actual image paths
1018
+ },
1019
+ {
1020
+ "name": "Priya Mehta",
1021
+ "position": "Machine Learning Engineer at InnovateX",
1022
+ "testimonial": "The interview preparation module helped me ace my interviews with confidence. Highly recommended!",
1023
+ "image": "images/user2.jpg"
1024
+ }
1025
+ ]
1026
+
1027
+ for user in testimonials:
1028
+ col1, col2 = st.columns([1, 3])
1029
+ with col1:
1030
+ try:
1031
+ st.image(user["image"], width=100)
1032
+ except:
1033
+ st.write("![User Image](https://via.placeholder.com/100)")
1034
+ with col2:
1035
+ st.write(f"**{user['name']}**")
1036
+ st.write(f"*{user['position']}*")
1037
+ st.write(f"\"{user['testimonial']}\"")
1038
+ st.write("---")
1039
+
1040
+ def help_page():
1041
+ st.header("Help & FAQ")
1042
+
1043
+ with st.expander("How do I generate a cover letter?"):
1044
+ st.write("""
1045
+ To generate a cover letter, navigate to the **Cover Letter Generator** section, enter the job link, upload your resume, and click on **Generate Cover Letter**.
1046
+ """)
1047
+
1048
+ with st.expander("How do I track my applications?"):
1049
+ st.write("""
1050
+ Use the **Application Tracking** dashboard to add new applications, update their status, and monitor deadlines.
1051
+ """)
1052
+
1053
+ with st.expander("How can I optimize my resume?"):
1054
+ st.write("""
1055
+ Upload your resume in the **Resume Analysis** section to extract skills and receive optimization suggestions.
1056
+ """)
1057
+
1058
+ with st.expander("How do I import my applications?"):
1059
+ st.write("""
1060
+ In the **Application Tracking** dashboard, use the **Import Applications** section to upload a CSV file containing your applications. Ensure the CSV has the required columns.
1061
+ """)
1062
+
1063
+ with st.expander("How do I provide feedback?"):
1064
+ st.write("""
1065
+ Navigate to the **Feedback and Continuous Improvement** section, fill out the form, and submit your feedback.
1066
+ """)
1067
+
1068
+ def chatbot_support_page():
1069
+ st.header("AI-Powered Chatbot Support")
1070
+
1071
+ st.write("""
1072
+ Have questions or need assistance? Chat with our AI-powered assistant!
1073
+ """)
1074
+
1075
+ # Initialize session state for chatbot
1076
+ if 'chat_history' not in st.session_state:
1077
+ st.session_state['chat_history'] = []
1078
+
1079
+ # User input
1080
+ user_input = st.text_input("You:", key="user_input")
1081
+
1082
+ if st.button("Send"):
1083
+ if user_input:
1084
+ # Append user message to chat history
1085
+ st.session_state['chat_history'].append({"message": user_input, "is_user": True})
1086
+ prompt = f"""
1087
+ You are a helpful assistant for a Job Application Assistant app. Answer the user's query based on the following context:
1088
+
1089
+ {user_input}
1090
+ """
1091
+ try:
1092
+ # Invoke the LLM to get a response
1093
+ response = llm.invoke(prompt).content.strip()
1094
+ # Append assistant response to chat history
1095
+ st.session_state['chat_history'].append({"message": response, "is_user": False})
1096
+ except Exception as e:
1097
+ error_message = "Sorry, I encountered an error while processing your request."
1098
+ st.session_state['chat_history'].append({"message": error_message, "is_user": False})
1099
+ st.error(f"Error in chatbot: {e}")
1100
+
1101
+ # Display chat history using streamlit-chat
1102
+ for chat in st.session_state['chat_history']:
1103
+ if chat['is_user']:
1104
+ message(chat['message'], is_user=True, avatar_style="thumbs")
1105
+ else:
1106
+ message(chat['message'], is_user=False, avatar_style="bottts")
1107
+
1108
+
1109
+ def main_app():
1110
+ # Apply a consistent theme or style
1111
+ st.markdown(
1112
+ """
1113
+ <style>
1114
+ .reportview-container {
1115
+ background-color: #f5f5f5;
1116
+ }
1117
+ .sidebar .sidebar-content {
1118
+ background-image: linear-gradient(#2e7bcf, #2e7bcf);
1119
+ color: white;
1120
+ }
1121
+ </style>
1122
+ """,
1123
+ unsafe_allow_html=True
1124
  )
1125
 
1126
+ # Sidebar Navigation
1127
+ with st.sidebar:
1128
+ selected = option_menu(
1129
+ "Main Menu",
1130
+ ["Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking",
1131
+ "Interview Preparation", "Personalized Learning Paths", "Networking Opportunities",
1132
+ "Salary Estimation", "Feedback", "Gamification", "Resource Library", "Success Stories", "Chatbot Support", "Help"],
1133
+ icons=["envelope", "file-earmark-text", "file-person", "briefcase", "gear",
1134
+ "book", "people", "currency-dollar", "chat-left-text", "trophy", "collection", "star", "chat", "question-circle"],
1135
+ menu_icon="cast",
1136
+ default_index=0,
1137
+ )
1138
+
1139
+ # Route to the selected page
1140
+ if selected == "Email Generator":
1141
+ email_generator_page()
1142
+ elif selected == "Cover Letter Generator":
1143
+ cover_letter_generator_page()
1144
+ elif selected == "Resume Analysis":
1145
+ resume_analysis_page()
1146
+ elif selected == "Application Tracking":
1147
+ application_tracking_dashboard()
1148
+ elif selected == "Interview Preparation":
1149
+ interview_preparation_module()
1150
+ elif selected == "Personalized Learning Paths":
1151
+ personalized_learning_paths_module()
1152
+ elif selected == "Networking Opportunities":
1153
+ networking_opportunities_module()
1154
+ elif selected == "Salary Estimation":
1155
+ salary_estimation_module()
1156
+ elif selected == "Feedback":
1157
+ feedback_and_improvement_module()
1158
+ elif selected == "Gamification":
1159
+ gamification_module()
1160
+ elif selected == "Resource Library":
1161
+ resource_library_page()
1162
+ elif selected == "Success Stories":
1163
+ success_stories_page()
1164
+ elif selected == "Chatbot Support":
1165
+ chatbot_support_page()
1166
+ elif selected == "Help":
1167
+ help_page()
1168
+
1169
+
1170
+ if __name__ == "__main__":
1171
+ main_app()
1172
+
1173
+ elif authentication_status == False:
1174
+ st.error('Username/password is incorrect')
1175
+ elif authentication_status == None:
1176
+ st.warning('Please enter your username and password')