Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
from streamlit_option_menu import option_menu
|
3 |
from langchain_groq import ChatGroq
|
@@ -10,6 +12,9 @@ import plotly.express as px
|
|
10 |
import re
|
11 |
import pandas as pd
|
12 |
import json
|
|
|
|
|
|
|
13 |
|
14 |
# Initialize the LLM with your Groq API key from Streamlit secrets
|
15 |
llm = ChatGroq(
|
@@ -30,7 +35,7 @@ def extract_text_from_pdf(pdf_file):
|
|
30 |
text += page.get_text()
|
31 |
return text
|
32 |
except Exception as e:
|
33 |
-
st.error(f"Error extracting text from
|
34 |
return ""
|
35 |
|
36 |
def extract_job_description(job_link):
|
@@ -159,7 +164,7 @@ def extract_skills(text):
|
|
159 |
|
160 |
skills = response.content.strip()
|
161 |
# Clean and split the skills
|
162 |
-
skills_list = [skill.strip() for skill in re.split('
|
163 |
return skills_list
|
164 |
|
165 |
def suggest_keywords(resume_text, job_description=None):
|
@@ -183,7 +188,7 @@ def suggest_keywords(resume_text, job_description=None):
|
|
183 |
response = chain.invoke({})
|
184 |
|
185 |
keywords = response.content.strip()
|
186 |
-
keywords_list = [keyword.strip() for keyword in re.split('
|
187 |
return keywords_list
|
188 |
|
189 |
def get_job_recommendations(resume_text, location="India"):
|
@@ -211,7 +216,7 @@ def get_job_recommendations(resume_text, location="India"):
|
|
211 |
"title": job.get("title"),
|
212 |
"company": job.get("company_name"),
|
213 |
"link": job.get("url"),
|
214 |
-
"job_description": job.get("description")
|
215 |
}
|
216 |
job_list.append(job_info)
|
217 |
return job_list
|
@@ -288,265 +293,393 @@ def parse_duration(duration_str):
|
|
288 |
except:
|
289 |
return 0
|
290 |
|
291 |
-
|
292 |
-
|
293 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
294 |
|
295 |
-
def
|
296 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
297 |
|
298 |
st.write("""
|
299 |
-
|
300 |
""")
|
301 |
|
302 |
# Input fields
|
303 |
-
|
304 |
-
|
305 |
|
306 |
-
if st.button("Generate
|
307 |
-
if not
|
308 |
-
st.error("Please enter
|
309 |
-
return
|
310 |
-
if not uploaded_file:
|
311 |
-
st.error("Please upload your resume.")
|
312 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
313 |
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
# Extract requirements
|
322 |
-
requirements = extract_requirements(job_description)
|
323 |
-
if not requirements:
|
324 |
-
st.error("Failed to extract requirements.")
|
325 |
-
return
|
326 |
-
|
327 |
-
# Extract resume text
|
328 |
-
resume_text = extract_text_from_pdf(uploaded_file)
|
329 |
-
if not resume_text:
|
330 |
-
st.error("Failed to extract text from resume.")
|
331 |
-
return
|
332 |
-
|
333 |
-
# Generate email
|
334 |
-
email_text = generate_email(job_description, requirements, resume_text)
|
335 |
-
if email_text:
|
336 |
-
st.subheader("Generated Email:")
|
337 |
-
st.write(email_text)
|
338 |
-
# Provide download option
|
339 |
-
st.download_button(
|
340 |
-
label="Download Email",
|
341 |
-
data=email_text,
|
342 |
-
file_name="generated_email.txt",
|
343 |
-
mime="text/plain"
|
344 |
-
)
|
345 |
-
else:
|
346 |
-
st.error("Failed to generate email.")
|
347 |
|
348 |
-
|
349 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
|
351 |
st.write("""
|
352 |
-
|
353 |
""")
|
354 |
|
355 |
-
|
356 |
-
|
357 |
-
uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
|
358 |
|
359 |
-
if st.button("
|
360 |
-
if not
|
361 |
-
st.error("Please enter
|
362 |
return
|
363 |
-
|
364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
365 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
st.error("Failed to extract requirements.")
|
378 |
-
return
|
379 |
-
|
380 |
-
# Extract resume text
|
381 |
-
resume_text = extract_text_from_pdf(uploaded_file)
|
382 |
-
if not resume_text:
|
383 |
-
st.error("Failed to extract text from resume.")
|
384 |
-
return
|
385 |
-
|
386 |
-
# Generate cover letter
|
387 |
-
cover_letter = generate_cover_letter(job_description, requirements, resume_text)
|
388 |
-
if cover_letter:
|
389 |
-
st.subheader("Generated Cover Letter:")
|
390 |
-
st.write(cover_letter)
|
391 |
-
# Provide download option
|
392 |
-
st.download_button(
|
393 |
-
label="Download Cover Letter",
|
394 |
-
data=cover_letter,
|
395 |
-
file_name="generated_cover_letter.txt",
|
396 |
-
mime="text/plain"
|
397 |
-
)
|
398 |
-
else:
|
399 |
-
st.error("Failed to generate cover letter.")
|
400 |
-
|
401 |
-
def resume_analysis_page():
|
402 |
-
import pandas as pd # Importing here to prevent unnecessary imports if not used
|
403 |
-
|
404 |
-
st.header("Resume Analysis and Optimization")
|
405 |
-
|
406 |
-
uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
|
407 |
-
|
408 |
-
if uploaded_file:
|
409 |
-
resume_text = extract_text_from_pdf(uploaded_file)
|
410 |
-
if resume_text:
|
411 |
-
st.success("Resume uploaded successfully!")
|
412 |
-
# Perform analysis
|
413 |
-
st.subheader("Extracted Information")
|
414 |
-
# Extracted skills
|
415 |
-
skills = extract_skills(resume_text)
|
416 |
-
st.write("**Skills:**", ', '.join(skills))
|
417 |
-
# Extract keywords
|
418 |
-
keywords = suggest_keywords(resume_text)
|
419 |
-
st.write("**Suggested Keywords for ATS Optimization:**", ', '.join(keywords))
|
420 |
-
# Provide optimization suggestions
|
421 |
-
st.subheader("Optimization Suggestions")
|
422 |
-
st.write("- **Keyword Optimization:** Incorporate the suggested keywords to improve ATS compatibility.")
|
423 |
-
st.write("- **Formatting:** Ensure consistent formatting for headings and bullet points to enhance readability.")
|
424 |
-
st.write("- **Experience Details:** Provide specific achievements and quantify your accomplishments where possible.")
|
425 |
-
|
426 |
-
# Visual Resume Analytics
|
427 |
-
st.subheader("Visual Resume Analytics")
|
428 |
-
# Skill Distribution Chart
|
429 |
-
if skills:
|
430 |
-
st.write("**Skill Distribution:**")
|
431 |
-
fig_skills = create_skill_distribution_chart(skills)
|
432 |
-
st.plotly_chart(fig_skills)
|
433 |
-
|
434 |
-
# Experience Timeline (if applicable)
|
435 |
-
fig_experience = create_experience_timeline(resume_text)
|
436 |
-
if fig_experience:
|
437 |
-
st.write("**Experience Timeline:**")
|
438 |
-
st.plotly_chart(fig_experience)
|
439 |
-
else:
|
440 |
-
st.write("**Experience Timeline:** Not enough data to generate a timeline.")
|
441 |
-
else:
|
442 |
-
st.error("Failed to extract text from resume.")
|
443 |
-
|
444 |
-
def job_recommendations_page():
|
445 |
-
st.header("Job Recommendations")
|
446 |
-
|
447 |
-
uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
|
448 |
-
|
449 |
-
if uploaded_file:
|
450 |
-
resume_text = extract_text_from_pdf(uploaded_file)
|
451 |
-
if resume_text:
|
452 |
-
st.success("Resume uploaded successfully!")
|
453 |
-
# Fetch job recommendations
|
454 |
-
st.subheader("Recommended Jobs")
|
455 |
-
jobs = get_job_recommendations(resume_text)
|
456 |
-
if jobs:
|
457 |
-
for idx, job in enumerate(jobs, 1):
|
458 |
-
st.markdown(f"### Job {idx}")
|
459 |
-
st.write(f"**{job['title']}** at **{job['company']}**")
|
460 |
-
st.markdown(f"[Apply Here]({job['link']})")
|
461 |
-
st.write("---")
|
462 |
-
else:
|
463 |
-
st.write("No job recommendations found based on your skills.")
|
464 |
-
else:
|
465 |
-
st.error("Failed to extract text from resume.")
|
466 |
-
|
467 |
-
def skill_matching_page():
|
468 |
-
st.header("Skill Matching and Gap Analysis")
|
469 |
-
|
470 |
-
uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
|
471 |
-
|
472 |
-
# Fetch job recommendations once resume is uploaded
|
473 |
-
if uploaded_file:
|
474 |
-
resume_text = extract_text_from_pdf(uploaded_file)
|
475 |
-
if resume_text:
|
476 |
-
st.success("Resume uploaded successfully!")
|
477 |
-
# Fetch job recommendations
|
478 |
-
jobs = get_job_recommendations(resume_text)
|
479 |
-
if jobs:
|
480 |
-
# Display job recommendations with selection
|
481 |
-
job_titles = [f"{job['title']} at {job['company']}" for job in jobs]
|
482 |
-
selected_job = st.selectbox("Select a job for Skill Matching:", job_titles)
|
483 |
-
if selected_job:
|
484 |
-
# Find the selected job's details
|
485 |
-
selected_index = job_titles.index(selected_job)
|
486 |
-
selected_job_info = jobs[selected_index]
|
487 |
-
job_description = selected_job_info['job_description']
|
488 |
-
|
489 |
-
# Extract requirements
|
490 |
-
requirements = extract_requirements(job_description)
|
491 |
-
if not requirements:
|
492 |
-
st.error("Failed to extract requirements.")
|
493 |
-
return
|
494 |
-
|
495 |
-
# Extract skills from resume
|
496 |
-
resume_skills = extract_skills(resume_text)
|
497 |
-
job_skills = extract_skills(job_description)
|
498 |
-
|
499 |
-
# Find matches and gaps
|
500 |
-
matching_skills = set(resume_skills).intersection(set(job_skills))
|
501 |
-
missing_skills = set(job_skills) - set(resume_skills)
|
502 |
-
|
503 |
-
# Display results
|
504 |
-
st.subheader("Matching Skills")
|
505 |
-
st.write(', '.join(matching_skills) if matching_skills else "No matching skills found.")
|
506 |
-
|
507 |
-
st.subheader("Missing Skills")
|
508 |
-
st.write(', '.join(missing_skills) if missing_skills else "No missing skills.")
|
509 |
-
|
510 |
-
# Provide suggestions
|
511 |
-
st.subheader("Suggestions for Improvement")
|
512 |
-
if missing_skills:
|
513 |
-
st.write("- **Acquire Missing Skills:** Consider pursuing courses or certifications in the following areas:")
|
514 |
-
for skill in missing_skills:
|
515 |
-
st.write(f" - {skill}")
|
516 |
-
else:
|
517 |
-
st.write("You have all the required skills for this job!")
|
518 |
|
|
|
|
|
|
|
519 |
else:
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
525 |
|
|
|
|
|
526 |
|
|
|
|
|
|
|
527 |
|
528 |
def main():
|
529 |
st.set_page_config(page_title="Job Application Assistant", layout="wide")
|
530 |
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
550 |
|
551 |
if __name__ == "__main__":
|
552 |
main()
|
|
|
1 |
+
# app.py
|
2 |
+
|
3 |
import streamlit as st
|
4 |
from streamlit_option_menu import option_menu
|
5 |
from langchain_groq import ChatGroq
|
|
|
12 |
import re
|
13 |
import pandas as pd
|
14 |
import json
|
15 |
+
import sqlite3
|
16 |
+
import streamlit_authenticator as stauth
|
17 |
+
from datetime import datetime, timedelta
|
18 |
|
19 |
# Initialize the LLM with your Groq API key from Streamlit secrets
|
20 |
llm = ChatGroq(
|
|
|
35 |
text += page.get_text()
|
36 |
return text
|
37 |
except Exception as e:
|
38 |
+
st.error(f"Error extracting text from PDF: {e}")
|
39 |
return ""
|
40 |
|
41 |
def extract_job_description(job_link):
|
|
|
164 |
|
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 |
|
170 |
def suggest_keywords(resume_text, job_description=None):
|
|
|
188 |
response = chain.invoke({})
|
189 |
|
190 |
keywords = response.content.strip()
|
191 |
+
keywords_list = [keyword.strip() for keyword in re.split(',|\n', keywords) if keyword.strip()]
|
192 |
return keywords_list
|
193 |
|
194 |
def get_job_recommendations(resume_text, location="India"):
|
|
|
216 |
"title": job.get("title"),
|
217 |
"company": job.get("company_name"),
|
218 |
"link": job.get("url"),
|
219 |
+
"job_description": job.get("description") # Including job description for Skill Matching
|
220 |
}
|
221 |
job_list.append(job_info)
|
222 |
return job_list
|
|
|
293 |
except:
|
294 |
return 0
|
295 |
|
296 |
+
def init_db():
|
297 |
+
"""
|
298 |
+
Initializes the SQLite database for application tracking.
|
299 |
+
"""
|
300 |
+
conn = sqlite3.connect('applications.db')
|
301 |
+
c = conn.cursor()
|
302 |
+
c.execute('''
|
303 |
+
CREATE TABLE IF NOT EXISTS applications (
|
304 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
305 |
+
job_title TEXT,
|
306 |
+
company TEXT,
|
307 |
+
application_date TEXT,
|
308 |
+
status TEXT,
|
309 |
+
deadline TEXT,
|
310 |
+
notes TEXT,
|
311 |
+
job_description TEXT,
|
312 |
+
resume_text TEXT,
|
313 |
+
skills TEXT
|
314 |
+
)
|
315 |
+
''')
|
316 |
+
conn.commit()
|
317 |
+
conn.close()
|
318 |
+
|
319 |
+
def add_application(job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills):
|
320 |
+
"""
|
321 |
+
Adds a new application to the database.
|
322 |
+
"""
|
323 |
+
conn = sqlite3.connect('applications.db')
|
324 |
+
c = conn.cursor()
|
325 |
+
c.execute('''
|
326 |
+
INSERT INTO applications (job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills)
|
327 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
328 |
+
''', (job_title, company, application_date, status, deadline, notes, job_description, resume_text, ', '.join(skills)))
|
329 |
+
conn.commit()
|
330 |
+
conn.close()
|
331 |
+
|
332 |
+
def fetch_applications():
|
333 |
+
"""
|
334 |
+
Fetches all applications from the database.
|
335 |
+
"""
|
336 |
+
conn = sqlite3.connect('applications.db')
|
337 |
+
c = conn.cursor()
|
338 |
+
c.execute('SELECT * FROM applications')
|
339 |
+
data = c.fetchall()
|
340 |
+
conn.close()
|
341 |
+
applications = []
|
342 |
+
for app in data:
|
343 |
+
applications.append({
|
344 |
+
"ID": app[0],
|
345 |
+
"Job Title": app[1],
|
346 |
+
"Company": app[2],
|
347 |
+
"Application Date": app[3],
|
348 |
+
"Status": app[4],
|
349 |
+
"Deadline": app[5],
|
350 |
+
"Notes": app[6],
|
351 |
+
"Job Description": app[7],
|
352 |
+
"Resume Text": app[8],
|
353 |
+
"Skills": app[9].split(', ')
|
354 |
+
})
|
355 |
+
return applications
|
356 |
+
|
357 |
+
def update_application_status(app_id, new_status):
|
358 |
+
"""
|
359 |
+
Updates the status of an application.
|
360 |
+
"""
|
361 |
+
conn = sqlite3.connect('applications.db')
|
362 |
+
c = conn.cursor()
|
363 |
+
c.execute('UPDATE applications SET status = ? WHERE id = ?', (new_status, app_id))
|
364 |
+
conn.commit()
|
365 |
+
conn.close()
|
366 |
+
|
367 |
+
def delete_application(app_id):
|
368 |
+
"""
|
369 |
+
Deletes an application from the database.
|
370 |
+
"""
|
371 |
+
conn = sqlite3.connect('applications.db')
|
372 |
+
c = conn.cursor()
|
373 |
+
c.execute('DELETE FROM applications WHERE id = ?', (app_id,))
|
374 |
+
conn.commit()
|
375 |
+
conn.close()
|
376 |
|
377 |
+
def generate_learning_path(career_goal, current_skills):
|
378 |
+
"""
|
379 |
+
Generates a personalized learning path using Groq based on career goal and current skills.
|
380 |
+
"""
|
381 |
+
prompt = f"""
|
382 |
+
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.
|
383 |
+
|
384 |
+
**Career Goal:**
|
385 |
+
{career_goal}
|
386 |
+
|
387 |
+
**Current Skills:**
|
388 |
+
{current_skills}
|
389 |
+
|
390 |
+
**Learning Path:**
|
391 |
+
"""
|
392 |
+
|
393 |
+
response = llm.invoke({"input": prompt})
|
394 |
+
learning_path = response.content.strip()
|
395 |
+
return learning_path
|
396 |
+
|
397 |
+
def setup_authentication():
|
398 |
+
"""
|
399 |
+
Sets up user authentication using streamlit-authenticator.
|
400 |
+
"""
|
401 |
+
# In a real application, retrieve user data from a secure database
|
402 |
+
names = ["Adithya S Nair"] # Replace with dynamic user data
|
403 |
+
usernames = ["adithya"]
|
404 |
+
# Passwords should be hashed in a real application
|
405 |
+
passwords = ["$2b$12$KIXvUq2YdZ19FWJGzTqFUeAewH1/gO7xmD2z77Qvxh3A1F1C9KdaW"] # "password" hashed
|
406 |
+
|
407 |
+
authenticator = stauth.Authenticate(names, usernames, passwords, "job_app_assistant", "abcdef", cookie_expiry_days=30)
|
408 |
+
return authenticator
|
409 |
+
|
410 |
+
def application_tracking_dashboard():
|
411 |
+
st.header("Application Tracking Dashboard")
|
412 |
+
|
413 |
+
# Initialize database
|
414 |
+
init_db()
|
415 |
+
|
416 |
+
# Form to add a new application
|
417 |
+
st.subheader("Add New Application")
|
418 |
+
with st.form("add_application"):
|
419 |
+
job_title = st.text_input("Job Title")
|
420 |
+
company = st.text_input("Company")
|
421 |
+
application_date = st.date_input("Application Date", datetime.today())
|
422 |
+
status = st.selectbox("Status", ["Applied", "Interviewing", "Offered", "Rejected"])
|
423 |
+
deadline = st.date_input("Application Deadline", datetime.today() + timedelta(days=30))
|
424 |
+
notes = st.text_area("Notes")
|
425 |
+
uploaded_file = st.file_uploader("Upload Job Description (PDF)", type="pdf")
|
426 |
+
uploaded_resume = st.file_uploader("Upload Resume (PDF)", type="pdf")
|
427 |
+
submitted = st.form_submit_button("Add Application")
|
428 |
+
if submitted:
|
429 |
+
if uploaded_file:
|
430 |
+
job_description = extract_text_from_pdf(uploaded_file)
|
431 |
+
else:
|
432 |
+
job_description = ""
|
433 |
+
if uploaded_resume:
|
434 |
+
resume_text = extract_text_from_pdf(uploaded_resume)
|
435 |
+
skills = extract_skills(resume_text)
|
436 |
+
else:
|
437 |
+
resume_text = ""
|
438 |
+
skills = []
|
439 |
+
add_application(
|
440 |
+
job_title=job_title,
|
441 |
+
company=company,
|
442 |
+
application_date=application_date.strftime("%Y-%m-%d"),
|
443 |
+
status=status,
|
444 |
+
deadline=deadline.strftime("%Y-%m-%d"),
|
445 |
+
notes=notes,
|
446 |
+
job_description=job_description,
|
447 |
+
resume_text=resume_text,
|
448 |
+
skills=skills
|
449 |
+
)
|
450 |
+
st.success("Application added successfully!")
|
451 |
+
|
452 |
+
# Display applications
|
453 |
+
st.subheader("Your Applications")
|
454 |
+
applications = fetch_applications()
|
455 |
+
if applications:
|
456 |
+
df = pd.DataFrame(applications)
|
457 |
+
df = df.drop(columns=["Job Description", "Resume Text", "Skills"])
|
458 |
+
st.dataframe(df)
|
459 |
+
|
460 |
+
# Actions: Update Status or Delete
|
461 |
+
for app in applications:
|
462 |
+
with st.expander(f"{app['Job Title']} at {app['Company']}"):
|
463 |
+
st.write(f"**Application Date:** {app['Application Date']}")
|
464 |
+
st.write(f"**Deadline:** {app['Deadline']}")
|
465 |
+
st.write(f"**Status:** {app['Status']}")
|
466 |
+
st.write(f"**Notes:** {app['Notes']}")
|
467 |
+
if app['Job Description']:
|
468 |
+
st.write("**Job Description:**")
|
469 |
+
st.write(app['Job Description'][:500] + "...")
|
470 |
+
if app['Skills']:
|
471 |
+
st.write("**Skills:**", ', '.join(app['Skills']))
|
472 |
+
# Update status
|
473 |
+
new_status = st.selectbox("Update Status:", ["Applied", "Interviewing", "Offered", "Rejected"], key=f"status_{app['ID']}")
|
474 |
+
if st.button("Update Status", key=f"update_{app['ID']}"):
|
475 |
+
update_application_status(app['ID'], new_status)
|
476 |
+
st.success("Status updated successfully!")
|
477 |
+
# Delete application
|
478 |
+
if st.button("Delete Application", key=f"delete_{app['ID']}"):
|
479 |
+
delete_application(app['ID'])
|
480 |
+
st.success("Application deleted successfully!")
|
481 |
+
else:
|
482 |
+
st.write("No applications found.")
|
483 |
+
|
484 |
+
def interview_preparation_module():
|
485 |
+
st.header("Interview Preparation")
|
486 |
|
487 |
st.write("""
|
488 |
+
Prepare for your interviews with tailored mock questions and expert tips.
|
489 |
""")
|
490 |
|
491 |
# Input fields
|
492 |
+
job_title = st.text_input("Enter the job title you're applying for:")
|
493 |
+
company = st.text_input("Enter the company name:")
|
494 |
|
495 |
+
if st.button("Generate Mock Interview Questions"):
|
496 |
+
if not job_title or not company:
|
497 |
+
st.error("Please enter both job title and company name.")
|
|
|
|
|
|
|
498 |
return
|
499 |
+
with st.spinner("Generating questions..."):
|
500 |
+
prompt = f"""
|
501 |
+
Generate a list of 10 interview questions for a {job_title} position at {company}. Include a mix of technical and behavioral questions.
|
502 |
+
"""
|
503 |
+
questions = llm.invoke({"input": prompt}).content.strip()
|
504 |
+
st.subheader("Mock Interview Questions:")
|
505 |
+
st.write(questions)
|
506 |
+
|
507 |
+
# Optionally, provide sample answers or tips
|
508 |
+
if st.checkbox("Show Sample Answers"):
|
509 |
+
sample_prompt = f"""
|
510 |
+
Provide sample answers for the following interview questions for a {job_title} position at {company}.
|
511 |
+
|
512 |
+
Questions:
|
513 |
+
{questions}
|
514 |
+
|
515 |
+
Sample Answers:
|
516 |
+
"""
|
517 |
+
sample_answers = llm.invoke({"input": sample_prompt}).content.strip()
|
518 |
+
st.subheader("Sample Answers:")
|
519 |
+
st.write(sample_answers)
|
520 |
+
|
521 |
+
def personalized_learning_paths_module():
|
522 |
+
st.header("Personalized Learning Paths")
|
523 |
|
524 |
+
st.write("""
|
525 |
+
Receive tailored learning plans to help you acquire the skills needed for your desired career.
|
526 |
+
""")
|
527 |
+
|
528 |
+
# Input fields
|
529 |
+
career_goal = st.text_input("Enter your career goal (e.g., Data Scientist, Machine Learning Engineer):")
|
530 |
+
current_skills = st.text_input("Enter your current skills (comma-separated):")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
531 |
|
532 |
+
if st.button("Generate Learning Path"):
|
533 |
+
if not career_goal or not current_skills:
|
534 |
+
st.error("Please enter both career goal and current skills.")
|
535 |
+
return
|
536 |
+
with st.spinner("Generating your personalized learning path..."):
|
537 |
+
learning_path = generate_learning_path(career_goal, current_skills)
|
538 |
+
st.subheader("Your Personalized Learning Path:")
|
539 |
+
st.write(learning_path)
|
540 |
+
|
541 |
+
def networking_opportunities_module():
|
542 |
+
st.header("Networking Opportunities")
|
543 |
|
544 |
st.write("""
|
545 |
+
Expand your professional network by connecting with relevant industry peers and joining professional groups.
|
546 |
""")
|
547 |
|
548 |
+
user_skills = st.text_input("Enter your key skills (comma-separated):")
|
549 |
+
industry = st.text_input("Enter your industry (e.g., Technology, Finance):")
|
|
|
550 |
|
551 |
+
if st.button("Find Networking Opportunities"):
|
552 |
+
if not user_skills or not industry:
|
553 |
+
st.error("Please enter both key skills and industry.")
|
554 |
return
|
555 |
+
with st.spinner("Fetching networking opportunities..."):
|
556 |
+
# Suggest LinkedIn groups or connections based on skills and industry
|
557 |
+
prompt = f"""
|
558 |
+
Based on the following skills: {user_skills}, and industry: {industry}, suggest relevant LinkedIn groups, professional organizations, and industry events for networking.
|
559 |
+
"""
|
560 |
+
suggestions = llm.invoke({"input": prompt}).content.strip()
|
561 |
+
st.subheader("Recommended Networking Groups and Events:")
|
562 |
+
st.write(suggestions)
|
563 |
+
|
564 |
+
def salary_estimation_module():
|
565 |
+
st.header("Salary Estimation and Negotiation Tips")
|
566 |
+
|
567 |
+
st.write("""
|
568 |
+
Understand the salary expectations for your desired roles and learn effective negotiation strategies.
|
569 |
+
""")
|
570 |
+
|
571 |
+
job_title = st.text_input("Enter the job title:")
|
572 |
+
location = st.text_input("Enter the location (e.g., Bangalore, India):")
|
573 |
+
|
574 |
+
if st.button("Get Salary Estimate"):
|
575 |
+
if not job_title or not location:
|
576 |
+
st.error("Please enter both job title and location.")
|
577 |
return
|
578 |
+
with st.spinner("Fetching salary data..."):
|
579 |
+
# Example using a placeholder API or precompiled data
|
580 |
+
# Replace with actual API integration if available
|
581 |
+
# For demonstration, we'll use a mock response
|
582 |
+
prompt = f"""
|
583 |
+
Provide an estimated salary range for a {job_title} in {location}. Include the minimum, average, and maximum salaries.
|
584 |
+
"""
|
585 |
+
salary_data = llm.invoke({"input": prompt}).content.strip()
|
586 |
+
st.subheader("Salary Estimate:")
|
587 |
+
st.write(salary_data)
|
588 |
+
|
589 |
+
# Generate negotiation tips
|
590 |
+
tips_prompt = f"""
|
591 |
+
Provide a list of 5 effective tips for negotiating a salary for a {job_title} position in {location}.
|
592 |
+
"""
|
593 |
+
tips = llm.invoke({"input": tips_prompt}).content.strip()
|
594 |
+
st.subheader("Negotiation Tips:")
|
595 |
+
st.write(tips)
|
596 |
+
|
597 |
+
def feedback_and_improvement_module():
|
598 |
+
st.header("Feedback and Continuous Improvement")
|
599 |
|
600 |
+
st.write("""
|
601 |
+
We value your feedback! Let us know how we can improve your experience.
|
602 |
+
""")
|
603 |
+
|
604 |
+
with st.form("feedback_form"):
|
605 |
+
name = st.text_input("Your Name")
|
606 |
+
email = st.text_input("Your Email")
|
607 |
+
feedback_type = st.selectbox("Type of Feedback", ["Bug Report", "Feature Request", "General Feedback"])
|
608 |
+
feedback = st.text_area("Your Feedback")
|
609 |
+
submitted = st.form_submit_button("Submit")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
610 |
|
611 |
+
if submitted:
|
612 |
+
if not name or not email or not feedback:
|
613 |
+
st.error("Please fill in all the fields.")
|
614 |
else:
|
615 |
+
# Here you can implement logic to store feedback, e.g., in a database or send via email
|
616 |
+
# For demonstration, we'll print to the console
|
617 |
+
print(f"Feedback from {name} ({email}): {feedback_type} - {feedback}")
|
618 |
+
st.success("Thank you for your feedback!")
|
619 |
+
|
620 |
+
# -------------------------------
|
621 |
+
# Authentication Setup
|
622 |
+
# -------------------------------
|
623 |
+
|
624 |
+
def setup_authentication():
|
625 |
+
"""
|
626 |
+
Sets up user authentication using streamlit-authenticator.
|
627 |
+
"""
|
628 |
+
# In a real application, retrieve user data from a secure database
|
629 |
+
names = ["Adithya S Nair"] # Replace with dynamic user data
|
630 |
+
usernames = ["adithya"]
|
631 |
+
# Passwords should be hashed in a real application
|
632 |
+
passwords = ["$2b$12$KIXvUq2YdZ19FWJGzTqFUeAewH1/gO7xmD2z77Qvxh3A1F1C9KdaW"] # "password" hashed
|
633 |
|
634 |
+
authenticator = stauth.Authenticate(names, usernames, passwords, "job_app_assistant", "abcdef", cookie_expiry_days=30)
|
635 |
+
return authenticator
|
636 |
|
637 |
+
# -------------------------------
|
638 |
+
# Main App with Sidebar Navigation
|
639 |
+
# -------------------------------
|
640 |
|
641 |
def main():
|
642 |
st.set_page_config(page_title="Job Application Assistant", layout="wide")
|
643 |
|
644 |
+
# Setup authentication
|
645 |
+
authenticator = setup_authentication()
|
646 |
+
name, authentication_status = authenticator.login("Login", "main")
|
647 |
+
|
648 |
+
if authentication_status:
|
649 |
+
with st.sidebar:
|
650 |
+
selected = option_menu(
|
651 |
+
"Main Menu",
|
652 |
+
["Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking",
|
653 |
+
"Interview Preparation", "Personalized Learning Paths", "Networking Opportunities",
|
654 |
+
"Salary Estimation", "Feedback"],
|
655 |
+
icons=["envelope", "file-earmark-text", "file-person", "briefcase", "gear",
|
656 |
+
"book", "people", "currency-dollar", "chat-left-text"],
|
657 |
+
menu_icon="cast",
|
658 |
+
default_index=0,
|
659 |
+
)
|
660 |
+
|
661 |
+
if selected == "Email Generator":
|
662 |
+
email_generator_page()
|
663 |
+
elif selected == "Cover Letter Generator":
|
664 |
+
cover_letter_generator_page()
|
665 |
+
elif selected == "Resume Analysis":
|
666 |
+
resume_analysis_page()
|
667 |
+
elif selected == "Application Tracking":
|
668 |
+
application_tracking_dashboard()
|
669 |
+
elif selected == "Interview Preparation":
|
670 |
+
interview_preparation_module()
|
671 |
+
elif selected == "Personalized Learning Paths":
|
672 |
+
personalized_learning_paths_module()
|
673 |
+
elif selected == "Networking Opportunities":
|
674 |
+
networking_opportunities_module()
|
675 |
+
elif selected == "Salary Estimation":
|
676 |
+
salary_estimation_module()
|
677 |
+
elif selected == "Feedback":
|
678 |
+
feedback_and_improvement_module()
|
679 |
+
elif authentication_status == False:
|
680 |
+
st.error("Username/password is incorrect")
|
681 |
+
elif authentication_status == None:
|
682 |
+
st.warning("Please enter your username and password")
|
683 |
|
684 |
if __name__ == "__main__":
|
685 |
main()
|