AdithyaSNair commited on
Commit
8a2ea33
·
verified ·
1 Parent(s): a0aab1c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +915 -170
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
- # YouTube Data API Integration
321
  # -------------------------------
322
 
 
323
  @st.cache_data(ttl=86400) # Cache results for 1 day
324
- def search_youtube_videos(query, max_results=3, video_duration="long", order="relevance"):
325
  """
326
- Searches YouTube for videos related to the query with additional filters.
327
-
328
  Args:
329
- query (str): The search query/topic.
330
- max_results (int): The maximum number of videos to retrieve.
331
- video_duration (str): Duration filter ('any', 'short', 'medium', 'long').
332
- order (str): Order of results ('date', 'rating', 'relevance', 'title', 'videoCount', 'viewCount').
333
-
 
334
  Returns:
335
- list: A list of YouTube video URLs.
336
  """
337
- api_key = YOUTUBE_API_KEY
338
- search_url = "https://www.googleapis.com/youtube/v3/search"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  params = {
341
- "part": "snippet",
342
- "q": query,
343
- "type": "video",
344
- "maxResults": max_results,
345
- "videoDuration": video_duration,
346
- "order": order,
347
- "key": api_key
348
  }
 
 
 
 
 
 
 
 
 
 
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  try:
351
- response = requests.get(search_url, params=params)
352
  response.raise_for_status()
353
- results = response.json().get("items", [])
354
- video_urls = [f"https://www.youtube.com/watch?v={item['id']['videoId']}" for item in results]
355
- return video_urls
356
  except requests.exceptions.RequestException as e:
357
- st.error(f"Error fetching YouTube videos: {e}")
358
  return []
359
 
360
- def embed_youtube_videos(video_urls, topic):
 
 
 
 
 
 
 
 
 
361
  """
362
- Embeds YouTube videos under a specific topic within an expander.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
 
 
 
 
 
364
  Args:
365
- video_urls (list): List of YouTube video URLs.
366
- topic (str): The interview topic.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  """
368
- with st.expander(f"Videos for {topic}"):
369
- for url in video_urls:
370
- st.video(url)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
 
372
  # -------------------------------
373
- # Database Functions
374
  # -------------------------------
375
 
376
  def init_db():
377
  """
378
- Initializes the SQLite database for application tracking.
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 tags or bullet points
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 tags
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"**Application Date:** {app['Application Date']}")
794
- st.write(f"**Deadline:** {app['Deadline']}")
795
- st.write(f"**Status:** {app['Status']}")
796
- st.write(f"**Notes:** {app['Notes']}")
797
  if app['Job Description']:
798
- st.write("**Job Description:**")
799
  st.write(app['Job Description'][:500] + "...")
800
  if app['Skills']:
801
- st.write("**Skills:**", ', '.join(app['Skills']))
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).content.strip()
 
1178
  # Append assistant response to chat history
1179
- st.session_state['chat_history'].append({"message": response, "is_user": False})
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", "Networking Opportunities",
1219
- "Salary Estimation", "Feedback", "Gamification", "Resource Library", "Success Stories", "Chatbot Support", "Help"],
1220
- icons=["envelope", "file-earmark-text", "file-person", "briefcase", "gear",
1221
- "book", "people", "currency-dollar", "chat-left-text", "trophy", "collection", "star", "chat", "question-circle"],
 
 
 
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":