Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -17,26 +17,30 @@ import urllib.request
|
|
| 17 |
import os
|
| 18 |
import base64
|
| 19 |
|
| 20 |
-
|
| 21 |
# Streamlit page configuration
|
| 22 |
st.set_page_config(
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
# Load API keys
|
| 29 |
try:
|
| 30 |
-
serp_api_key = os.getenv("serp_api_key")
|
| 31 |
-
rapidapi_key = os.getenv("rapidapi_key")
|
| 32 |
-
google_api_key = os.getenv("google_api_key")
|
| 33 |
-
gnews_api_key = os.getenv("gnews_api_key")
|
| 34 |
-
newsapi_key = os.getenv("newsapi_ke")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
except KeyError as e:
|
| 36 |
st.error(f"Missing API key: {e}. Please configure it in secrets.toml.")
|
| 37 |
st.stop()
|
| 38 |
|
| 39 |
-
# Custom CSS
|
| 40 |
st.markdown("""
|
| 41 |
<style>
|
| 42 |
.main {
|
|
@@ -76,7 +80,7 @@ st.markdown("""
|
|
| 76 |
width: 150px;
|
| 77 |
}
|
| 78 |
.card {
|
| 79 |
-
background-color:
|
| 80 |
padding: 15px;
|
| 81 |
border-radius: 10px;
|
| 82 |
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
@@ -110,7 +114,6 @@ Start with our [CV Maker](#) or [Job Finder](#)!
|
|
| 110 |
|
| 111 |
st.sidebar.caption("v2.1.0 | Last updated: April 2024")
|
| 112 |
|
| 113 |
-
|
| 114 |
# Home page
|
| 115 |
if page == "Home":
|
| 116 |
st.markdown("""
|
|
@@ -189,7 +192,7 @@ if page == "Home":
|
|
| 189 |
|
| 190 |
logo_path = "logo.ico"
|
| 191 |
if os.path.exists(logo_path):
|
| 192 |
-
col1, col2, col3 = st.columns([1,2,1])
|
| 193 |
with col2:
|
| 194 |
with open(logo_path, "rb") as f:
|
| 195 |
logo_bytes = f.read()
|
|
@@ -202,7 +205,7 @@ if page == "Home":
|
|
| 202 |
|
| 203 |
st.markdown("""
|
| 204 |
## Welcome to Your Career Transformation
|
| 205 |
-
|
| 206 |
CareerBoost is your **AI-powered career companion** designed to help you navigate every step of your professional
|
| 207 |
journey with confidence. Whether you're looking for your dream job, optimizing your resume, or preparing for
|
| 208 |
interviews, we've got you covered with smart, personalized tools.
|
|
@@ -230,7 +233,7 @@ if page == "Home":
|
|
| 230 |
""", unsafe_allow_html=True)
|
| 231 |
|
| 232 |
st.markdown("## ✨ How CareerBoost Helps You Succeed")
|
| 233 |
-
|
| 234 |
features = [
|
| 235 |
{
|
| 236 |
"title": "Smart Job Matching",
|
|
@@ -253,7 +256,7 @@ if page == "Home":
|
|
| 253 |
"icon": "🧭"
|
| 254 |
}
|
| 255 |
]
|
| 256 |
-
|
| 257 |
for feature in features:
|
| 258 |
st.markdown(f"""
|
| 259 |
<div class="feature-card">
|
|
@@ -272,10 +275,12 @@ elif page == "Job Finding":
|
|
| 272 |
st.title("🔍 Job Finding")
|
| 273 |
st.markdown("Search for job opportunities tailored to your preferences.")
|
| 274 |
|
|
|
|
| 275 |
def build_google_search_link(title, company):
|
| 276 |
query = f"{title} {company} apply job"
|
| 277 |
return f"https://www.google.com/search?q={quote_plus(query)}"
|
| 278 |
|
|
|
|
| 279 |
def search_serp(query, location, experience='fresher', job_category='full-time', num_results=10):
|
| 280 |
params = {
|
| 281 |
"engine": "google_jobs",
|
|
@@ -297,7 +302,9 @@ elif page == "Job Finding":
|
|
| 297 |
"location": job.get('location', 'N/A'),
|
| 298 |
"posted": job.get('detected_extensions', {}).get('posted_at', 'N/A'),
|
| 299 |
"description": job.get('description', '')[:200] + "...",
|
| 300 |
-
"apply_link": job.get("job_google_link") or build_google_search_link(job.get('title', ''),
|
|
|
|
|
|
|
| 301 |
"source": "SerpAPI",
|
| 302 |
"category": job_category
|
| 303 |
}
|
|
@@ -306,6 +313,7 @@ elif page == "Job Finding":
|
|
| 306 |
except requests.exceptions.RequestException as e:
|
| 307 |
return [{"error": f"SerpAPI error: {str(e)}"}]
|
| 308 |
|
|
|
|
| 309 |
def duckduckgo_job_search(query: str, job_category: str) -> list:
|
| 310 |
try:
|
| 311 |
with DDGS() as ddgs:
|
|
@@ -326,10 +334,12 @@ elif page == "Job Finding":
|
|
| 326 |
except Exception as e:
|
| 327 |
return [{"error": f"DuckDuckGo error: {str(e)}"}]
|
| 328 |
|
|
|
|
| 329 |
def job_search(field, location, experience, job_category='full-time'):
|
| 330 |
query = f"find {field} job in {location} for {experience}"
|
| 331 |
return duckduckgo_job_search(query, job_category)
|
| 332 |
|
|
|
|
| 333 |
def rapid_job_searcher(job: str, location: str, pages: int = 1, country: str = "us") -> list:
|
| 334 |
conn = http.client.HTTPSConnection("jsearch.p.rapidapi.com")
|
| 335 |
headers = {
|
|
@@ -365,6 +375,7 @@ elif page == "Job Finding":
|
|
| 365 |
except Exception as e:
|
| 366 |
return [{"error": f"RapidAPI request failed: {str(e)}"}]
|
| 367 |
|
|
|
|
| 368 |
job_categories = ['full-time', 'part-time', 'intern', 'contract', 'temporary']
|
| 369 |
experience_levels = ['fresher', 'experienced', 'senior']
|
| 370 |
|
|
@@ -383,10 +394,10 @@ elif page == "Job Finding":
|
|
| 383 |
rapid_results = rapid_job_searcher(job, location)
|
| 384 |
ddg_results = job_search(job, location, experience, category)
|
| 385 |
serp_results = search_serp(job, location, experience, category)
|
| 386 |
-
all_results = {'
|
| 387 |
|
| 388 |
for source, results in all_results.items():
|
| 389 |
-
st.subheader(f"Jobs
|
| 390 |
if results and not any("error" in r for r in results):
|
| 391 |
for result in results:
|
| 392 |
with st.container():
|
|
@@ -403,7 +414,7 @@ elif page == "Job Finding":
|
|
| 403 |
else:
|
| 404 |
error_msg = results[0].get("error", "No results found.") if results else "No results found."
|
| 405 |
st.error(error_msg)
|
| 406 |
-
|
| 407 |
# CV Maker Section
|
| 408 |
elif page == "CV Maker":
|
| 409 |
st.title("📝 CV Maker")
|
|
@@ -592,9 +603,9 @@ elif page == "Interview Preparation":
|
|
| 592 |
interview_prompt = PromptTemplate(
|
| 593 |
input_variables=["job_field", "search_results"],
|
| 594 |
template="""
|
| 595 |
-
You are an interview preparation expert. Generate exactly
|
| 596 |
Requirements:
|
| 597 |
-
- Include
|
| 598 |
- Incorporate trends and frequently asked questions from 2022-2025.
|
| 599 |
- Use the search results for context to inform answers, but do not include raw search data or URLs in the output: {search_results}.
|
| 600 |
- Format as plain text with question numbers, type (Technical/Behavioral/Situational), questions, and answers.
|
|
@@ -622,7 +633,7 @@ elif page == "Interview Preparation":
|
|
| 622 |
st.markdown("### Interview Questions")
|
| 623 |
st.text_area("Questions and Answers", questions, height=400)
|
| 624 |
st.download_button(
|
| 625 |
-
label="Download Questions",
|
| 626 |
data=questions,
|
| 627 |
file_name="Interview_Questions.txt",
|
| 628 |
mime="text/plain"
|
|
@@ -637,6 +648,7 @@ elif page == "Career Insights":
|
|
| 637 |
|
| 638 |
newsapi = NewsApiClient(api_key=newsapi_key)
|
| 639 |
|
|
|
|
| 640 |
def get_gnews_articles():
|
| 641 |
url = f"https://gnews.io/api/v4/search?q=job%20market%20OR%20employment%20OR%20hiring%20OR%20recruitment%20OR%20careers%20OR%20job%20opportunities%20India%20OR%20tech%20OR%20IT%20OR%20technology&lang=en&country=in&max=10&apikey={gnews_api_key}"
|
| 642 |
try:
|
|
@@ -658,6 +670,7 @@ elif page == "Career Insights":
|
|
| 658 |
st.error(f"Failed to fetch GNews: {e.reason}")
|
| 659 |
return []
|
| 660 |
|
|
|
|
| 661 |
def get_indian_job_news():
|
| 662 |
try:
|
| 663 |
yesterday = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d') # Extended to 7 days
|
|
@@ -684,6 +697,7 @@ elif page == "Career Insights":
|
|
| 684 |
st.error(f"Unexpected error fetching job news: {str(e)}")
|
| 685 |
return []
|
| 686 |
|
|
|
|
| 687 |
def get_indian_tech_news():
|
| 688 |
try:
|
| 689 |
domains = 'economictimes.indiatimes.com,livemint.com,indiatoday.in'
|
|
@@ -708,6 +722,7 @@ elif page == "Career Insights":
|
|
| 708 |
st.error(f"Unexpected error fetching tech news: {str(e)}")
|
| 709 |
return []
|
| 710 |
|
|
|
|
| 711 |
def get_company_hiring_news():
|
| 712 |
companies = ['TCS', 'Infosys', 'Wipro', 'HCL', 'Tech Mahindra']
|
| 713 |
company_news = []
|
|
@@ -735,6 +750,7 @@ elif page == "Career Insights":
|
|
| 735 |
st.error(f"Unexpected error fetching company news: {str(e)}")
|
| 736 |
return []
|
| 737 |
|
|
|
|
| 738 |
with st.spinner("Fetching news..."):
|
| 739 |
gnews_articles = get_gnews_articles()
|
| 740 |
job_news = get_indian_job_news()
|
|
@@ -765,7 +781,8 @@ elif page == "Career Insights":
|
|
| 765 |
""", unsafe_allow_html=True)
|
| 766 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 767 |
else:
|
| 768 |
-
st.markdown('<p class="no-news">No job market news available at the moment. Please try again later.</p>',
|
|
|
|
| 769 |
|
| 770 |
# Company Hiring News Section
|
| 771 |
st.markdown('<h2 class="section-header">Company Hiring News</h2>', unsafe_allow_html=True)
|
|
@@ -782,7 +799,8 @@ elif page == "Career Insights":
|
|
| 782 |
""", unsafe_allow_html=True)
|
| 783 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 784 |
else:
|
| 785 |
-
st.markdown('<p class="no-news">No company hiring news available at the moment. Please try again later.</p>',
|
|
|
|
| 786 |
|
| 787 |
# Tech Industry News Section
|
| 788 |
st.markdown('<h2 class="section-header">Tech Industry News</h2>', unsafe_allow_html=True)
|
|
@@ -799,7 +817,8 @@ elif page == "Career Insights":
|
|
| 799 |
""", unsafe_allow_html=True)
|
| 800 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 801 |
else:
|
| 802 |
-
st.markdown('<p class="no-news">No tech industry news available at the moment. Please try again later.</p>',
|
|
|
|
| 803 |
|
| 804 |
|
| 805 |
# About Section
|
|
|
|
| 17 |
import os
|
| 18 |
import base64
|
| 19 |
|
|
|
|
| 20 |
# Streamlit page configuration
|
| 21 |
st.set_page_config(
|
| 22 |
+
page_title="CareerBoost",
|
| 23 |
+
page_icon="💼",
|
| 24 |
+
layout="wide",
|
| 25 |
+
initial_sidebar_state="expanded"
|
| 26 |
+
)
|
| 27 |
# Load API keys
|
| 28 |
try:
|
| 29 |
+
# serp_api_key = os.getenv("serp_api_key")
|
| 30 |
+
# rapidapi_key = os.getenv("rapidapi_key")
|
| 31 |
+
# google_api_key = os.getenv("google_api_key")
|
| 32 |
+
# gnews_api_key = os.getenv("gnews_api_key")
|
| 33 |
+
# newsapi_key = os.getenv("newsapi_ke")
|
| 34 |
+
serp_api_key = "97e9c83e6b430eb8a8cdd71cf70aaf2d22812a74d6192c87a56dfcbad55dfaa3"
|
| 35 |
+
rapidapi_key = "d4b6e17cb6msh1464b70685fc390p17d5ccjsn81a387e95e22"
|
| 36 |
+
google_api_key = "AIzaSyB-IkNZfgUbdS78JoOByBFfhiW7nydLa08"
|
| 37 |
+
gnews_api_key = "2c381983217903362384b7e454fccd09"
|
| 38 |
+
newsapi_key = "e6e70c06012845b7a9c5fa1e53b79d83"
|
| 39 |
except KeyError as e:
|
| 40 |
st.error(f"Missing API key: {e}. Please configure it in secrets.toml.")
|
| 41 |
st.stop()
|
| 42 |
|
| 43 |
+
# Custom CSS
|
| 44 |
st.markdown("""
|
| 45 |
<style>
|
| 46 |
.main {
|
|
|
|
| 80 |
width: 150px;
|
| 81 |
}
|
| 82 |
.card {
|
| 83 |
+
background-color: #2d3748;
|
| 84 |
padding: 15px;
|
| 85 |
border-radius: 10px;
|
| 86 |
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
|
|
| 114 |
|
| 115 |
st.sidebar.caption("v2.1.0 | Last updated: April 2024")
|
| 116 |
|
|
|
|
| 117 |
# Home page
|
| 118 |
if page == "Home":
|
| 119 |
st.markdown("""
|
|
|
|
| 192 |
|
| 193 |
logo_path = "logo.ico"
|
| 194 |
if os.path.exists(logo_path):
|
| 195 |
+
col1, col2, col3 = st.columns([1, 2, 1])
|
| 196 |
with col2:
|
| 197 |
with open(logo_path, "rb") as f:
|
| 198 |
logo_bytes = f.read()
|
|
|
|
| 205 |
|
| 206 |
st.markdown("""
|
| 207 |
## Welcome to Your Career Transformation
|
| 208 |
+
|
| 209 |
CareerBoost is your **AI-powered career companion** designed to help you navigate every step of your professional
|
| 210 |
journey with confidence. Whether you're looking for your dream job, optimizing your resume, or preparing for
|
| 211 |
interviews, we've got you covered with smart, personalized tools.
|
|
|
|
| 233 |
""", unsafe_allow_html=True)
|
| 234 |
|
| 235 |
st.markdown("## ✨ How CareerBoost Helps You Succeed")
|
| 236 |
+
|
| 237 |
features = [
|
| 238 |
{
|
| 239 |
"title": "Smart Job Matching",
|
|
|
|
| 256 |
"icon": "🧭"
|
| 257 |
}
|
| 258 |
]
|
| 259 |
+
|
| 260 |
for feature in features:
|
| 261 |
st.markdown(f"""
|
| 262 |
<div class="feature-card">
|
|
|
|
| 275 |
st.title("🔍 Job Finding")
|
| 276 |
st.markdown("Search for job opportunities tailored to your preferences.")
|
| 277 |
|
| 278 |
+
|
| 279 |
def build_google_search_link(title, company):
|
| 280 |
query = f"{title} {company} apply job"
|
| 281 |
return f"https://www.google.com/search?q={quote_plus(query)}"
|
| 282 |
|
| 283 |
+
|
| 284 |
def search_serp(query, location, experience='fresher', job_category='full-time', num_results=10):
|
| 285 |
params = {
|
| 286 |
"engine": "google_jobs",
|
|
|
|
| 302 |
"location": job.get('location', 'N/A'),
|
| 303 |
"posted": job.get('detected_extensions', {}).get('posted_at', 'N/A'),
|
| 304 |
"description": job.get('description', '')[:200] + "...",
|
| 305 |
+
"apply_link": job.get("job_google_link") or build_google_search_link(job.get('title', ''),
|
| 306 |
+
job.get('company_name',
|
| 307 |
+
'Unknown')),
|
| 308 |
"source": "SerpAPI",
|
| 309 |
"category": job_category
|
| 310 |
}
|
|
|
|
| 313 |
except requests.exceptions.RequestException as e:
|
| 314 |
return [{"error": f"SerpAPI error: {str(e)}"}]
|
| 315 |
|
| 316 |
+
|
| 317 |
def duckduckgo_job_search(query: str, job_category: str) -> list:
|
| 318 |
try:
|
| 319 |
with DDGS() as ddgs:
|
|
|
|
| 334 |
except Exception as e:
|
| 335 |
return [{"error": f"DuckDuckGo error: {str(e)}"}]
|
| 336 |
|
| 337 |
+
|
| 338 |
def job_search(field, location, experience, job_category='full-time'):
|
| 339 |
query = f"find {field} job in {location} for {experience}"
|
| 340 |
return duckduckgo_job_search(query, job_category)
|
| 341 |
|
| 342 |
+
|
| 343 |
def rapid_job_searcher(job: str, location: str, pages: int = 1, country: str = "us") -> list:
|
| 344 |
conn = http.client.HTTPSConnection("jsearch.p.rapidapi.com")
|
| 345 |
headers = {
|
|
|
|
| 375 |
except Exception as e:
|
| 376 |
return [{"error": f"RapidAPI request failed: {str(e)}"}]
|
| 377 |
|
| 378 |
+
|
| 379 |
job_categories = ['full-time', 'part-time', 'intern', 'contract', 'temporary']
|
| 380 |
experience_levels = ['fresher', 'experienced', 'senior']
|
| 381 |
|
|
|
|
| 394 |
rapid_results = rapid_job_searcher(job, location)
|
| 395 |
ddg_results = job_search(job, location, experience, category)
|
| 396 |
serp_results = search_serp(job, location, experience, category)
|
| 397 |
+
all_results = {'DuckDuckGo': ddg_results,'RapidAPI': rapid_results, 'SerpAPI': serp_results}
|
| 398 |
|
| 399 |
for source, results in all_results.items():
|
| 400 |
+
st.subheader(f"Jobs Tailored for You")
|
| 401 |
if results and not any("error" in r for r in results):
|
| 402 |
for result in results:
|
| 403 |
with st.container():
|
|
|
|
| 414 |
else:
|
| 415 |
error_msg = results[0].get("error", "No results found.") if results else "No results found."
|
| 416 |
st.error(error_msg)
|
| 417 |
+
|
| 418 |
# CV Maker Section
|
| 419 |
elif page == "CV Maker":
|
| 420 |
st.title("📝 CV Maker")
|
|
|
|
| 603 |
interview_prompt = PromptTemplate(
|
| 604 |
input_variables=["job_field", "search_results"],
|
| 605 |
template="""
|
| 606 |
+
You are an interview preparation expert. Generate exactly 15 interview questions with detailed, professional answers for {job_field}. Do NOT provide links or references to external resources; focus on self-contained questions and answers.
|
| 607 |
Requirements:
|
| 608 |
+
- Include 6 technical questions, 5 behavioral questions, and 4 situational questions.
|
| 609 |
- Incorporate trends and frequently asked questions from 2022-2025.
|
| 610 |
- Use the search results for context to inform answers, but do not include raw search data or URLs in the output: {search_results}.
|
| 611 |
- Format as plain text with question numbers, type (Technical/Behavioral/Situational), questions, and answers.
|
|
|
|
| 633 |
st.markdown("### Interview Questions")
|
| 634 |
st.text_area("Questions and Answers", questions, height=400)
|
| 635 |
st.download_button(
|
| 636 |
+
label="Download Questions&Answer",
|
| 637 |
data=questions,
|
| 638 |
file_name="Interview_Questions.txt",
|
| 639 |
mime="text/plain"
|
|
|
|
| 648 |
|
| 649 |
newsapi = NewsApiClient(api_key=newsapi_key)
|
| 650 |
|
| 651 |
+
|
| 652 |
def get_gnews_articles():
|
| 653 |
url = f"https://gnews.io/api/v4/search?q=job%20market%20OR%20employment%20OR%20hiring%20OR%20recruitment%20OR%20careers%20OR%20job%20opportunities%20India%20OR%20tech%20OR%20IT%20OR%20technology&lang=en&country=in&max=10&apikey={gnews_api_key}"
|
| 654 |
try:
|
|
|
|
| 670 |
st.error(f"Failed to fetch GNews: {e.reason}")
|
| 671 |
return []
|
| 672 |
|
| 673 |
+
|
| 674 |
def get_indian_job_news():
|
| 675 |
try:
|
| 676 |
yesterday = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d') # Extended to 7 days
|
|
|
|
| 697 |
st.error(f"Unexpected error fetching job news: {str(e)}")
|
| 698 |
return []
|
| 699 |
|
| 700 |
+
|
| 701 |
def get_indian_tech_news():
|
| 702 |
try:
|
| 703 |
domains = 'economictimes.indiatimes.com,livemint.com,indiatoday.in'
|
|
|
|
| 722 |
st.error(f"Unexpected error fetching tech news: {str(e)}")
|
| 723 |
return []
|
| 724 |
|
| 725 |
+
|
| 726 |
def get_company_hiring_news():
|
| 727 |
companies = ['TCS', 'Infosys', 'Wipro', 'HCL', 'Tech Mahindra']
|
| 728 |
company_news = []
|
|
|
|
| 750 |
st.error(f"Unexpected error fetching company news: {str(e)}")
|
| 751 |
return []
|
| 752 |
|
| 753 |
+
|
| 754 |
with st.spinner("Fetching news..."):
|
| 755 |
gnews_articles = get_gnews_articles()
|
| 756 |
job_news = get_indian_job_news()
|
|
|
|
| 781 |
""", unsafe_allow_html=True)
|
| 782 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 783 |
else:
|
| 784 |
+
st.markdown('<p class="no-news">No job market news available at the moment. Please try again later.</p>',
|
| 785 |
+
unsafe_allow_html=True)
|
| 786 |
|
| 787 |
# Company Hiring News Section
|
| 788 |
st.markdown('<h2 class="section-header">Company Hiring News</h2>', unsafe_allow_html=True)
|
|
|
|
| 799 |
""", unsafe_allow_html=True)
|
| 800 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 801 |
else:
|
| 802 |
+
st.markdown('<p class="no-news">No company hiring news available at the moment. Please try again later.</p>',
|
| 803 |
+
unsafe_allow_html=True)
|
| 804 |
|
| 805 |
# Tech Industry News Section
|
| 806 |
st.markdown('<h2 class="section-header">Tech Industry News</h2>', unsafe_allow_html=True)
|
|
|
|
| 817 |
""", unsafe_allow_html=True)
|
| 818 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 819 |
else:
|
| 820 |
+
st.markdown('<p class="no-news">No tech industry news available at the moment. Please try again later.</p>',
|
| 821 |
+
unsafe_allow_html=True)
|
| 822 |
|
| 823 |
|
| 824 |
# About Section
|