AdithyaSNair commited on
Commit
811efd0
·
verified ·
1 Parent(s): 364ed89

Update app.py

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