AdithyaSNair commited on
Commit
384abd1
·
verified ·
1 Parent(s): 2d0a829

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +213 -42
app.py CHANGED
@@ -1,5 +1,3 @@
1
- # app.py
2
-
3
  import streamlit as st
4
  import requests
5
  from langchain_groq import ChatGroq
@@ -14,9 +12,6 @@ from streamlit_option_menu import option_menu
14
  import fitz # PyMuPDF
15
  from bs4 import BeautifulSoup
16
 
17
- # -------------------------------
18
- # API Key Retrieval
19
- # -------------------------------
20
 
21
  GROQ_API_KEY = st.secrets["GROQ_API_KEY"]
22
  RAPIDAPI_KEY = st.secrets["RAPIDAPI_KEY"]
@@ -24,9 +19,6 @@ YOUTUBE_API_KEY = st.secrets["YOUTUBE_API_KEY"]
24
  THE_MUSE_API_KEY = st.secrets.get("THE_MUSE_API_KEY", "")
25
  BLS_API_KEY = st.secrets.get("BLS_API_KEY", "")
26
 
27
- # -------------------------------
28
- # Initialize Language Model
29
- # -------------------------------
30
 
31
  llm = ChatGroq(
32
  temperature=0,
@@ -34,9 +26,7 @@ llm = ChatGroq(
34
  model_name="llama-3.1-70b-versatile"
35
  )
36
 
37
- # -------------------------------
38
- # Function Definitions
39
- # -------------------------------
40
 
41
  @st.cache_data(ttl=3600)
42
  def extract_text_from_pdf(pdf_file):
@@ -359,44 +349,51 @@ def fetch_muse_jobs_api(job_title, location=None, category=None, max_results=50)
359
  st.error(f"Error fetching jobs from The Muse: {e}")
360
  return []
361
 
362
- # Indeed API Integration
363
  @st.cache_data(ttl=86400) # Cache results for 1 day
364
- def fetch_indeed_jobs_api(job_title, location="CA", sort="-1", page_size=50):
365
  """
366
- Fetches job listings from Indeed API based on user preferences.
367
 
368
  Args:
369
- job_title (str): The job title to search for (e.g., "Front end developer").
370
- location (str, optional): The country code (e.g., "CA" for Canada). Defaults to "CA".
371
- sort (str, optional): Sorting parameter (e.g., "-1" for relevance). Defaults to "-1".
372
- page_size (int, optional): Number of results per page. Defaults to 50.
 
 
 
 
 
373
 
374
  Returns:
375
- list: A list of job dictionaries.
376
  """
377
- url = "https://indeed46.p.rapidapi.com/job"
378
-
379
- # Encode job title to handle spaces and special characters
380
- encoded_job_title = re.sub(r'\s+', '+', job_title.strip())
381
 
382
  querystring = {
383
- "country": location,
384
- "sort": sort,
385
- "page_size": str(page_size),
386
- "title": encoded_job_title # Assuming the API accepts a 'title' parameter for job titles
 
 
 
 
 
387
  }
388
 
389
  headers = {
390
- "x-rapidapi-key": RAPIDAPI_KEY, # Ensure RAPIDAPI_KEY is set in .streamlit/secrets.toml
391
- "x-rapidapi-host": "indeed46.p.rapidapi.com"
392
  }
393
 
394
  try:
395
  response = requests.get(url, headers=headers, params=querystring)
396
- response.raise_for_status() # Raises HTTPError for bad responses (4xx or 5xx)
397
  data = response.json()
398
- jobs = data.get("data", [])
399
- return jobs
400
  except requests.exceptions.HTTPError as http_err:
401
  if response.status_code == 400:
402
  st.error("❌ Bad Request: Please check the parameters you're sending.")
@@ -414,6 +411,52 @@ def fetch_indeed_jobs_api(job_title, location="CA", sort="-1", page_size=50):
414
  st.error(f"❌ An unexpected error occurred: {e}")
415
  return []
416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  def recommend_indeed_jobs(user_skills, user_preferences):
418
  """
419
  Recommends jobs from Indeed API based on user skills and preferences.
@@ -426,19 +469,30 @@ def recommend_indeed_jobs(user_skills, user_preferences):
426
  list: Recommended job listings.
427
  """
428
  job_title = user_preferences.get("job_title", "")
429
- country = user_preferences.get("location_country", "CA")
430
- category = user_preferences.get("category")
 
431
 
432
- jobs = fetch_indeed_jobs_api(job_title, country, sort="-1", page_size=50)
 
433
 
434
- # Simple matching based on skills appearing in job description
435
  recommended_jobs = []
436
- for job in jobs:
437
- job_description = job.get("description", "").lower()
438
- match_score = sum(skill.lower() in job_description for skill in user_skills)
439
- if match_score > 0:
440
- recommended_jobs.append((match_score, job))
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  # Sort jobs based on match_score
443
  recommended_jobs.sort(reverse=True, key=lambda x: x[0])
444
 
@@ -546,6 +600,97 @@ def display_bls_data(series_id, title):
546
  fig = px.line(df, x="Year", y="Value", title=series_title, markers=True)
547
  st.plotly_chart(fig, use_container_width=True)
548
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
549
  # -------------------------------
550
  # Application Tracking Database Functions
551
  # -------------------------------
@@ -711,7 +856,32 @@ def embed_youtube_videos(video_urls, module_name):
711
  # Job Recommendations and BLS Integration
712
  # -------------------------------
713
 
714
- # Removed Adzuna-related functions as per request
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
 
716
  # -------------------------------
717
  # Page Functions
@@ -931,6 +1101,7 @@ def application_tracking_dashboard():
931
 
932
  # Initialize database
933
  init_db()
 
934
 
935
  # Form to add a new application
936
  st.subheader("➕ Add New Application")
 
 
 
1
  import streamlit as st
2
  import requests
3
  from langchain_groq import ChatGroq
 
12
  import fitz # PyMuPDF
13
  from bs4 import BeautifulSoup
14
 
 
 
 
15
 
16
  GROQ_API_KEY = st.secrets["GROQ_API_KEY"]
17
  RAPIDAPI_KEY = st.secrets["RAPIDAPI_KEY"]
 
19
  THE_MUSE_API_KEY = st.secrets.get("THE_MUSE_API_KEY", "")
20
  BLS_API_KEY = st.secrets.get("BLS_API_KEY", "")
21
 
 
 
 
22
 
23
  llm = ChatGroq(
24
  temperature=0,
 
26
  model_name="llama-3.1-70b-versatile"
27
  )
28
 
29
+
 
 
30
 
31
  @st.cache_data(ttl=3600)
32
  def extract_text_from_pdf(pdf_file):
 
349
  st.error(f"Error fetching jobs from The Muse: {e}")
350
  return []
351
 
352
+ # Indeed API Integration using /list and /get
353
  @st.cache_data(ttl=86400) # Cache results for 1 day
354
+ def fetch_indeed_jobs_list_api(job_title, location="United States", distance="1.0", language="en_GB", remoteOnly="false", datePosted="month", employmentTypes="fulltime;parttime;intern;contractor", index=0, page_size=10):
355
  """
356
+ Fetches a list of job IDs from Indeed API based on user preferences.
357
 
358
  Args:
359
+ job_title (str): The job title to search for.
360
+ location (str, optional): The job location. Defaults to "United States".
361
+ distance (str, optional): Search radius in miles. Defaults to "1.0".
362
+ language (str, optional): Language code. Defaults to "en_GB".
363
+ remoteOnly (str, optional): "true" or "false". Defaults to "false".
364
+ datePosted (str, optional): e.g., "month". Defaults to "month".
365
+ employmentTypes (str, optional): e.g., "fulltime;parttime;intern;contractor". Defaults to "fulltime;parttime;intern;contractor".
366
+ index (int, optional): Pagination index. Defaults to 0.
367
+ page_size (int, optional): Number of jobs to fetch. Defaults to 10.
368
 
369
  Returns:
370
+ list: A list of job IDs.
371
  """
372
+ url = "https://jobs-api14.p.rapidapi.com/list"
 
 
 
373
 
374
  querystring = {
375
+ "query": job_title,
376
+ "location": location,
377
+ "distance": distance,
378
+ "language": language,
379
+ "remoteOnly": remoteOnly,
380
+ "datePosted": datePosted,
381
+ "employmentTypes": employmentTypes,
382
+ "index": str(index),
383
+ "page_size": str(page_size)
384
  }
385
 
386
  headers = {
387
+ "x-rapidapi-key": RAPIDAPI_KEY,
388
+ "x-rapidapi-host": "jobs-api14.p.rapidapi.com"
389
  }
390
 
391
  try:
392
  response = requests.get(url, headers=headers, params=querystring)
393
+ response.raise_for_status()
394
  data = response.json()
395
+ job_ids = [job["id"] for job in data.get("jobs", [])]
396
+ return job_ids
397
  except requests.exceptions.HTTPError as http_err:
398
  if response.status_code == 400:
399
  st.error("❌ Bad Request: Please check the parameters you're sending.")
 
411
  st.error(f"❌ An unexpected error occurred: {e}")
412
  return []
413
 
414
+ @st.cache_data(ttl=86400) # Cache results for 1 day
415
+ def fetch_indeed_job_details_api(job_id, language="en_GB"):
416
+ """
417
+ Fetches job details from Indeed API based on job ID.
418
+
419
+ Args:
420
+ job_id (str): The job ID.
421
+ language (str, optional): Language code. Defaults to "en_GB".
422
+
423
+ Returns:
424
+ dict: Job details.
425
+ """
426
+ url = "https://jobs-api14.p.rapidapi.com/get"
427
+
428
+ querystring = {
429
+ "id": job_id,
430
+ "language": language
431
+ }
432
+
433
+ headers = {
434
+ "x-rapidapi-key": RAPIDAPI_KEY,
435
+ "x-rapidapi-host": "jobs-api14.p.rapidapi.com"
436
+ }
437
+
438
+ try:
439
+ response = requests.get(url, headers=headers, params=querystring)
440
+ response.raise_for_status()
441
+ job_details = response.json()
442
+ return job_details
443
+ except requests.exceptions.HTTPError as http_err:
444
+ if response.status_code == 400:
445
+ st.error("❌ Bad Request: Please check the job ID and parameters.")
446
+ elif response.status_code == 403:
447
+ st.error("❌ Access Forbidden: Check your API key and permissions.")
448
+ elif response.status_code == 404:
449
+ st.error("❌ Job Not Found: Verify the job ID.")
450
+ else:
451
+ st.error(f"❌ HTTP error occurred: {http_err}")
452
+ return {}
453
+ except requests.exceptions.RequestException as req_err:
454
+ st.error(f"❌ Request Exception: {req_err}")
455
+ return {}
456
+ except Exception as e:
457
+ st.error(f"❌ An unexpected error occurred: {e}")
458
+ return {}
459
+
460
  def recommend_indeed_jobs(user_skills, user_preferences):
461
  """
462
  Recommends jobs from Indeed API based on user skills and preferences.
 
469
  list: Recommended job listings.
470
  """
471
  job_title = user_preferences.get("job_title", "")
472
+ location = user_preferences.get("location", "United States")
473
+ category = user_preferences.get("category", "")
474
+ language = "en_GB"
475
 
476
+ # Fetch job IDs
477
+ job_ids = fetch_indeed_jobs_list_api(job_title, location=location, category=category, page_size=5) # Limiting to 5 for API call count
478
 
 
479
  recommended_jobs = []
480
+ api_calls_needed = len(job_ids) # Each /get call counts as one
 
 
 
 
481
 
482
+ # Check if enough API calls are left
483
+ if not can_make_api_calls(api_calls_needed):
484
+ st.error("❌ You have reached your monthly API request limit. Please try again next month.")
485
+ return []
486
+
487
+ for job_id in job_ids:
488
+ job_details = fetch_indeed_job_details_api(job_id, language=language)
489
+ if job_details and not job_details.get("hasError", True):
490
+ job_description = job_details.get("description", "").lower()
491
+ match_score = sum(skill.lower() in job_description for skill in user_skills)
492
+ if match_score > 0:
493
+ recommended_jobs.append((match_score, job_details))
494
+ decrement_api_calls(1)
495
+
496
  # Sort jobs based on match_score
497
  recommended_jobs.sort(reverse=True, key=lambda x: x[0])
498
 
 
600
  fig = px.line(df, x="Year", y="Value", title=series_title, markers=True)
601
  st.plotly_chart(fig, use_container_width=True)
602
 
603
+ # -------------------------------
604
+ # API Usage Counter Functions
605
+ # -------------------------------
606
+
607
+ def init_api_usage_db():
608
+ """
609
+ Initializes the SQLite database and creates the api_usage table if it doesn't exist.
610
+ """
611
+ conn = sqlite3.connect('applications.db')
612
+ c = conn.cursor()
613
+ c.execute('''
614
+ CREATE TABLE IF NOT EXISTS api_usage (
615
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
616
+ count INTEGER,
617
+ last_reset DATE
618
+ )
619
+ ''')
620
+ # Check if a row exists, if not, initialize
621
+ c.execute('SELECT COUNT(*) FROM api_usage')
622
+ if c.fetchone()[0] == 0:
623
+ # Initialize with 25 requests and current date
624
+ c.execute('INSERT INTO api_usage (count, last_reset) VALUES (?, ?)', (25, datetime.now().date()))
625
+ conn.commit()
626
+ conn.close()
627
+
628
+ def get_api_usage():
629
+ """
630
+ Retrieves the current API usage count and last reset date.
631
+
632
+ Returns:
633
+ tuple: (count, last_reset_date)
634
+ """
635
+ conn = sqlite3.connect('applications.db')
636
+ c = conn.cursor()
637
+ c.execute('SELECT count, last_reset FROM api_usage WHERE id = 1')
638
+ row = c.fetchone()
639
+ conn.close()
640
+ if row:
641
+ return row[0], datetime.strptime(row[1], "%Y-%m-%d").date()
642
+ else:
643
+ return 25, datetime.now().date()
644
+
645
+ def reset_api_usage():
646
+ """
647
+ Resets the API usage count to 25 and updates the last reset date.
648
+ """
649
+ conn = sqlite3.connect('applications.db')
650
+ c = conn.cursor()
651
+ c.execute('UPDATE api_usage SET count = ?, last_reset = ? WHERE id = 1', (25, datetime.now().date()))
652
+ conn.commit()
653
+ conn.close()
654
+
655
+ def can_make_api_calls(requests_needed):
656
+ """
657
+ Checks if there are enough API calls remaining.
658
+
659
+ Args:
660
+ requests_needed (int): Number of API calls required.
661
+
662
+ Returns:
663
+ bool: True if allowed, False otherwise.
664
+ """
665
+ count, last_reset = get_api_usage()
666
+ today = datetime.now().date()
667
+ if today >= last_reset + timedelta(days=30):
668
+ reset_api_usage()
669
+ count, last_reset = get_api_usage()
670
+ if count >= requests_needed:
671
+ return True
672
+ else:
673
+ return False
674
+
675
+ def decrement_api_calls(requests_used):
676
+ """
677
+ Decrements the API usage count by the number of requests used.
678
+
679
+ Args:
680
+ requests_used (int): Number of API calls used.
681
+ """
682
+ conn = sqlite3.connect('applications.db')
683
+ c = conn.cursor()
684
+ c.execute('SELECT count FROM api_usage WHERE id = 1')
685
+ row = c.fetchone()
686
+ if row:
687
+ new_count = row[0] - requests_used
688
+ if new_count < 0:
689
+ new_count = 0
690
+ c.execute('UPDATE api_usage SET count = ? WHERE id = 1', (new_count,))
691
+ conn.commit()
692
+ conn.close()
693
+
694
  # -------------------------------
695
  # Application Tracking Database Functions
696
  # -------------------------------
 
856
  # Job Recommendations and BLS Integration
857
  # -------------------------------
858
 
859
+ def labor_market_insights_module():
860
+ st.header("📈 Labor Market Insights")
861
+
862
+ st.write("""
863
+ Gain valuable insights into the current labor market trends, employment rates, and industry growth to make informed career decisions.
864
+ """)
865
+
866
+ # Define BLS Series IDs based on desired data
867
+ # Example: Unemployment rate (Series ID: LNS14000000)
868
+ # Reference: https://www.bls.gov/web/laus/laumstrk.htm
869
+ unemployment_series_id = "LNS14000000" # Unemployment Rate
870
+ employment_series_id = "CEU0000000001" # Total Employment
871
+
872
+ # Display Unemployment Rate
873
+ display_bls_data(unemployment_series_id, "Unemployment Rate (%)")
874
+
875
+ # Display Total Employment
876
+ display_bls_data(employment_series_id, "Total Employment")
877
+
878
+ # Additional Insights
879
+ st.subheader("💡 Additional Insights")
880
+ st.write("""
881
+ - **Industry Growth:** Understanding which industries are growing can help you target your job search effectively.
882
+ - **Salary Trends:** Keeping an eye on salary trends ensures that you negotiate effectively and align your expectations.
883
+ - **Geographical Demand:** Some regions may have higher demand for certain roles, guiding your location preferences.
884
+ """)
885
 
886
  # -------------------------------
887
  # Page Functions
 
1101
 
1102
  # Initialize database
1103
  init_db()
1104
+ init_api_usage_db()
1105
 
1106
  # Form to add a new application
1107
  st.subheader("➕ Add New Application")