Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -17,6 +17,10 @@ from bs4 import BeautifulSoup
|
|
17 |
GROQ_API_KEY = st.secrets["GROQ_API_KEY"]
|
18 |
RAPIDAPI_KEY = st.secrets["RAPIDAPI_KEY"]
|
19 |
YOUTUBE_API_KEY = st.secrets["YOUTUBE_API_KEY"]
|
|
|
|
|
|
|
|
|
20 |
|
21 |
llm = ChatGroq(
|
22 |
temperature=0,
|
@@ -103,8 +107,6 @@ def generate_email(job_description, requirements, resume_text):
|
|
103 |
- **Body:** Highlight your relevant skills, projects, internships, and leadership experiences that align with the job requirements.
|
104 |
- **Value Proposition:** Explain how your fresh perspective and recent academic knowledge can add value to the company.
|
105 |
- **Closing:** Express enthusiasm for the opportunity, mention your willingness for an interview, and thank the recipient for their time.
|
106 |
-
|
107 |
-
**Email:**
|
108 |
"""
|
109 |
|
110 |
try:
|
@@ -141,8 +143,6 @@ def generate_cover_letter(job_description, requirements, resume_text):
|
|
141 |
4. **Value Proposition:** Explain how your fresh perspective, recent academic knowledge, and eagerness to learn can contribute to the company's success.
|
142 |
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.
|
143 |
6. **Professional Tone:** Maintain a respectful and professional tone throughout the letter.
|
144 |
-
|
145 |
-
**Cover Letter:**
|
146 |
"""
|
147 |
|
148 |
try:
|
@@ -317,65 +317,284 @@ def parse_duration(duration_str):
|
|
317 |
return 0
|
318 |
|
319 |
# -------------------------------
|
320 |
-
#
|
321 |
# -------------------------------
|
322 |
|
|
|
323 |
@st.cache_data(ttl=86400) # Cache results for 1 day
|
324 |
-
def
|
325 |
"""
|
326 |
-
|
327 |
-
|
328 |
Args:
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
|
|
334 |
Returns:
|
335 |
-
list: A list of
|
336 |
"""
|
337 |
-
|
338 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
params = {
|
341 |
-
"
|
342 |
-
"
|
343 |
-
"
|
344 |
-
"
|
345 |
-
"
|
346 |
-
"order": order,
|
347 |
-
"key": api_key
|
348 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
349 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
try:
|
351 |
-
response = requests.get(
|
352 |
response.raise_for_status()
|
353 |
-
|
354 |
-
|
355 |
-
return video_urls
|
356 |
except requests.exceptions.RequestException as e:
|
357 |
-
st.error(f"Error fetching
|
358 |
return []
|
359 |
|
360 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
361 |
"""
|
362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
363 |
|
|
|
|
|
|
|
|
|
|
|
364 |
Args:
|
365 |
-
|
366 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
367 |
"""
|
368 |
-
|
369 |
-
|
370 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
371 |
|
372 |
# -------------------------------
|
373 |
-
# Database Functions
|
374 |
# -------------------------------
|
375 |
|
376 |
def init_db():
|
377 |
"""
|
378 |
-
Initializes the SQLite database
|
379 |
"""
|
380 |
conn = sqlite3.connect('applications.db')
|
381 |
c = conn.cursor()
|
@@ -398,7 +617,7 @@ def init_db():
|
|
398 |
|
399 |
def add_application(job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills):
|
400 |
"""
|
401 |
-
Adds a new application to the database.
|
402 |
"""
|
403 |
conn = sqlite3.connect('applications.db')
|
404 |
c = conn.cursor()
|
@@ -454,6 +673,11 @@ def delete_application(app_id):
|
|
454 |
conn.commit()
|
455 |
conn.close()
|
456 |
|
|
|
|
|
|
|
|
|
|
|
457 |
def generate_learning_path(career_goal, current_skills):
|
458 |
"""
|
459 |
Generates a personalized learning path using Groq based on career goal and current skills.
|
@@ -478,12 +702,322 @@ def generate_learning_path(career_goal, current_skills):
|
|
478 |
st.error(f"Error generating learning path: {e}")
|
479 |
return ""
|
480 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
481 |
# -------------------------------
|
482 |
# Page Functions
|
483 |
# -------------------------------
|
484 |
|
485 |
def email_generator_page():
|
486 |
-
st.header("Automated Email Generator")
|
487 |
|
488 |
st.write("""
|
489 |
Generate personalized cold emails based on job postings and your resume.
|
@@ -492,9 +1026,9 @@ def email_generator_page():
|
|
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 Email"):
|
500 |
if not job_link:
|
@@ -526,7 +1060,7 @@ def email_generator_page():
|
|
526 |
# Generate email
|
527 |
email_text = generate_email(job_description, requirements, resume_text)
|
528 |
if email_text:
|
529 |
-
st.subheader("Generated Email:")
|
530 |
st.write(email_text)
|
531 |
# Provide download option
|
532 |
st.download_button(
|
@@ -539,7 +1073,7 @@ def email_generator_page():
|
|
539 |
st.error("Failed to generate email.")
|
540 |
|
541 |
def cover_letter_generator_page():
|
542 |
-
st.header("Automated Cover Letter Generator")
|
543 |
|
544 |
st.write("""
|
545 |
Generate personalized cover letters based on job postings and your resume.
|
@@ -548,9 +1082,9 @@ def cover_letter_generator_page():
|
|
548 |
# Create two columns for input fields
|
549 |
col1, col2 = st.columns(2)
|
550 |
with col1:
|
551 |
-
job_link = st.text_input("Enter the job link:")
|
552 |
with col2:
|
553 |
-
uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf")
|
554 |
|
555 |
if st.button("Generate Cover Letter"):
|
556 |
if not job_link:
|
@@ -582,7 +1116,7 @@ def cover_letter_generator_page():
|
|
582 |
# Generate cover letter
|
583 |
cover_letter = generate_cover_letter(job_description, requirements, resume_text)
|
584 |
if cover_letter:
|
585 |
-
st.subheader("Generated Cover Letter:")
|
586 |
st.write(cover_letter)
|
587 |
# Provide download option
|
588 |
st.download_button(
|
@@ -618,7 +1152,7 @@ def resume_analysis_page():
|
|
618 |
skills = extract_skills(resume_text)
|
619 |
if skills:
|
620 |
st.markdown("**Identified Skills:**")
|
621 |
-
# Display skills as
|
622 |
cols = st.columns(4)
|
623 |
for idx, skill in enumerate(skills, 1):
|
624 |
cols[idx % 4].write(f"- {skill}")
|
@@ -629,7 +1163,7 @@ def resume_analysis_page():
|
|
629 |
keywords = suggest_keywords(resume_text)
|
630 |
if keywords:
|
631 |
st.markdown("**Suggested Keywords for ATS Optimization:**")
|
632 |
-
# Display keywords as
|
633 |
cols = st.columns(4)
|
634 |
for idx, keyword in enumerate(keywords, 1):
|
635 |
cols[idx % 4].write(f"- {keyword}")
|
@@ -691,25 +1225,24 @@ def resume_analysis_page():
|
|
691 |
else:
|
692 |
st.error("❌ Failed to extract text from resume.")
|
693 |
|
694 |
-
|
695 |
def application_tracking_dashboard():
|
696 |
-
st.header("Application Tracking Dashboard")
|
697 |
|
698 |
# Initialize database
|
699 |
init_db()
|
700 |
|
701 |
# Form to add a new application
|
702 |
-
st.subheader("Add New Application")
|
703 |
with st.form("add_application"):
|
704 |
-
job_title = st.text_input("Job Title")
|
705 |
-
company = st.text_input("Company")
|
706 |
-
application_date = st.date_input("Application Date", datetime.today())
|
707 |
-
status = st.selectbox("Status", ["Applied", "Interviewing", "Offered", "Rejected"])
|
708 |
-
deadline = st.date_input("Application Deadline", datetime.today() + timedelta(days=30))
|
709 |
-
notes = st.text_area("Notes")
|
710 |
-
uploaded_file = st.file_uploader("Upload Job Description (PDF)", type="pdf")
|
711 |
-
uploaded_resume = st.file_uploader("Upload Resume (PDF)", type="pdf")
|
712 |
-
submitted = st.form_submit_button("Add Application")
|
713 |
if submitted:
|
714 |
if uploaded_file:
|
715 |
job_description = extract_text_from_pdf(uploaded_file)
|
@@ -732,10 +1265,10 @@ def application_tracking_dashboard():
|
|
732 |
resume_text=resume_text,
|
733 |
skills=skills
|
734 |
)
|
735 |
-
st.success("Application added successfully!")
|
736 |
|
737 |
# Display applications
|
738 |
-
st.subheader("Your Applications")
|
739 |
applications = fetch_applications()
|
740 |
if applications:
|
741 |
df = pd.DataFrame(applications)
|
@@ -745,22 +1278,22 @@ def application_tracking_dashboard():
|
|
745 |
# Export Button
|
746 |
csv = df.to_csv(index=False).encode('utf-8')
|
747 |
st.download_button(
|
748 |
-
label="Download Applications as CSV",
|
749 |
data=csv,
|
750 |
file_name='applications.csv',
|
751 |
mime='text/csv',
|
752 |
)
|
753 |
|
754 |
# Import Button
|
755 |
-
st.subheader("Import Applications")
|
756 |
-
uploaded_csv = st.file_uploader("Upload a CSV file", type="csv")
|
757 |
if uploaded_csv:
|
758 |
try:
|
759 |
imported_df = pd.read_csv(uploaded_csv)
|
760 |
# Validate required columns
|
761 |
required_columns = {"Job Title", "Company", "Application Date", "Status", "Deadline", "Notes"}
|
762 |
if not required_columns.issubset(imported_df.columns):
|
763 |
-
st.error("Uploaded CSV is missing required columns.")
|
764 |
else:
|
765 |
for index, row in imported_df.iterrows():
|
766 |
job_title = row.get("Job Title", "N/A")
|
@@ -783,36 +1316,99 @@ def application_tracking_dashboard():
|
|
783 |
resume_text=resume_text,
|
784 |
skills=skills
|
785 |
)
|
786 |
-
st.success("Applications imported successfully!")
|
787 |
except Exception as e:
|
788 |
-
st.error(f"Error importing applications: {e}")
|
789 |
|
790 |
# Actions: Update Status or Delete
|
791 |
for app in applications:
|
792 |
with st.expander(f"{app['Job Title']} at {app['Company']}"):
|
793 |
-
st.write(f"
|
794 |
-
st.write(f"
|
795 |
-
st.write(f"
|
796 |
-
st.write(f"
|
797 |
if app['Job Description']:
|
798 |
-
st.write("
|
799 |
st.write(app['Job Description'][:500] + "...")
|
800 |
if app['Skills']:
|
801 |
-
st.write("
|
802 |
# Update status
|
803 |
-
new_status = st.selectbox("Update Status:", ["Applied", "Interviewing", "Offered", "Rejected"], key=f"status_{app['ID']}")
|
804 |
-
if st.button("Update Status", key=f"update_{app['ID']}"):
|
805 |
update_application_status(app['ID'], new_status)
|
806 |
-
st.success("Status updated successfully!")
|
807 |
# Delete application
|
808 |
-
if st.button("Delete Application", key=f"delete_{app['ID']}"):
|
809 |
delete_application(app['ID'])
|
810 |
-
st.success("Application deleted successfully!")
|
811 |
else:
|
812 |
-
st.write("No applications found.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
813 |
|
814 |
def interview_preparation_module():
|
815 |
-
st.header("Interview Preparation")
|
816 |
|
817 |
st.write("""
|
818 |
Prepare for your interviews with tailored mock questions and expert answers.
|
@@ -821,15 +1417,15 @@ def interview_preparation_module():
|
|
821 |
# Create two columns for input fields
|
822 |
col1, col2 = st.columns(2)
|
823 |
with col1:
|
824 |
-
job_title = st.text_input("Enter the job title you're applying for:")
|
825 |
with col2:
|
826 |
-
company = st.text_input("Enter the company name:")
|
827 |
|
828 |
-
if st.button("Generate Mock Interview Questions"):
|
829 |
if not job_title or not company:
|
830 |
-
st.error("Please enter both job title and company name.")
|
831 |
return
|
832 |
-
with st.spinner("Generating questions..."):
|
833 |
# Prompt to generate 50 interview questions with answers
|
834 |
prompt = f"""
|
835 |
Generate a list of 50 interview questions along with their answers for the position of {job_title} at {company}. Each question should be followed by a concise and professional answer.
|
@@ -840,7 +1436,7 @@ def interview_preparation_module():
|
|
840 |
qa_text = llm.invoke(prompt).content.strip()
|
841 |
# Split into question-answer pairs
|
842 |
qa_pairs = qa_text.split('\n\n')
|
843 |
-
st.subheader("Mock Interview Questions and Answers:")
|
844 |
for idx, qa in enumerate(qa_pairs, 1):
|
845 |
if qa.strip():
|
846 |
parts = qa.split('\n', 1)
|
@@ -851,10 +1447,10 @@ def interview_preparation_module():
|
|
851 |
st.markdown(f"**A:** {answer}")
|
852 |
st.write("---")
|
853 |
except Exception as e:
|
854 |
-
st.error(f"Error generating interview questions: {e}")
|
855 |
|
856 |
def personalized_learning_paths_module():
|
857 |
-
st.header("Personalized Learning Paths")
|
858 |
|
859 |
st.write("""
|
860 |
Receive tailored learning plans to help you acquire the skills needed for your desired career, complemented with curated video resources.
|
@@ -863,18 +1459,18 @@ def personalized_learning_paths_module():
|
|
863 |
# Create two columns for input fields
|
864 |
col1, col2 = st.columns(2)
|
865 |
with col1:
|
866 |
-
career_goal = st.text_input("Enter your career goal (e.g., Data Scientist, Machine Learning Engineer):")
|
867 |
with col2:
|
868 |
-
current_skills = st.text_input("Enter your current skills (comma-separated):")
|
869 |
|
870 |
-
if st.button("Generate Learning Path"):
|
871 |
if not career_goal or not current_skills:
|
872 |
-
st.error("Please enter both career goal and current skills.")
|
873 |
return
|
874 |
-
with st.spinner("Generating your personalized learning path..."):
|
875 |
learning_path = generate_learning_path(career_goal, current_skills)
|
876 |
if learning_path:
|
877 |
-
st.subheader("Your Personalized Learning Path:")
|
878 |
st.write(learning_path)
|
879 |
|
880 |
# Assuming the learning path is divided into modules/subparts separated by newlines or numbering
|
@@ -888,7 +1484,7 @@ def personalized_learning_paths_module():
|
|
888 |
modules = re.split(r'\d+\.\s+', learning_path)
|
889 |
modules = [module.strip() for module in modules if module.strip()]
|
890 |
|
891 |
-
st.subheader("Recommended YouTube Videos for Each Module:")
|
892 |
for module in modules:
|
893 |
# Search for long videos related to the module
|
894 |
video_urls = search_youtube_videos(query=module, max_results=2, video_duration="long")
|
@@ -898,10 +1494,10 @@ def personalized_learning_paths_module():
|
|
898 |
else:
|
899 |
st.write(f"No videos found for **{module}**.")
|
900 |
else:
|
901 |
-
st.error("Failed to generate learning path.")
|
902 |
|
903 |
def networking_opportunities_module():
|
904 |
-
st.header("Networking Opportunities")
|
905 |
|
906 |
st.write("""
|
907 |
Expand your professional network by connecting with relevant industry peers and joining professional groups.
|
@@ -910,28 +1506,28 @@ def networking_opportunities_module():
|
|
910 |
# Create two columns for input fields
|
911 |
col1, col2 = st.columns(2)
|
912 |
with col1:
|
913 |
-
user_skills = st.text_input("Enter your key skills (comma-separated):")
|
914 |
with col2:
|
915 |
-
industry = st.text_input("Enter your industry (e.g., Technology, Finance):")
|
916 |
|
917 |
-
if st.button("Find Networking Opportunities"):
|
918 |
if not user_skills or not industry:
|
919 |
-
st.error("Please enter both key skills and industry.")
|
920 |
return
|
921 |
-
with st.spinner("Fetching networking opportunities..."):
|
922 |
# Suggest LinkedIn groups or connections based on skills and industry
|
923 |
prompt = f"""
|
924 |
Based on the following skills: {user_skills}, and industry: {industry}, suggest relevant LinkedIn groups, professional organizations, and industry events for networking.
|
925 |
"""
|
926 |
try:
|
927 |
suggestions = llm.invoke(prompt).content.strip()
|
928 |
-
st.subheader("Recommended Networking Groups and Events:")
|
929 |
st.write(suggestions)
|
930 |
except Exception as e:
|
931 |
-
st.error(f"Error fetching networking opportunities: {e}")
|
932 |
|
933 |
def salary_estimation_module():
|
934 |
-
st.header("Salary Estimation and Negotiation Tips")
|
935 |
|
936 |
st.write("""
|
937 |
Understand the salary expectations for your desired roles and learn effective negotiation strategies.
|
@@ -940,15 +1536,15 @@ def salary_estimation_module():
|
|
940 |
# Create two columns for input fields
|
941 |
col1, col2 = st.columns(2)
|
942 |
with col1:
|
943 |
-
job_title = st.text_input("Enter the job title:")
|
944 |
with col2:
|
945 |
-
location = st.text_input("Enter the location (e.g., New York, NY, USA):")
|
946 |
|
947 |
-
if st.button("Get Salary Estimate"):
|
948 |
if not job_title or not location:
|
949 |
-
st.error("Please enter both job title and location.")
|
950 |
return
|
951 |
-
with st.spinner("Fetching salary data..."):
|
952 |
# Job Salary Data API Integration
|
953 |
salary_data = get_job_recommendations(job_title, location)
|
954 |
if salary_data:
|
@@ -957,7 +1553,7 @@ def salary_estimation_module():
|
|
957 |
max_salary = salary_data.get("max_salary")
|
958 |
|
959 |
if min_salary and avg_salary and max_salary:
|
960 |
-
st.subheader("Salary Estimate:")
|
961 |
st.write(f"**Minimum Salary:** ${min_salary:,}")
|
962 |
st.write(f"**Average Salary:** ${avg_salary:,}")
|
963 |
st.write(f"**Maximum Salary:** ${max_salary:,}")
|
@@ -972,9 +1568,9 @@ def salary_estimation_module():
|
|
972 |
title=f"Salary Estimates for {job_title} in {location}",
|
973 |
labels={"Amount": "Salary (USD)"},
|
974 |
text_auto=True)
|
975 |
-
st.plotly_chart(fig)
|
976 |
else:
|
977 |
-
st.error("Salary data not available for the provided job title and location.")
|
978 |
|
979 |
# Generate negotiation tips using Groq
|
980 |
tips_prompt = f"""
|
@@ -982,38 +1578,38 @@ def salary_estimation_module():
|
|
982 |
"""
|
983 |
try:
|
984 |
tips = llm.invoke(tips_prompt).content.strip()
|
985 |
-
st.subheader("Negotiation Tips:")
|
986 |
st.write(tips)
|
987 |
except Exception as e:
|
988 |
-
st.error(f"Error generating negotiation tips: {e}")
|
989 |
else:
|
990 |
-
st.error("Failed to retrieve salary data.")
|
991 |
|
992 |
def feedback_and_improvement_module():
|
993 |
-
st.header("Feedback and Continuous Improvement")
|
994 |
|
995 |
st.write("""
|
996 |
We value your feedback! Let us know how we can improve your experience.
|
997 |
""")
|
998 |
|
999 |
with st.form("feedback_form"):
|
1000 |
-
name = st.text_input("Your Name")
|
1001 |
-
email = st.text_input("Your Email")
|
1002 |
-
feedback_type = st.selectbox("Type of Feedback", ["Bug Report", "Feature Request", "General Feedback"])
|
1003 |
-
feedback = st.text_area("Your Feedback")
|
1004 |
-
submitted = st.form_submit_button("Submit")
|
1005 |
-
|
1006 |
if submitted:
|
1007 |
if not name or not email or not feedback:
|
1008 |
-
st.error("Please fill in all the fields.")
|
1009 |
else:
|
1010 |
# Here you can implement logic to store feedback, e.g., in a database or send via email
|
1011 |
# For demonstration, we'll print to the console
|
1012 |
print(f"Feedback from {name} ({email}): {feedback_type} - {feedback}")
|
1013 |
-
st.success("Thank you for your feedback!")
|
1014 |
|
1015 |
def gamification_module():
|
1016 |
-
st.header("Gamification and Achievements")
|
1017 |
|
1018 |
st.write("""
|
1019 |
Stay motivated by earning badges and tracking your progress!
|
@@ -1046,7 +1642,7 @@ def gamification_module():
|
|
1046 |
st.write(f"{progress * 100:.0f}% complete")
|
1047 |
|
1048 |
def resource_library_page():
|
1049 |
-
st.header("Resource Library")
|
1050 |
|
1051 |
st.write("""
|
1052 |
Access a collection of templates and guides to enhance your job search.
|
@@ -1076,17 +1672,17 @@ def resource_library_page():
|
|
1076 |
try:
|
1077 |
with open(resource['file'], "rb") as file:
|
1078 |
btn = st.download_button(
|
1079 |
-
label="Download",
|
1080 |
data=file,
|
1081 |
file_name=os.path.basename(resource['file']),
|
1082 |
mime="application/octet-stream"
|
1083 |
)
|
1084 |
except FileNotFoundError:
|
1085 |
-
st.error(f"File {resource['file']} not found. Please ensure the file is in the correct directory.")
|
1086 |
st.write("---")
|
1087 |
|
1088 |
def success_stories_page():
|
1089 |
-
st.header("Success Stories")
|
1090 |
|
1091 |
st.write("""
|
1092 |
Hear from our users who have successfully landed their dream jobs with our assistance!
|
@@ -1121,36 +1717,8 @@ def success_stories_page():
|
|
1121 |
st.write(f"\"{user['testimonial']}\"")
|
1122 |
st.write("---")
|
1123 |
|
1124 |
-
def help_page():
|
1125 |
-
st.header("Help & FAQ")
|
1126 |
-
|
1127 |
-
with st.expander("How do I generate a cover letter?"):
|
1128 |
-
st.write("""
|
1129 |
-
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**.
|
1130 |
-
""")
|
1131 |
-
|
1132 |
-
with st.expander("How do I track my applications?"):
|
1133 |
-
st.write("""
|
1134 |
-
Use the **Application Tracking** dashboard to add new applications, update their status, and monitor deadlines.
|
1135 |
-
""")
|
1136 |
-
|
1137 |
-
with st.expander("How can I optimize my resume?"):
|
1138 |
-
st.write("""
|
1139 |
-
Upload your resume in the **Resume Analysis** section to extract skills and receive optimization suggestions.
|
1140 |
-
""")
|
1141 |
-
|
1142 |
-
with st.expander("How do I import my applications?"):
|
1143 |
-
st.write("""
|
1144 |
-
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.
|
1145 |
-
""")
|
1146 |
-
|
1147 |
-
with st.expander("How do I provide feedback?"):
|
1148 |
-
st.write("""
|
1149 |
-
Navigate to the **Feedback and Continuous Improvement** section, fill out the form, and submit your feedback.
|
1150 |
-
""")
|
1151 |
-
|
1152 |
def chatbot_support_page():
|
1153 |
-
st.header("AI-Powered Chatbot Support")
|
1154 |
|
1155 |
st.write("""
|
1156 |
Have questions or need assistance? Chat with our AI-powered assistant!
|
@@ -1161,7 +1729,7 @@ def chatbot_support_page():
|
|
1161 |
st.session_state['chat_history'] = []
|
1162 |
|
1163 |
# User input
|
1164 |
-
user_input = st.text_input("You:", key="user_input")
|
1165 |
|
1166 |
if st.button("Send"):
|
1167 |
if user_input:
|
@@ -1174,13 +1742,14 @@ def chatbot_support_page():
|
|
1174 |
"""
|
1175 |
try:
|
1176 |
# Invoke the LLM to get a response
|
1177 |
-
response = llm.invoke(prompt)
|
|
|
1178 |
# Append assistant response to chat history
|
1179 |
-
st.session_state['chat_history'].append({"message":
|
1180 |
except Exception as e:
|
1181 |
-
error_message = "Sorry, I encountered an error while processing your request."
|
1182 |
st.session_state['chat_history'].append({"message": error_message, "is_user": False})
|
1183 |
-
st.error(f"Error in chatbot: {e}")
|
1184 |
|
1185 |
# Display chat history using streamlit-chat
|
1186 |
for chat in st.session_state['chat_history']:
|
@@ -1189,6 +1758,175 @@ def chatbot_support_page():
|
|
1189 |
else:
|
1190 |
message(chat['message'], is_user=False, avatar_style="bottts")
|
1191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1192 |
# -------------------------------
|
1193 |
# Main App Function
|
1194 |
# -------------------------------
|
@@ -1213,12 +1951,15 @@ def main_app():
|
|
1213 |
# Sidebar Navigation using streamlit_option_menu
|
1214 |
with st.sidebar:
|
1215 |
selected = option_menu(
|
1216 |
-
menu_title="Main Menu",
|
1217 |
options=["Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking",
|
1218 |
-
"Interview Preparation", "Personalized Learning Paths",
|
1219 |
-
"Salary Estimation", "Feedback", "Gamification", "Resource Library",
|
1220 |
-
|
1221 |
-
|
|
|
|
|
|
|
1222 |
menu_icon="cast",
|
1223 |
default_index=0,
|
1224 |
styles={
|
@@ -1238,6 +1979,10 @@ def main_app():
|
|
1238 |
resume_analysis_page()
|
1239 |
elif selected == "Application Tracking":
|
1240 |
application_tracking_dashboard()
|
|
|
|
|
|
|
|
|
1241 |
elif selected == "Interview Preparation":
|
1242 |
interview_preparation_module()
|
1243 |
elif selected == "Personalized Learning Paths":
|
|
|
17 |
GROQ_API_KEY = st.secrets["GROQ_API_KEY"]
|
18 |
RAPIDAPI_KEY = st.secrets["RAPIDAPI_KEY"]
|
19 |
YOUTUBE_API_KEY = st.secrets["YOUTUBE_API_KEY"]
|
20 |
+
ADZUNA_APP_ID = st.secrets["ADZUNA_APP_ID"]
|
21 |
+
ADZUNA_APP_KEY = st.secrets["ADZUNA_APP_KEY"]
|
22 |
+
THE_MUSE_API_KEY = st.secrets.get("THE_MUSE_API_KEY", "")
|
23 |
+
BLS_API_KEY = st.secrets.get("BLS_API_KEY", "")
|
24 |
|
25 |
llm = ChatGroq(
|
26 |
temperature=0,
|
|
|
107 |
- **Body:** Highlight your relevant skills, projects, internships, and leadership experiences that align with the job requirements.
|
108 |
- **Value Proposition:** Explain how your fresh perspective and recent academic knowledge can add value to the company.
|
109 |
- **Closing:** Express enthusiasm for the opportunity, mention your willingness for an interview, and thank the recipient for their time.
|
|
|
|
|
110 |
"""
|
111 |
|
112 |
try:
|
|
|
143 |
4. **Value Proposition:** Explain how your fresh perspective, recent academic knowledge, and eagerness to learn can contribute to the company's success.
|
144 |
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.
|
145 |
6. **Professional Tone:** Maintain a respectful and professional tone throughout the letter.
|
|
|
|
|
146 |
"""
|
147 |
|
148 |
try:
|
|
|
317 |
return 0
|
318 |
|
319 |
# -------------------------------
|
320 |
+
# API Integration Functions
|
321 |
# -------------------------------
|
322 |
|
323 |
+
# Remotive API Integration
|
324 |
@st.cache_data(ttl=86400) # Cache results for 1 day
|
325 |
+
def fetch_remotive_jobs_api(job_title, location=None, category=None, remote=True, max_results=50):
|
326 |
"""
|
327 |
+
Fetches job listings from Remotive API based on user preferences.
|
328 |
+
|
329 |
Args:
|
330 |
+
job_title (str): The job title to search for.
|
331 |
+
location (str, optional): The job location. Defaults to None.
|
332 |
+
category (str, optional): The job category. Defaults to None.
|
333 |
+
remote (bool, optional): Whether to fetch remote jobs. Defaults to True.
|
334 |
+
max_results (int, optional): Maximum number of jobs to fetch. Defaults to 50.
|
335 |
+
|
336 |
Returns:
|
337 |
+
list: A list of job dictionaries.
|
338 |
"""
|
339 |
+
base_url = "https://remotive.com/api/remote-jobs"
|
340 |
+
params = {
|
341 |
+
"search": job_title,
|
342 |
+
"limit": max_results
|
343 |
+
}
|
344 |
+
if category:
|
345 |
+
params["category"] = category
|
346 |
+
try:
|
347 |
+
response = requests.get(base_url, params=params)
|
348 |
+
response.raise_for_status()
|
349 |
+
jobs = response.json().get("jobs", [])
|
350 |
+
if remote:
|
351 |
+
# Filter for remote jobs if not already
|
352 |
+
jobs = [job for job in jobs if job.get("candidate_required_location") == "Worldwide" or job.get("remote") == True]
|
353 |
+
return jobs
|
354 |
+
except requests.exceptions.RequestException as e:
|
355 |
+
st.error(f"Error fetching jobs from Remotive: {e}")
|
356 |
+
return []
|
357 |
|
358 |
+
# The Muse API Integration
|
359 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
360 |
+
def fetch_muse_jobs_api(job_title, location=None, category=None, max_results=50):
|
361 |
+
"""
|
362 |
+
Fetches job listings from The Muse API based on user preferences.
|
363 |
+
|
364 |
+
Args:
|
365 |
+
job_title (str): The job title to search for.
|
366 |
+
location (str, optional): The job location. Defaults to None.
|
367 |
+
category (str, optional): The job category. Defaults to None.
|
368 |
+
max_results (int, optional): Maximum number of jobs to fetch. Defaults to 50.
|
369 |
+
|
370 |
+
Returns:
|
371 |
+
list: A list of job dictionaries.
|
372 |
+
"""
|
373 |
+
base_url = "https://www.themuse.com/api/public/jobs"
|
374 |
+
headers = {
|
375 |
+
"Content-Type": "application/json"
|
376 |
+
}
|
377 |
params = {
|
378 |
+
"page": 1,
|
379 |
+
"per_page": max_results,
|
380 |
+
"category": category,
|
381 |
+
"location": location,
|
382 |
+
"company": None # Can be extended based on needs
|
|
|
|
|
383 |
}
|
384 |
+
try:
|
385 |
+
response = requests.get(base_url, params=params, headers=headers)
|
386 |
+
response.raise_for_status()
|
387 |
+
jobs = response.json().get("results", [])
|
388 |
+
# Filter based on job title
|
389 |
+
filtered_jobs = [job for job in jobs if job_title.lower() in job.get("name", "").lower()]
|
390 |
+
return filtered_jobs
|
391 |
+
except requests.exceptions.RequestException as e:
|
392 |
+
st.error(f"Error fetching jobs from The Muse: {e}")
|
393 |
+
return []
|
394 |
|
395 |
+
# Adzuna API Integration
|
396 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
397 |
+
def fetch_adzuna_jobs_api(job_title, location="india", category=None, max_results=50):
|
398 |
+
"""
|
399 |
+
Fetches job listings from Adzuna API based on user preferences.
|
400 |
+
|
401 |
+
Args:
|
402 |
+
job_title (str): The job title to search for.
|
403 |
+
location (str, optional): The job location. Defaults to "india".
|
404 |
+
category (str, optional): The job category. Defaults to None.
|
405 |
+
max_results (int, optional): Maximum number of jobs to fetch. Defaults to 50.
|
406 |
+
|
407 |
+
Returns:
|
408 |
+
list: A list of job dictionaries.
|
409 |
+
"""
|
410 |
+
base_url = f"https://api.adzuna.com/v1/api/jobs/{location}/search/1"
|
411 |
+
params = {
|
412 |
+
"app_id": ADZUNA_APP_ID,
|
413 |
+
"app_key": ADZUNA_APP_KEY,
|
414 |
+
"what": job_title,
|
415 |
+
"results_per_page": max_results,
|
416 |
+
"content-type": "application/json"
|
417 |
+
}
|
418 |
+
if category:
|
419 |
+
params["category"] = category
|
420 |
try:
|
421 |
+
response = requests.get(base_url, params=params)
|
422 |
response.raise_for_status()
|
423 |
+
jobs = response.json().get("results", [])
|
424 |
+
return jobs
|
|
|
425 |
except requests.exceptions.RequestException as e:
|
426 |
+
st.error(f"Error fetching jobs from Adzuna: {e}")
|
427 |
return []
|
428 |
|
429 |
+
def recommend_jobs(user_skills, user_preferences):
|
430 |
+
"""
|
431 |
+
Recommends jobs based on user skills and preferences from Remotive API.
|
432 |
+
|
433 |
+
Args:
|
434 |
+
user_skills (list): List of user's skills.
|
435 |
+
user_preferences (dict): User preferences like job title, location, category.
|
436 |
+
|
437 |
+
Returns:
|
438 |
+
list: Recommended job listings.
|
439 |
"""
|
440 |
+
job_title = user_preferences.get("job_title", "")
|
441 |
+
location = user_preferences.get("location")
|
442 |
+
category = user_preferences.get("category")
|
443 |
+
|
444 |
+
jobs = fetch_remotive_jobs_api(job_title, location, category)
|
445 |
+
|
446 |
+
# Simple matching based on skills appearing in job description
|
447 |
+
recommended_jobs = []
|
448 |
+
for job in jobs:
|
449 |
+
job_description = job.get("description", "").lower()
|
450 |
+
match_score = sum(skill.lower() in job_description for skill in user_skills)
|
451 |
+
if match_score > 0:
|
452 |
+
recommended_jobs.append((match_score, job))
|
453 |
+
|
454 |
+
# Sort jobs based on match_score
|
455 |
+
recommended_jobs.sort(reverse=True, key=lambda x: x[0])
|
456 |
+
|
457 |
+
# Return only the job dictionaries
|
458 |
+
return [job for score, job in recommended_jobs[:10]] # Top 10 recommendations
|
459 |
+
|
460 |
+
def recommend_muse_jobs(user_skills, user_preferences):
|
461 |
+
"""
|
462 |
+
Recommends jobs from The Muse API based on user skills and preferences.
|
463 |
+
|
464 |
+
Args:
|
465 |
+
user_skills (list): List of user's skills.
|
466 |
+
user_preferences (dict): User preferences like job title, location, category.
|
467 |
+
|
468 |
+
Returns:
|
469 |
+
list: Recommended job listings.
|
470 |
+
"""
|
471 |
+
job_title = user_preferences.get("job_title", "")
|
472 |
+
location = user_preferences.get("location")
|
473 |
+
category = user_preferences.get("category")
|
474 |
+
|
475 |
+
jobs = fetch_muse_jobs_api(job_title, location, category)
|
476 |
+
|
477 |
+
# Simple matching based on skills appearing in job description
|
478 |
+
recommended_jobs = []
|
479 |
+
for job in jobs:
|
480 |
+
job_description = job.get("contents", "").lower()
|
481 |
+
match_score = sum(skill.lower() in job_description for skill in user_skills)
|
482 |
+
if match_score > 0:
|
483 |
+
recommended_jobs.append((match_score, job))
|
484 |
+
|
485 |
+
# Sort jobs based on match_score
|
486 |
+
recommended_jobs.sort(reverse=True, key=lambda x: x[0])
|
487 |
+
|
488 |
+
# Return only the job dictionaries
|
489 |
+
return [job for score, job in recommended_jobs[:10]] # Top 10 recommendations
|
490 |
+
|
491 |
+
def recommend_adzuna_jobs(user_skills, user_preferences):
|
492 |
+
"""
|
493 |
+
Recommends jobs from Adzuna API based on user skills and preferences.
|
494 |
+
|
495 |
+
Args:
|
496 |
+
user_skills (list): List of user's skills.
|
497 |
+
user_preferences (dict): User preferences like job title, location, category.
|
498 |
+
|
499 |
+
Returns:
|
500 |
+
list: Recommended job listings.
|
501 |
+
"""
|
502 |
+
job_title = user_preferences.get("job_title", "")
|
503 |
+
location = user_preferences.get("location", "india")
|
504 |
+
category = user_preferences.get("category")
|
505 |
+
|
506 |
+
jobs = fetch_adzuna_jobs_api(job_title, location, category)
|
507 |
+
|
508 |
+
# Simple matching based on skills appearing in job description
|
509 |
+
recommended_jobs = []
|
510 |
+
for job in jobs:
|
511 |
+
job_description = job.get("description", "").lower()
|
512 |
+
match_score = sum(skill.lower() in job_description for skill in user_skills)
|
513 |
+
if match_score > 0:
|
514 |
+
recommended_jobs.append((match_score, job))
|
515 |
+
|
516 |
+
# Sort jobs based on match_score
|
517 |
+
recommended_jobs.sort(reverse=True, key=lambda x: x[0])
|
518 |
+
|
519 |
+
# Return only the job dictionaries
|
520 |
+
return [job for score, job in recommended_jobs[:10]] # Top 10 recommendations
|
521 |
+
|
522 |
+
# -------------------------------
|
523 |
+
# BLS API Integration and Display
|
524 |
+
# -------------------------------
|
525 |
|
526 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
527 |
+
def fetch_bls_data(series_ids, start_year=2020, end_year=datetime.now().year):
|
528 |
+
"""
|
529 |
+
Fetches labor market data from the BLS API.
|
530 |
+
|
531 |
Args:
|
532 |
+
series_ids (list): List of BLS series IDs.
|
533 |
+
start_year (int, optional): Start year for data. Defaults to 2020.
|
534 |
+
end_year (int, optional): End year for data. Defaults to current year.
|
535 |
+
|
536 |
+
Returns:
|
537 |
+
dict: BLS data response.
|
538 |
+
"""
|
539 |
+
bls_url = "https://api.bls.gov/publicAPI/v2/timeseries/data/"
|
540 |
+
headers = {
|
541 |
+
"Content-Type": "application/json"
|
542 |
+
}
|
543 |
+
payload = {
|
544 |
+
"seriesid": series_ids,
|
545 |
+
"startyear": str(start_year),
|
546 |
+
"endyear": str(end_year)
|
547 |
+
}
|
548 |
+
try:
|
549 |
+
response = requests.post(bls_url, json=payload, headers=headers)
|
550 |
+
response.raise_for_status()
|
551 |
+
data = response.json()
|
552 |
+
if data.get("status") == "REQUEST_SUCCEEDED":
|
553 |
+
return data.get("Results", {})
|
554 |
+
else:
|
555 |
+
st.error("BLS API request failed.")
|
556 |
+
return {}
|
557 |
+
except requests.exceptions.RequestException as e:
|
558 |
+
st.error(f"Error fetching data from BLS: {e}")
|
559 |
+
return {}
|
560 |
+
|
561 |
+
def display_bls_data(series_id, title):
|
562 |
+
"""
|
563 |
+
Processes and displays BLS data with visualizations.
|
564 |
+
|
565 |
+
Args:
|
566 |
+
series_id (str): BLS series ID.
|
567 |
+
title (str): Title for the visualization.
|
568 |
"""
|
569 |
+
data = fetch_bls_data([series_id])
|
570 |
+
if not data:
|
571 |
+
st.info("No data available.")
|
572 |
+
return
|
573 |
+
|
574 |
+
series_data = data.get("series", [])[0]
|
575 |
+
series_title = series_data.get("title", title)
|
576 |
+
observations = series_data.get("data", [])
|
577 |
+
|
578 |
+
# Extract year and value
|
579 |
+
years = [int(obs["year"]) for obs in observations]
|
580 |
+
values = [float(obs["value"].replace(',', '')) for obs in observations]
|
581 |
+
|
582 |
+
df = pd.DataFrame({
|
583 |
+
"Year": years,
|
584 |
+
"Value": values
|
585 |
+
}).sort_values("Year")
|
586 |
+
|
587 |
+
st.markdown(f"### {series_title}")
|
588 |
+
fig = px.line(df, x="Year", y="Value", title=series_title, markers=True)
|
589 |
+
st.plotly_chart(fig, use_container_width=True)
|
590 |
|
591 |
# -------------------------------
|
592 |
+
# Application Tracking Database Functions
|
593 |
# -------------------------------
|
594 |
|
595 |
def init_db():
|
596 |
"""
|
597 |
+
Initializes the SQLite database and creates the applications table if it doesn't exist.
|
598 |
"""
|
599 |
conn = sqlite3.connect('applications.db')
|
600 |
c = conn.cursor()
|
|
|
617 |
|
618 |
def add_application(job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills):
|
619 |
"""
|
620 |
+
Adds a new job application to the database.
|
621 |
"""
|
622 |
conn = sqlite3.connect('applications.db')
|
623 |
c = conn.cursor()
|
|
|
673 |
conn.commit()
|
674 |
conn.close()
|
675 |
|
676 |
+
# -------------------------------
|
677 |
+
# Learning Path Generation Function
|
678 |
+
# -------------------------------
|
679 |
+
|
680 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
681 |
def generate_learning_path(career_goal, current_skills):
|
682 |
"""
|
683 |
Generates a personalized learning path using Groq based on career goal and current skills.
|
|
|
702 |
st.error(f"Error generating learning path: {e}")
|
703 |
return ""
|
704 |
|
705 |
+
# -------------------------------
|
706 |
+
# Visual Resume Analytics Functions
|
707 |
+
# -------------------------------
|
708 |
+
|
709 |
+
def create_skill_distribution_chart(skills):
|
710 |
+
"""
|
711 |
+
Creates a bar chart showing the distribution of skills.
|
712 |
+
"""
|
713 |
+
skill_counts = {}
|
714 |
+
for skill in skills:
|
715 |
+
skill_counts[skill] = skill_counts.get(skill, 0) + 1
|
716 |
+
df = pd.DataFrame(list(skill_counts.items()), columns=['Skill', 'Count'])
|
717 |
+
fig = px.bar(df, x='Skill', y='Count', title='Skill Distribution')
|
718 |
+
return fig
|
719 |
+
|
720 |
+
def create_experience_timeline(resume_text):
|
721 |
+
"""
|
722 |
+
Creates an experience timeline from the resume text.
|
723 |
+
"""
|
724 |
+
# Extract work experience details using Groq
|
725 |
+
prompt = f"""
|
726 |
+
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).
|
727 |
+
|
728 |
+
Resume Text:
|
729 |
+
{resume_text}
|
730 |
+
|
731 |
+
Table:
|
732 |
+
"""
|
733 |
+
|
734 |
+
try:
|
735 |
+
response = llm.invoke(prompt)
|
736 |
+
table_text = response.content.strip()
|
737 |
+
# Parse the table_text to create a DataFrame
|
738 |
+
data = []
|
739 |
+
for line in table_text.split('\n'):
|
740 |
+
if line.strip() and not line.lower().startswith("job title"):
|
741 |
+
parts = line.split('|')
|
742 |
+
if len(parts) == 3:
|
743 |
+
job_title = parts[0].strip()
|
744 |
+
company = parts[1].strip()
|
745 |
+
duration = parts[2].strip()
|
746 |
+
# Convert duration to a float representing years
|
747 |
+
duration_years = parse_duration(duration)
|
748 |
+
data.append({"Job Title": job_title, "Company": company, "Duration (years)": duration_years})
|
749 |
+
df = pd.DataFrame(data)
|
750 |
+
if not df.empty:
|
751 |
+
# Create a cumulative duration for timeline
|
752 |
+
df['Start Year'] = df['Duration (years)'].cumsum() - df['Duration (years)']
|
753 |
+
df['End Year'] = df['Duration (years)'].cumsum()
|
754 |
+
fig = px.timeline(df, x_start="Start Year", x_end="End Year", y="Job Title", color="Company", title="Experience Timeline")
|
755 |
+
fig.update_yaxes(categoryorder="total ascending")
|
756 |
+
return fig
|
757 |
+
else:
|
758 |
+
return None
|
759 |
+
except Exception as e:
|
760 |
+
st.error(f"Error creating experience timeline: {e}")
|
761 |
+
return None
|
762 |
+
|
763 |
+
def parse_duration(duration_str):
|
764 |
+
"""
|
765 |
+
Parses duration strings like '2 years' or '6 months' into float years.
|
766 |
+
"""
|
767 |
+
try:
|
768 |
+
if 'year' in duration_str.lower():
|
769 |
+
years = float(re.findall(r'\d+\.?\d*', duration_str)[0])
|
770 |
+
return years
|
771 |
+
elif 'month' in duration_str.lower():
|
772 |
+
months = float(re.findall(r'\d+\.?\d*', duration_str)[0])
|
773 |
+
return months / 12
|
774 |
+
else:
|
775 |
+
return 0
|
776 |
+
except:
|
777 |
+
return 0
|
778 |
+
|
779 |
+
# -------------------------------
|
780 |
+
# Job Recommendations and BLS Integration
|
781 |
+
# -------------------------------
|
782 |
+
|
783 |
+
# Remotive API Integration
|
784 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
785 |
+
def fetch_remotive_jobs_api(job_title, location=None, category=None, remote=True, max_results=50):
|
786 |
+
"""
|
787 |
+
Fetches job listings from Remotive API based on user preferences.
|
788 |
+
|
789 |
+
Args:
|
790 |
+
job_title (str): The job title to search for.
|
791 |
+
location (str, optional): The job location. Defaults to None.
|
792 |
+
category (str, optional): The job category. Defaults to None.
|
793 |
+
remote (bool, optional): Whether to fetch remote jobs. Defaults to True.
|
794 |
+
max_results (int, optional): Maximum number of jobs to fetch. Defaults to 50.
|
795 |
+
|
796 |
+
Returns:
|
797 |
+
list: A list of job dictionaries.
|
798 |
+
"""
|
799 |
+
base_url = "https://remotive.com/api/remote-jobs"
|
800 |
+
params = {
|
801 |
+
"search": job_title,
|
802 |
+
"limit": max_results
|
803 |
+
}
|
804 |
+
if category:
|
805 |
+
params["category"] = category
|
806 |
+
try:
|
807 |
+
response = requests.get(base_url, params=params)
|
808 |
+
response.raise_for_status()
|
809 |
+
jobs = response.json().get("jobs", [])
|
810 |
+
if remote:
|
811 |
+
# Filter for remote jobs if not already
|
812 |
+
jobs = [job for job in jobs if job.get("candidate_required_location") == "Worldwide" or job.get("remote") == True]
|
813 |
+
return jobs
|
814 |
+
except requests.exceptions.RequestException as e:
|
815 |
+
st.error(f"Error fetching jobs from Remotive: {e}")
|
816 |
+
return []
|
817 |
+
|
818 |
+
# The Muse API Integration
|
819 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
820 |
+
def fetch_muse_jobs_api(job_title, location=None, category=None, max_results=50):
|
821 |
+
"""
|
822 |
+
Fetches job listings from The Muse API based on user preferences.
|
823 |
+
|
824 |
+
Args:
|
825 |
+
job_title (str): The job title to search for.
|
826 |
+
location (str, optional): The job location. Defaults to None.
|
827 |
+
category (str, optional): The job category. Defaults to None.
|
828 |
+
max_results (int, optional): Maximum number of jobs to fetch. Defaults to 50.
|
829 |
+
|
830 |
+
Returns:
|
831 |
+
list: A list of job dictionaries.
|
832 |
+
"""
|
833 |
+
base_url = "https://www.themuse.com/api/public/jobs"
|
834 |
+
headers = {
|
835 |
+
"Content-Type": "application/json"
|
836 |
+
}
|
837 |
+
params = {
|
838 |
+
"page": 1,
|
839 |
+
"per_page": max_results,
|
840 |
+
"category": category,
|
841 |
+
"location": location,
|
842 |
+
"company": None # Can be extended based on needs
|
843 |
+
}
|
844 |
+
try:
|
845 |
+
response = requests.get(base_url, params=params, headers=headers)
|
846 |
+
response.raise_for_status()
|
847 |
+
jobs = response.json().get("results", [])
|
848 |
+
# Filter based on job title
|
849 |
+
filtered_jobs = [job for job in jobs if job_title.lower() in job.get("name", "").lower()]
|
850 |
+
return filtered_jobs
|
851 |
+
except requests.exceptions.RequestException as e:
|
852 |
+
st.error(f"Error fetching jobs from The Muse: {e}")
|
853 |
+
return []
|
854 |
+
|
855 |
+
# Adzuna API Integration
|
856 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
857 |
+
def fetch_adzuna_jobs_api(job_title, location="india", category=None, max_results=50):
|
858 |
+
"""
|
859 |
+
Fetches job listings from Adzuna API based on user preferences.
|
860 |
+
|
861 |
+
Args:
|
862 |
+
job_title (str): The job title to search for.
|
863 |
+
location (str, optional): The job location. Defaults to "india".
|
864 |
+
category (str, optional): The job category. Defaults to None.
|
865 |
+
max_results (int, optional): Maximum number of jobs to fetch. Defaults to 50.
|
866 |
+
|
867 |
+
Returns:
|
868 |
+
list: A list of job dictionaries.
|
869 |
+
"""
|
870 |
+
base_url = f"https://api.adzuna.com/v1/api/jobs/{location}/search/1"
|
871 |
+
params = {
|
872 |
+
"app_id": ADZUNA_APP_ID,
|
873 |
+
"app_key": ADZUNA_APP_KEY,
|
874 |
+
"what": job_title,
|
875 |
+
"results_per_page": max_results,
|
876 |
+
"content-type": "application/json"
|
877 |
+
}
|
878 |
+
if category:
|
879 |
+
params["category"] = category
|
880 |
+
try:
|
881 |
+
response = requests.get(base_url, params=params)
|
882 |
+
response.raise_for_status()
|
883 |
+
jobs = response.json().get("results", [])
|
884 |
+
return jobs
|
885 |
+
except requests.exceptions.RequestException as e:
|
886 |
+
st.error(f"Error fetching jobs from Adzuna: {e}")
|
887 |
+
return []
|
888 |
+
|
889 |
+
def recommend_jobs(user_skills, user_preferences):
|
890 |
+
"""
|
891 |
+
Recommends jobs based on user skills and preferences from Remotive API.
|
892 |
+
|
893 |
+
Args:
|
894 |
+
user_skills (list): List of user's skills.
|
895 |
+
user_preferences (dict): User preferences like job title, location, category.
|
896 |
+
|
897 |
+
Returns:
|
898 |
+
list: Recommended job listings.
|
899 |
+
"""
|
900 |
+
job_title = user_preferences.get("job_title", "")
|
901 |
+
location = user_preferences.get("location")
|
902 |
+
category = user_preferences.get("category")
|
903 |
+
|
904 |
+
jobs = fetch_remotive_jobs_api(job_title, location, category)
|
905 |
+
|
906 |
+
# Simple matching based on skills appearing in job description
|
907 |
+
recommended_jobs = []
|
908 |
+
for job in jobs:
|
909 |
+
job_description = job.get("description", "").lower()
|
910 |
+
match_score = sum(skill.lower() in job_description for skill in user_skills)
|
911 |
+
if match_score > 0:
|
912 |
+
recommended_jobs.append((match_score, job))
|
913 |
+
|
914 |
+
# Sort jobs based on match_score
|
915 |
+
recommended_jobs.sort(reverse=True, key=lambda x: x[0])
|
916 |
+
|
917 |
+
# Return only the job dictionaries
|
918 |
+
return [job for score, job in recommended_jobs[:10]] # Top 10 recommendations
|
919 |
+
|
920 |
+
def recommend_muse_jobs(user_skills, user_preferences):
|
921 |
+
"""
|
922 |
+
Recommends jobs from The Muse API based on user skills and preferences.
|
923 |
+
|
924 |
+
Args:
|
925 |
+
user_skills (list): List of user's skills.
|
926 |
+
user_preferences (dict): User preferences like job title, location, category.
|
927 |
+
|
928 |
+
Returns:
|
929 |
+
list: Recommended job listings.
|
930 |
+
"""
|
931 |
+
job_title = user_preferences.get("job_title", "")
|
932 |
+
location = user_preferences.get("location")
|
933 |
+
category = user_preferences.get("category")
|
934 |
+
|
935 |
+
jobs = fetch_muse_jobs_api(job_title, location, category)
|
936 |
+
|
937 |
+
# Simple matching based on skills appearing in job description
|
938 |
+
recommended_jobs = []
|
939 |
+
for job in jobs:
|
940 |
+
job_description = job.get("contents", "").lower()
|
941 |
+
match_score = sum(skill.lower() in job_description for skill in user_skills)
|
942 |
+
if match_score > 0:
|
943 |
+
recommended_jobs.append((match_score, job))
|
944 |
+
|
945 |
+
# Sort jobs based on match_score
|
946 |
+
recommended_jobs.sort(reverse=True, key=lambda x: x[0])
|
947 |
+
|
948 |
+
# Return only the job dictionaries
|
949 |
+
return [job for score, job in recommended_jobs[:10]] # Top 10 recommendations
|
950 |
+
|
951 |
+
def recommend_adzuna_jobs(user_skills, user_preferences):
|
952 |
+
"""
|
953 |
+
Recommends jobs from Adzuna API based on user skills and preferences.
|
954 |
+
|
955 |
+
Args:
|
956 |
+
user_skills (list): List of user's skills.
|
957 |
+
user_preferences (dict): User preferences like job title, location, category.
|
958 |
+
|
959 |
+
Returns:
|
960 |
+
list: Recommended job listings.
|
961 |
+
"""
|
962 |
+
job_title = user_preferences.get("job_title", "")
|
963 |
+
location = user_preferences.get("location", "india")
|
964 |
+
category = user_preferences.get("category")
|
965 |
+
|
966 |
+
jobs = fetch_adzuna_jobs_api(job_title, location, category)
|
967 |
+
|
968 |
+
# Simple matching based on skills appearing in job description
|
969 |
+
recommended_jobs = []
|
970 |
+
for job in jobs:
|
971 |
+
job_description = job.get("description", "").lower()
|
972 |
+
match_score = sum(skill.lower() in job_description for skill in user_skills)
|
973 |
+
if match_score > 0:
|
974 |
+
recommended_jobs.append((match_score, job))
|
975 |
+
|
976 |
+
# Sort jobs based on match_score
|
977 |
+
recommended_jobs.sort(reverse=True, key=lambda x: x[0])
|
978 |
+
|
979 |
+
# Return only the job dictionaries
|
980 |
+
return [job for score, job in recommended_jobs[:10]] # Top 10 recommendations
|
981 |
+
|
982 |
+
# -------------------------------
|
983 |
+
# Labor Market Insights Module
|
984 |
+
# -------------------------------
|
985 |
+
|
986 |
+
def labor_market_insights_module():
|
987 |
+
st.header("📈 Labor Market Insights")
|
988 |
+
|
989 |
+
st.write("""
|
990 |
+
Gain valuable insights into the current labor market trends, employment rates, and industry growth to make informed career decisions.
|
991 |
+
""")
|
992 |
+
|
993 |
+
# Define BLS Series IDs based on desired data
|
994 |
+
# Example: Unemployment rate (Series ID: LNS14000000)
|
995 |
+
# Reference: https://www.bls.gov/web/laus/laumstrk.htm
|
996 |
+
unemployment_series_id = "LNS14000000" # Unemployment Rate
|
997 |
+
employment_series_id = "CEU0000000001" # Total Employment
|
998 |
+
|
999 |
+
# Display Unemployment Rate
|
1000 |
+
display_bls_data(unemployment_series_id, "Unemployment Rate (%)")
|
1001 |
+
|
1002 |
+
# Display Total Employment
|
1003 |
+
display_bls_data(employment_series_id, "Total Employment")
|
1004 |
+
|
1005 |
+
# Additional Insights
|
1006 |
+
st.subheader("💡 Additional Insights")
|
1007 |
+
st.write("""
|
1008 |
+
- **Industry Growth:** Understanding which industries are growing can help you target your job search effectively.
|
1009 |
+
- **Salary Trends:** Keeping an eye on salary trends ensures that you negotiate effectively and align your expectations.
|
1010 |
+
- **Geographical Demand:** Some regions may have higher demand for certain roles, guiding your location preferences.
|
1011 |
+
""")
|
1012 |
+
|
1013 |
+
# Fetch and display more BLS data as needed
|
1014 |
+
|
1015 |
# -------------------------------
|
1016 |
# Page Functions
|
1017 |
# -------------------------------
|
1018 |
|
1019 |
def email_generator_page():
|
1020 |
+
st.header("📧 Automated Email Generator")
|
1021 |
|
1022 |
st.write("""
|
1023 |
Generate personalized cold emails based on job postings and your resume.
|
|
|
1026 |
# Create two columns for input fields
|
1027 |
col1, col2 = st.columns(2)
|
1028 |
with col1:
|
1029 |
+
job_link = st.text_input("🔗 Enter the job link:")
|
1030 |
with col2:
|
1031 |
+
uploaded_file = st.file_uploader("📄 Upload your resume (PDF format):", type="pdf")
|
1032 |
|
1033 |
if st.button("Generate Email"):
|
1034 |
if not job_link:
|
|
|
1060 |
# Generate email
|
1061 |
email_text = generate_email(job_description, requirements, resume_text)
|
1062 |
if email_text:
|
1063 |
+
st.subheader("📨 Generated Email:")
|
1064 |
st.write(email_text)
|
1065 |
# Provide download option
|
1066 |
st.download_button(
|
|
|
1073 |
st.error("Failed to generate email.")
|
1074 |
|
1075 |
def cover_letter_generator_page():
|
1076 |
+
st.header("📝 Automated Cover Letter Generator")
|
1077 |
|
1078 |
st.write("""
|
1079 |
Generate personalized cover letters based on job postings and your resume.
|
|
|
1082 |
# Create two columns for input fields
|
1083 |
col1, col2 = st.columns(2)
|
1084 |
with col1:
|
1085 |
+
job_link = st.text_input("🔗 Enter the job link:")
|
1086 |
with col2:
|
1087 |
+
uploaded_file = st.file_uploader("📄 Upload your resume (PDF format):", type="pdf")
|
1088 |
|
1089 |
if st.button("Generate Cover Letter"):
|
1090 |
if not job_link:
|
|
|
1116 |
# Generate cover letter
|
1117 |
cover_letter = generate_cover_letter(job_description, requirements, resume_text)
|
1118 |
if cover_letter:
|
1119 |
+
st.subheader("📝 Generated Cover Letter:")
|
1120 |
st.write(cover_letter)
|
1121 |
# Provide download option
|
1122 |
st.download_button(
|
|
|
1152 |
skills = extract_skills(resume_text)
|
1153 |
if skills:
|
1154 |
st.markdown("**Identified Skills:**")
|
1155 |
+
# Display skills as bullet points in columns
|
1156 |
cols = st.columns(4)
|
1157 |
for idx, skill in enumerate(skills, 1):
|
1158 |
cols[idx % 4].write(f"- {skill}")
|
|
|
1163 |
keywords = suggest_keywords(resume_text)
|
1164 |
if keywords:
|
1165 |
st.markdown("**Suggested Keywords for ATS Optimization:**")
|
1166 |
+
# Display keywords as bullet points in columns
|
1167 |
cols = st.columns(4)
|
1168 |
for idx, keyword in enumerate(keywords, 1):
|
1169 |
cols[idx % 4].write(f"- {keyword}")
|
|
|
1225 |
else:
|
1226 |
st.error("❌ Failed to extract text from resume.")
|
1227 |
|
|
|
1228 |
def application_tracking_dashboard():
|
1229 |
+
st.header("📋 Application Tracking Dashboard")
|
1230 |
|
1231 |
# Initialize database
|
1232 |
init_db()
|
1233 |
|
1234 |
# Form to add a new application
|
1235 |
+
st.subheader("➕ Add New Application")
|
1236 |
with st.form("add_application"):
|
1237 |
+
job_title = st.text_input("🖇️ Job Title")
|
1238 |
+
company = st.text_input("🏢 Company")
|
1239 |
+
application_date = st.date_input("📅 Application Date", datetime.today())
|
1240 |
+
status = st.selectbox("📈 Status", ["Applied", "Interviewing", "Offered", "Rejected"])
|
1241 |
+
deadline = st.date_input("⏰ Application Deadline", datetime.today() + timedelta(days=30))
|
1242 |
+
notes = st.text_area("📝 Notes")
|
1243 |
+
uploaded_file = st.file_uploader("📂 Upload Job Description (PDF)", type="pdf")
|
1244 |
+
uploaded_resume = st.file_uploader("📄 Upload Resume (PDF)", type="pdf")
|
1245 |
+
submitted = st.form_submit_button("➕ Add Application")
|
1246 |
if submitted:
|
1247 |
if uploaded_file:
|
1248 |
job_description = extract_text_from_pdf(uploaded_file)
|
|
|
1265 |
resume_text=resume_text,
|
1266 |
skills=skills
|
1267 |
)
|
1268 |
+
st.success("✅ Application added successfully!")
|
1269 |
|
1270 |
# Display applications
|
1271 |
+
st.subheader("📊 Your Applications")
|
1272 |
applications = fetch_applications()
|
1273 |
if applications:
|
1274 |
df = pd.DataFrame(applications)
|
|
|
1278 |
# Export Button
|
1279 |
csv = df.to_csv(index=False).encode('utf-8')
|
1280 |
st.download_button(
|
1281 |
+
label="💾 Download Applications as CSV",
|
1282 |
data=csv,
|
1283 |
file_name='applications.csv',
|
1284 |
mime='text/csv',
|
1285 |
)
|
1286 |
|
1287 |
# Import Button
|
1288 |
+
st.subheader("📥 Import Applications")
|
1289 |
+
uploaded_csv = st.file_uploader("📁 Upload a CSV file", type="csv")
|
1290 |
if uploaded_csv:
|
1291 |
try:
|
1292 |
imported_df = pd.read_csv(uploaded_csv)
|
1293 |
# Validate required columns
|
1294 |
required_columns = {"Job Title", "Company", "Application Date", "Status", "Deadline", "Notes"}
|
1295 |
if not required_columns.issubset(imported_df.columns):
|
1296 |
+
st.error("❌ Uploaded CSV is missing required columns.")
|
1297 |
else:
|
1298 |
for index, row in imported_df.iterrows():
|
1299 |
job_title = row.get("Job Title", "N/A")
|
|
|
1316 |
resume_text=resume_text,
|
1317 |
skills=skills
|
1318 |
)
|
1319 |
+
st.success("✅ Applications imported successfully!")
|
1320 |
except Exception as e:
|
1321 |
+
st.error(f"❌ Error importing applications: {e}")
|
1322 |
|
1323 |
# Actions: Update Status or Delete
|
1324 |
for app in applications:
|
1325 |
with st.expander(f"{app['Job Title']} at {app['Company']}"):
|
1326 |
+
st.write(f"**📅 Application Date:** {app['Application Date']}")
|
1327 |
+
st.write(f"**⏰ Deadline:** {app['Deadline']}")
|
1328 |
+
st.write(f"**📈 Status:** {app['Status']}")
|
1329 |
+
st.write(f"**📝 Notes:** {app['Notes']}")
|
1330 |
if app['Job Description']:
|
1331 |
+
st.write("**📄 Job Description:**")
|
1332 |
st.write(app['Job Description'][:500] + "...")
|
1333 |
if app['Skills']:
|
1334 |
+
st.write("**💼 Skills:**", ', '.join(app['Skills']))
|
1335 |
# Update status
|
1336 |
+
new_status = st.selectbox("🔄 Update Status:", ["Applied", "Interviewing", "Offered", "Rejected"], key=f"status_{app['ID']}")
|
1337 |
+
if st.button("🔁 Update Status", key=f"update_{app['ID']}"):
|
1338 |
update_application_status(app['ID'], new_status)
|
1339 |
+
st.success("✅ Status updated successfully!")
|
1340 |
# Delete application
|
1341 |
+
if st.button("🗑️ Delete Application", key=f"delete_{app['ID']}"):
|
1342 |
delete_application(app['ID'])
|
1343 |
+
st.success("✅ Application deleted successfully!")
|
1344 |
else:
|
1345 |
+
st.write("ℹ️ No applications found.")
|
1346 |
+
|
1347 |
+
def job_recommendations_module():
|
1348 |
+
st.header("🔍 Job Matching & Recommendations")
|
1349 |
+
|
1350 |
+
st.write("""
|
1351 |
+
Discover job opportunities tailored to your skills and preferences. Get personalized recommendations from multiple job platforms.
|
1352 |
+
""")
|
1353 |
+
|
1354 |
+
# User Preferences Form
|
1355 |
+
st.subheader("🎯 Set Your Preferences")
|
1356 |
+
with st.form("preferences_form"):
|
1357 |
+
job_title = st.text_input("🔍 Desired Job Title", placeholder="e.g., Data Scientist, Backend Developer")
|
1358 |
+
location = st.text_input("📍 Preferred Location", placeholder="e.g., New York, NY, USA or Remote")
|
1359 |
+
category = st.selectbox("📂 Job Category", ["", "Engineering", "Marketing", "Design", "Sales", "Finance", "Healthcare", "Education", "Other"])
|
1360 |
+
user_skills_input = st.text_input("💡 Your Skills (comma-separated)", placeholder="e.g., Python, Machine Learning, SQL")
|
1361 |
+
submitted = st.form_submit_button("🚀 Get Recommendations")
|
1362 |
+
|
1363 |
+
if submitted:
|
1364 |
+
if not job_title or not user_skills_input:
|
1365 |
+
st.error("❌ Please enter both job title and your skills.")
|
1366 |
+
return
|
1367 |
+
|
1368 |
+
user_skills = [skill.strip() for skill in user_skills_input.split(",") if skill.strip()]
|
1369 |
+
user_preferences = {
|
1370 |
+
"job_title": job_title,
|
1371 |
+
"location": location,
|
1372 |
+
"category": category
|
1373 |
+
}
|
1374 |
+
|
1375 |
+
with st.spinner("🔄 Fetching job recommendations..."):
|
1376 |
+
# Fetch recommendations from Remotive
|
1377 |
+
remotive_jobs = recommend_jobs(user_skills, user_preferences)
|
1378 |
+
# Fetch recommendations from The Muse
|
1379 |
+
muse_jobs = recommend_muse_jobs(user_skills, user_preferences)
|
1380 |
+
# Fetch recommendations from Adzuna
|
1381 |
+
adzuna_jobs = recommend_adzuna_jobs(user_skills, user_preferences)
|
1382 |
+
|
1383 |
+
# Combine all job listings
|
1384 |
+
combined_jobs = remotive_jobs + muse_jobs + adzuna_jobs
|
1385 |
+
# Remove duplicates based on job URL
|
1386 |
+
unique_jobs = {}
|
1387 |
+
for job in combined_jobs:
|
1388 |
+
url = job.get("url") or job.get("redirect_url") or job.get("url_standard")
|
1389 |
+
if url and url not in unique_jobs:
|
1390 |
+
unique_jobs[url] = job
|
1391 |
+
|
1392 |
+
if unique_jobs:
|
1393 |
+
st.subheader("💼 Recommended Jobs:")
|
1394 |
+
for idx, job in enumerate(unique_jobs.values(), 1):
|
1395 |
+
job_title_display = job.get("title") or job.get("name")
|
1396 |
+
company_display = job.get("company", {}).get("name") or job.get("company_name")
|
1397 |
+
location_display = job.get("candidate_required_location") or job.get("location")
|
1398 |
+
salary_display = f"${job.get('salary_min', 'N/A')} - ${job.get('salary_max', 'N/A')}" if job.get('salary_min') or job.get('salary_max') else "N/A"
|
1399 |
+
job_url = job.get("url") or job.get("redirect_url") or job.get("url_standard")
|
1400 |
+
|
1401 |
+
st.markdown(f"### {idx}. {job_title_display}")
|
1402 |
+
st.markdown(f"**🏢 Company:** {company_display}")
|
1403 |
+
st.markdown(f"**📍 Location:** {location_display}")
|
1404 |
+
st.markdown(f"**💰 Salary:** {salary_display}")
|
1405 |
+
st.markdown(f"**🔗 Job URL:** [Apply Here]({job_url})")
|
1406 |
+
st.write("---")
|
1407 |
+
else:
|
1408 |
+
st.info("ℹ️ No job recommendations found based on your criteria.")
|
1409 |
|
1410 |
def interview_preparation_module():
|
1411 |
+
st.header("🎤 Interview Preparation")
|
1412 |
|
1413 |
st.write("""
|
1414 |
Prepare for your interviews with tailored mock questions and expert answers.
|
|
|
1417 |
# Create two columns for input fields
|
1418 |
col1, col2 = st.columns(2)
|
1419 |
with col1:
|
1420 |
+
job_title = st.text_input("🔍 Enter the job title you're applying for:")
|
1421 |
with col2:
|
1422 |
+
company = st.text_input("🏢 Enter the company name:")
|
1423 |
|
1424 |
+
if st.button("🎯 Generate Mock Interview Questions"):
|
1425 |
if not job_title or not company:
|
1426 |
+
st.error("❌ Please enter both job title and company name.")
|
1427 |
return
|
1428 |
+
with st.spinner("⏳ Generating questions..."):
|
1429 |
# Prompt to generate 50 interview questions with answers
|
1430 |
prompt = f"""
|
1431 |
Generate a list of 50 interview questions along with their answers for the position of {job_title} at {company}. Each question should be followed by a concise and professional answer.
|
|
|
1436 |
qa_text = llm.invoke(prompt).content.strip()
|
1437 |
# Split into question-answer pairs
|
1438 |
qa_pairs = qa_text.split('\n\n')
|
1439 |
+
st.subheader("🗣️ Mock Interview Questions and Answers:")
|
1440 |
for idx, qa in enumerate(qa_pairs, 1):
|
1441 |
if qa.strip():
|
1442 |
parts = qa.split('\n', 1)
|
|
|
1447 |
st.markdown(f"**A:** {answer}")
|
1448 |
st.write("---")
|
1449 |
except Exception as e:
|
1450 |
+
st.error(f"❌ Error generating interview questions: {e}")
|
1451 |
|
1452 |
def personalized_learning_paths_module():
|
1453 |
+
st.header("📚 Personalized Learning Paths")
|
1454 |
|
1455 |
st.write("""
|
1456 |
Receive tailored learning plans to help you acquire the skills needed for your desired career, complemented with curated video resources.
|
|
|
1459 |
# Create two columns for input fields
|
1460 |
col1, col2 = st.columns(2)
|
1461 |
with col1:
|
1462 |
+
career_goal = st.text_input("🎯 Enter your career goal (e.g., Data Scientist, Machine Learning Engineer):")
|
1463 |
with col2:
|
1464 |
+
current_skills = st.text_input("💡 Enter your current skills (comma-separated):")
|
1465 |
|
1466 |
+
if st.button("🚀 Generate Learning Path"):
|
1467 |
if not career_goal or not current_skills:
|
1468 |
+
st.error("❌ Please enter both career goal and current skills.")
|
1469 |
return
|
1470 |
+
with st.spinner("🔄 Generating your personalized learning path..."):
|
1471 |
learning_path = generate_learning_path(career_goal, current_skills)
|
1472 |
if learning_path:
|
1473 |
+
st.subheader("📜 Your Personalized Learning Path:")
|
1474 |
st.write(learning_path)
|
1475 |
|
1476 |
# Assuming the learning path is divided into modules/subparts separated by newlines or numbering
|
|
|
1484 |
modules = re.split(r'\d+\.\s+', learning_path)
|
1485 |
modules = [module.strip() for module in modules if module.strip()]
|
1486 |
|
1487 |
+
st.subheader("📹 Recommended YouTube Videos for Each Module:")
|
1488 |
for module in modules:
|
1489 |
# Search for long videos related to the module
|
1490 |
video_urls = search_youtube_videos(query=module, max_results=2, video_duration="long")
|
|
|
1494 |
else:
|
1495 |
st.write(f"No videos found for **{module}**.")
|
1496 |
else:
|
1497 |
+
st.error("❌ Failed to generate learning path.")
|
1498 |
|
1499 |
def networking_opportunities_module():
|
1500 |
+
st.header("🤝 Networking Opportunities")
|
1501 |
|
1502 |
st.write("""
|
1503 |
Expand your professional network by connecting with relevant industry peers and joining professional groups.
|
|
|
1506 |
# Create two columns for input fields
|
1507 |
col1, col2 = st.columns(2)
|
1508 |
with col1:
|
1509 |
+
user_skills = st.text_input("💡 Enter your key skills (comma-separated):")
|
1510 |
with col2:
|
1511 |
+
industry = st.text_input("🏭 Enter your industry (e.g., Technology, Finance):")
|
1512 |
|
1513 |
+
if st.button("🔍 Find Networking Opportunities"):
|
1514 |
if not user_skills or not industry:
|
1515 |
+
st.error("❌ Please enter both key skills and industry.")
|
1516 |
return
|
1517 |
+
with st.spinner("🔄 Fetching networking opportunities..."):
|
1518 |
# Suggest LinkedIn groups or connections based on skills and industry
|
1519 |
prompt = f"""
|
1520 |
Based on the following skills: {user_skills}, and industry: {industry}, suggest relevant LinkedIn groups, professional organizations, and industry events for networking.
|
1521 |
"""
|
1522 |
try:
|
1523 |
suggestions = llm.invoke(prompt).content.strip()
|
1524 |
+
st.subheader("🔗 Recommended Networking Groups and Events:")
|
1525 |
st.write(suggestions)
|
1526 |
except Exception as e:
|
1527 |
+
st.error(f"❌ Error fetching networking opportunities: {e}")
|
1528 |
|
1529 |
def salary_estimation_module():
|
1530 |
+
st.header("💵 Salary Estimation and Negotiation Tips")
|
1531 |
|
1532 |
st.write("""
|
1533 |
Understand the salary expectations for your desired roles and learn effective negotiation strategies.
|
|
|
1536 |
# Create two columns for input fields
|
1537 |
col1, col2 = st.columns(2)
|
1538 |
with col1:
|
1539 |
+
job_title = st.text_input("🔍 Enter the job title:")
|
1540 |
with col2:
|
1541 |
+
location = st.text_input("📍 Enter the location (e.g., New York, NY, USA):")
|
1542 |
|
1543 |
+
if st.button("💰 Get Salary Estimate"):
|
1544 |
if not job_title or not location:
|
1545 |
+
st.error("❌ Please enter both job title and location.")
|
1546 |
return
|
1547 |
+
with st.spinner("🔄 Fetching salary data..."):
|
1548 |
# Job Salary Data API Integration
|
1549 |
salary_data = get_job_recommendations(job_title, location)
|
1550 |
if salary_data:
|
|
|
1553 |
max_salary = salary_data.get("max_salary")
|
1554 |
|
1555 |
if min_salary and avg_salary and max_salary:
|
1556 |
+
st.subheader("💲 Salary Estimate:")
|
1557 |
st.write(f"**Minimum Salary:** ${min_salary:,}")
|
1558 |
st.write(f"**Average Salary:** ${avg_salary:,}")
|
1559 |
st.write(f"**Maximum Salary:** ${max_salary:,}")
|
|
|
1568 |
title=f"Salary Estimates for {job_title} in {location}",
|
1569 |
labels={"Amount": "Salary (USD)"},
|
1570 |
text_auto=True)
|
1571 |
+
st.plotly_chart(fig, use_container_width=True)
|
1572 |
else:
|
1573 |
+
st.error("❌ Salary data not available for the provided job title and location.")
|
1574 |
|
1575 |
# Generate negotiation tips using Groq
|
1576 |
tips_prompt = f"""
|
|
|
1578 |
"""
|
1579 |
try:
|
1580 |
tips = llm.invoke(tips_prompt).content.strip()
|
1581 |
+
st.subheader("📝 Negotiation Tips:")
|
1582 |
st.write(tips)
|
1583 |
except Exception as e:
|
1584 |
+
st.error(f"❌ Error generating negotiation tips: {e}")
|
1585 |
else:
|
1586 |
+
st.error("❌ Failed to retrieve salary data.")
|
1587 |
|
1588 |
def feedback_and_improvement_module():
|
1589 |
+
st.header("🗣️ Feedback and Continuous Improvement")
|
1590 |
|
1591 |
st.write("""
|
1592 |
We value your feedback! Let us know how we can improve your experience.
|
1593 |
""")
|
1594 |
|
1595 |
with st.form("feedback_form"):
|
1596 |
+
name = st.text_input("👤 Your Name")
|
1597 |
+
email = st.text_input("📧 Your Email")
|
1598 |
+
feedback_type = st.selectbox("📂 Type of Feedback", ["Bug Report", "Feature Request", "General Feedback"])
|
1599 |
+
feedback = st.text_area("📝 Your Feedback")
|
1600 |
+
submitted = st.form_submit_button("✅ Submit")
|
1601 |
+
|
1602 |
if submitted:
|
1603 |
if not name or not email or not feedback:
|
1604 |
+
st.error("❌ Please fill in all the fields.")
|
1605 |
else:
|
1606 |
# Here you can implement logic to store feedback, e.g., in a database or send via email
|
1607 |
# For demonstration, we'll print to the console
|
1608 |
print(f"Feedback from {name} ({email}): {feedback_type} - {feedback}")
|
1609 |
+
st.success("✅ Thank you for your feedback!")
|
1610 |
|
1611 |
def gamification_module():
|
1612 |
+
st.header("🏆 Gamification and Achievements")
|
1613 |
|
1614 |
st.write("""
|
1615 |
Stay motivated by earning badges and tracking your progress!
|
|
|
1642 |
st.write(f"{progress * 100:.0f}% complete")
|
1643 |
|
1644 |
def resource_library_page():
|
1645 |
+
st.header("📚 Resource Library")
|
1646 |
|
1647 |
st.write("""
|
1648 |
Access a collection of templates and guides to enhance your job search.
|
|
|
1672 |
try:
|
1673 |
with open(resource['file'], "rb") as file:
|
1674 |
btn = st.download_button(
|
1675 |
+
label="⬇️ Download",
|
1676 |
data=file,
|
1677 |
file_name=os.path.basename(resource['file']),
|
1678 |
mime="application/octet-stream"
|
1679 |
)
|
1680 |
except FileNotFoundError:
|
1681 |
+
st.error(f"❌ File {resource['file']} not found. Please ensure the file is in the correct directory.")
|
1682 |
st.write("---")
|
1683 |
|
1684 |
def success_stories_page():
|
1685 |
+
st.header("🌟 Success Stories")
|
1686 |
|
1687 |
st.write("""
|
1688 |
Hear from our users who have successfully landed their dream jobs with our assistance!
|
|
|
1717 |
st.write(f"\"{user['testimonial']}\"")
|
1718 |
st.write("---")
|
1719 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1720 |
def chatbot_support_page():
|
1721 |
+
st.header("🤖 AI-Powered Chatbot Support")
|
1722 |
|
1723 |
st.write("""
|
1724 |
Have questions or need assistance? Chat with our AI-powered assistant!
|
|
|
1729 |
st.session_state['chat_history'] = []
|
1730 |
|
1731 |
# User input
|
1732 |
+
user_input = st.text_input("🗨️ You:", key="user_input")
|
1733 |
|
1734 |
if st.button("Send"):
|
1735 |
if user_input:
|
|
|
1742 |
"""
|
1743 |
try:
|
1744 |
# Invoke the LLM to get a response
|
1745 |
+
response = llm.invoke(prompt)
|
1746 |
+
assistant_message = response.content.strip()
|
1747 |
# Append assistant response to chat history
|
1748 |
+
st.session_state['chat_history'].append({"message": assistant_message, "is_user": False})
|
1749 |
except Exception as e:
|
1750 |
+
error_message = "❌ Sorry, I encountered an error while processing your request."
|
1751 |
st.session_state['chat_history'].append({"message": error_message, "is_user": False})
|
1752 |
+
st.error(f"❌ Error in chatbot: {e}")
|
1753 |
|
1754 |
# Display chat history using streamlit-chat
|
1755 |
for chat in st.session_state['chat_history']:
|
|
|
1758 |
else:
|
1759 |
message(chat['message'], is_user=False, avatar_style="bottts")
|
1760 |
|
1761 |
+
def help_page():
|
1762 |
+
st.header("❓ Help & FAQ")
|
1763 |
+
|
1764 |
+
with st.expander("🛠️ How do I generate a cover letter?"):
|
1765 |
+
st.write("""
|
1766 |
+
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**.
|
1767 |
+
""")
|
1768 |
+
|
1769 |
+
with st.expander("📋 How do I track my applications?"):
|
1770 |
+
st.write("""
|
1771 |
+
Use the **Application Tracking Dashboard** to add new applications, update their status, and monitor deadlines.
|
1772 |
+
""")
|
1773 |
+
|
1774 |
+
with st.expander("📄 How can I optimize my resume?"):
|
1775 |
+
st.write("""
|
1776 |
+
Upload your resume in the **Resume Analysis** section to extract skills and receive optimization suggestions.
|
1777 |
+
""")
|
1778 |
+
|
1779 |
+
with st.expander("📥 How do I import my applications?"):
|
1780 |
+
st.write("""
|
1781 |
+
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.
|
1782 |
+
""")
|
1783 |
+
|
1784 |
+
with st.expander("🗣️ How do I provide feedback?"):
|
1785 |
+
st.write("""
|
1786 |
+
Navigate to the **Feedback and Continuous Improvement** section, fill out the form, and submit your feedback.
|
1787 |
+
""")
|
1788 |
+
|
1789 |
+
# -------------------------------
|
1790 |
+
# YouTube Video Search and Embed Functions
|
1791 |
+
# -------------------------------
|
1792 |
+
|
1793 |
+
@st.cache_data(ttl=86400) # Cache results for 1 day
|
1794 |
+
def search_youtube_videos(query, max_results=2, video_duration="long"):
|
1795 |
+
"""
|
1796 |
+
Searches YouTube for videos matching the query and returns video URLs.
|
1797 |
+
|
1798 |
+
Args:
|
1799 |
+
query (str): Search query.
|
1800 |
+
max_results (int, optional): Number of videos to return. Defaults to 2.
|
1801 |
+
video_duration (str, optional): Duration filter ('any', 'short', 'medium', 'long'). Defaults to "long".
|
1802 |
+
|
1803 |
+
Returns:
|
1804 |
+
list: List of YouTube video URLs.
|
1805 |
+
"""
|
1806 |
+
search_url = "https://www.googleapis.com/youtube/v3/search"
|
1807 |
+
params = {
|
1808 |
+
"part": "snippet",
|
1809 |
+
"q": query,
|
1810 |
+
"type": "video",
|
1811 |
+
"maxResults": max_results,
|
1812 |
+
"videoDuration": video_duration,
|
1813 |
+
"key": YOUTUBE_API_KEY
|
1814 |
+
}
|
1815 |
+
try:
|
1816 |
+
response = requests.get(search_url, params=params)
|
1817 |
+
response.raise_for_status()
|
1818 |
+
results = response.json().get("items", [])
|
1819 |
+
video_urls = [f"https://www.youtube.com/watch?v={item['id']['videoId']}" for item in results]
|
1820 |
+
return video_urls
|
1821 |
+
except requests.exceptions.RequestException as e:
|
1822 |
+
st.error(f"❌ Error fetching YouTube videos: {e}")
|
1823 |
+
return []
|
1824 |
+
|
1825 |
+
def embed_youtube_videos(video_urls, module_name):
|
1826 |
+
"""
|
1827 |
+
Embeds YouTube videos in the Streamlit app.
|
1828 |
+
|
1829 |
+
Args:
|
1830 |
+
video_urls (list): List of YouTube video URLs.
|
1831 |
+
module_name (str): Name of the module for context.
|
1832 |
+
"""
|
1833 |
+
for url in video_urls:
|
1834 |
+
st.video(url)
|
1835 |
+
|
1836 |
+
# -------------------------------
|
1837 |
+
# Job Recommendations and Labor Market Insights
|
1838 |
+
# -------------------------------
|
1839 |
+
|
1840 |
+
def job_recommendations_module():
|
1841 |
+
st.header("🔍 Job Matching & Recommendations")
|
1842 |
+
|
1843 |
+
st.write("""
|
1844 |
+
Discover job opportunities tailored to your skills and preferences. Get personalized recommendations from multiple job platforms.
|
1845 |
+
""")
|
1846 |
+
|
1847 |
+
# User Preferences Form
|
1848 |
+
st.subheader("🎯 Set Your Preferences")
|
1849 |
+
with st.form("preferences_form"):
|
1850 |
+
job_title = st.text_input("🔍 Desired Job Title", placeholder="e.g., Data Scientist, Backend Developer")
|
1851 |
+
location = st.text_input("📍 Preferred Location", placeholder="e.g., New York, NY, USA or Remote")
|
1852 |
+
category = st.selectbox("📂 Job Category", ["", "Engineering", "Marketing", "Design", "Sales", "Finance", "Healthcare", "Education", "Other"])
|
1853 |
+
user_skills_input = st.text_input("💡 Your Skills (comma-separated)", placeholder="e.g., Python, Machine Learning, SQL")
|
1854 |
+
submitted = st.form_submit_button("🚀 Get Recommendations")
|
1855 |
+
|
1856 |
+
if submitted:
|
1857 |
+
if not job_title or not user_skills_input:
|
1858 |
+
st.error("❌ Please enter both job title and your skills.")
|
1859 |
+
return
|
1860 |
+
|
1861 |
+
user_skills = [skill.strip() for skill in user_skills_input.split(",") if skill.strip()]
|
1862 |
+
user_preferences = {
|
1863 |
+
"job_title": job_title,
|
1864 |
+
"location": location,
|
1865 |
+
"category": category
|
1866 |
+
}
|
1867 |
+
|
1868 |
+
with st.spinner("🔄 Fetching job recommendations..."):
|
1869 |
+
# Fetch recommendations from Remotive
|
1870 |
+
remotive_jobs = recommend_jobs(user_skills, user_preferences)
|
1871 |
+
# Fetch recommendations from The Muse
|
1872 |
+
muse_jobs = recommend_muse_jobs(user_skills, user_preferences)
|
1873 |
+
# Fetch recommendations from Adzuna
|
1874 |
+
adzuna_jobs = recommend_adzuna_jobs(user_skills, user_preferences)
|
1875 |
+
|
1876 |
+
# Combine all job listings
|
1877 |
+
combined_jobs = remotive_jobs + muse_jobs + adzuna_jobs
|
1878 |
+
# Remove duplicates based on job URL
|
1879 |
+
unique_jobs = {}
|
1880 |
+
for job in combined_jobs:
|
1881 |
+
url = job.get("url") or job.get("redirect_url") or job.get("url_standard")
|
1882 |
+
if url and url not in unique_jobs:
|
1883 |
+
unique_jobs[url] = job
|
1884 |
+
|
1885 |
+
if unique_jobs:
|
1886 |
+
st.subheader("💼 Recommended Jobs:")
|
1887 |
+
for idx, job in enumerate(unique_jobs.values(), 1):
|
1888 |
+
job_title_display = job.get("title") or job.get("name")
|
1889 |
+
company_display = job.get("company", {}).get("name") or job.get("company_name")
|
1890 |
+
location_display = job.get("candidate_required_location") or job.get("location")
|
1891 |
+
salary_display = f"${job.get('salary_min', 'N/A')} - ${job.get('salary_max', 'N/A')}" if job.get('salary_min') or job.get('salary_max') else "N/A"
|
1892 |
+
job_url = job.get("url") or job.get("redirect_url") or job.get("url_standard")
|
1893 |
+
|
1894 |
+
st.markdown(f"### {idx}. {job_title_display}")
|
1895 |
+
st.markdown(f"**🏢 Company:** {company_display}")
|
1896 |
+
st.markdown(f"**📍 Location:** {location_display}")
|
1897 |
+
st.markdown(f"**💰 Salary:** {salary_display}")
|
1898 |
+
st.markdown(f"**🔗 Job URL:** [Apply Here]({job_url})")
|
1899 |
+
st.write("---")
|
1900 |
+
else:
|
1901 |
+
st.info("ℹ️ No job recommendations found based on your criteria.")
|
1902 |
+
|
1903 |
+
def labor_market_insights_module():
|
1904 |
+
st.header("📈 Labor Market Insights")
|
1905 |
+
|
1906 |
+
st.write("""
|
1907 |
+
Gain valuable insights into the current labor market trends, employment rates, and industry growth to make informed career decisions.
|
1908 |
+
""")
|
1909 |
+
|
1910 |
+
# Define BLS Series IDs based on desired data
|
1911 |
+
# Example: Unemployment rate (Series ID: LNS14000000)
|
1912 |
+
# Reference: https://www.bls.gov/web/laus/laumstrk.htm
|
1913 |
+
unemployment_series_id = "LNS14000000" # Unemployment Rate
|
1914 |
+
employment_series_id = "CEU0000000001" # Total Employment
|
1915 |
+
|
1916 |
+
# Display Unemployment Rate
|
1917 |
+
display_bls_data(unemployment_series_id, "Unemployment Rate (%)")
|
1918 |
+
|
1919 |
+
# Display Total Employment
|
1920 |
+
display_bls_data(employment_series_id, "Total Employment")
|
1921 |
+
|
1922 |
+
# Additional Insights
|
1923 |
+
st.subheader("💡 Additional Insights")
|
1924 |
+
st.write("""
|
1925 |
+
- **Industry Growth:** Understanding which industries are growing can help you target your job search effectively.
|
1926 |
+
- **Salary Trends:** Keeping an eye on salary trends ensures that you negotiate effectively and align your expectations.
|
1927 |
+
- **Geographical Demand:** Some regions may have higher demand for certain roles, guiding your location preferences.
|
1928 |
+
""")
|
1929 |
+
|
1930 |
# -------------------------------
|
1931 |
# Main App Function
|
1932 |
# -------------------------------
|
|
|
1951 |
# Sidebar Navigation using streamlit_option_menu
|
1952 |
with st.sidebar:
|
1953 |
selected = option_menu(
|
1954 |
+
menu_title="📂 Main Menu",
|
1955 |
options=["Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking",
|
1956 |
+
"Job Recommendations", "Labor Market Insights", "Interview Preparation", "Personalized Learning Paths",
|
1957 |
+
"Networking Opportunities", "Salary Estimation", "Feedback", "Gamification", "Resource Library",
|
1958 |
+
"Success Stories", "Chatbot Support", "Help"],
|
1959 |
+
icons=["envelope", "file-earmark-text", "file-person", "briefcase",
|
1960 |
+
"search", "bar-chart-line", "microphone", "book",
|
1961 |
+
"people", "currency-dollar", "chat-left-text", "trophy", "collection",
|
1962 |
+
"star", "robot", "question-circle"],
|
1963 |
menu_icon="cast",
|
1964 |
default_index=0,
|
1965 |
styles={
|
|
|
1979 |
resume_analysis_page()
|
1980 |
elif selected == "Application Tracking":
|
1981 |
application_tracking_dashboard()
|
1982 |
+
elif selected == "Job Recommendations":
|
1983 |
+
job_recommendations_module()
|
1984 |
+
elif selected == "Labor Market Insights":
|
1985 |
+
labor_market_insights_module()
|
1986 |
elif selected == "Interview Preparation":
|
1987 |
interview_preparation_module()
|
1988 |
elif selected == "Personalized Learning Paths":
|