Spaces:
Sleeping
Sleeping
Upload 6 files
Browse filesimitial commit of careerboost
- .gitattributes +1 -0
- Agent.py +292 -0
- LICENSE.md +108 -0
- README.md +116 -9
- app.py +879 -0
- logo.ico +3 -0
- requirements.txt +8 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
logo.ico filter=lfs diff=lfs merge=lfs -text
|
Agent.py
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import random
|
| 4 |
+
import asyncio
|
| 5 |
+
import aiohttp
|
| 6 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 7 |
+
from langchain.agents import AgentType, initialize_agent, Tool
|
| 8 |
+
from langchain.prompts import PromptTemplate
|
| 9 |
+
from langchain.memory import ConversationBufferMemory
|
| 10 |
+
from langchain.tools import Tool
|
| 11 |
+
from duckduckgo_search import DDGS
|
| 12 |
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
| 13 |
+
from functools import lru_cache
|
| 14 |
+
import re
|
| 15 |
+
import http.client
|
| 16 |
+
import urllib.parse
|
| 17 |
+
|
| 18 |
+
# Random User-Agent
|
| 19 |
+
def get_random_user_agent():
|
| 20 |
+
""" Various user-agent strings for Windows, macOS, Linux, Mobile devices, Tablets, Consoles, Smart TVs
|
| 21 |
+
This helps avoid being blocked by websites due to repetitive scraping
|
| 22 |
+
List of user agents truncated for brevity"""
|
| 23 |
+
USER_AGENTS = [
|
| 24 |
+
# Windows User Agents
|
| 25 |
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
| 26 |
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
|
| 27 |
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/91.0.864.59 Safari/537.36',
|
| 28 |
+
'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
|
| 29 |
+
|
| 30 |
+
# macOS User Agents
|
| 31 |
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
| 32 |
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
|
| 33 |
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:89.0) Gecko/20100101 Firefox/89.0',
|
| 34 |
+
|
| 35 |
+
# Linux User Agents
|
| 36 |
+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
| 37 |
+
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0',
|
| 38 |
+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.124 Safari/537.36',
|
| 39 |
+
|
| 40 |
+
# Mobile User Agents (Android)
|
| 41 |
+
'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
|
| 42 |
+
'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
|
| 43 |
+
'Mozilla/5.0 (Linux; Android 9; SM-G960F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
|
| 44 |
+
|
| 45 |
+
# Mobile User Agents (iOS)
|
| 46 |
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
|
| 47 |
+
'Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
|
| 48 |
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/91.0.4472.124 Mobile/15E148 Safari/604.1', # Chrome on iOS
|
| 49 |
+
|
| 50 |
+
# Tablet User Agents
|
| 51 |
+
'Mozilla/5.0 (Linux; Android 10; SM-T860) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Safari/537.36',
|
| 52 |
+
'Mozilla/5.0 (Linux; Android 11; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Safari/537.36',
|
| 53 |
+
'Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
|
| 54 |
+
|
| 55 |
+
# Gaming Consoles
|
| 56 |
+
'Mozilla/5.0 (PlayStation 4 8.52) AppleWebKit/605.1.15 (KHTML, like Gecko)',
|
| 57 |
+
'Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/609.4 (KHTML, like Gecko) NF/6.0.2.19.3 NintendoBrowser/5.1.0.22401',
|
| 58 |
+
|
| 59 |
+
# Smart TVs
|
| 60 |
+
'Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 WebAppManager',
|
| 61 |
+
'Mozilla/5.0 (SMART-TV; Linux; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.0 Chrome/91.0.4472.124 Safari/537.36',
|
| 62 |
+
|
| 63 |
+
]
|
| 64 |
+
return random.choice(USER_AGENTS)
|
| 65 |
+
|
| 66 |
+
# Load API keys
|
| 67 |
+
google_api_key = os.getenv("GOOGLE_API_KEY")
|
| 68 |
+
rapidapi_key = os.getenv("RAPIDAPI_KEY")
|
| 69 |
+
|
| 70 |
+
if not google_api_key:
|
| 71 |
+
raise ValueError("Google API key not found.")
|
| 72 |
+
if not rapidapi_key:
|
| 73 |
+
raise ValueError("RapidAPI key not found.")
|
| 74 |
+
|
| 75 |
+
# Initialize LLM
|
| 76 |
+
llm = ChatGoogleGenerativeAI(
|
| 77 |
+
model="gemini-1.5-flash",
|
| 78 |
+
google_api_key=google_api_key
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
# DuckDuckGo search
|
| 82 |
+
def duckduckgo_search(query: str) -> str:
|
| 83 |
+
try:
|
| 84 |
+
with DDGS() as ddgs:
|
| 85 |
+
results = [r for r in ddgs.text(query, max_results=20)]
|
| 86 |
+
return json.dumps(results, indent=2)
|
| 87 |
+
except Exception as e:
|
| 88 |
+
return json.dumps({"error": f"Error in WebSearch: {str(e)}"}, indent=2)
|
| 89 |
+
|
| 90 |
+
duckduckgo_tool = Tool(
|
| 91 |
+
name="WebSearch",
|
| 92 |
+
func=duckduckgo_search,
|
| 93 |
+
description="Use this tool to search the web for job listings or interview questionss"
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
# Job finding agent
|
| 98 |
+
job_prompt = PromptTemplate(
|
| 99 |
+
input_variables=["input", "chat_history", "tools", "tool_names", "agent_scratchpad"],
|
| 100 |
+
template="""
|
| 101 |
+
You are an advanced job search assistant focused on finding job vacancies worldwide, with a special emphasis on India.
|
| 102 |
+
Your tasks:
|
| 103 |
+
- Find relevant job listings based on the field and location, including job titles, companies, locations, and links.
|
| 104 |
+
- Use the JobScraper tool first to get detailed job listings from job boards like Naukri.com, Shine.com, LinkedIn, and Indeed and other indian job boards.
|
| 105 |
+
- If JobScraper fails or returns no valid jobs, use WebSearch to find job-related information and extract relevant details.
|
| 106 |
+
- Avoid duplicate listings by checking job titles, companies, and locations.
|
| 107 |
+
- Format the output as a numbered list with: Title, Company, Location, Link, Source.
|
| 108 |
+
- If no jobs are found, clearly state so and provide any relevant links from WebSearch.
|
| 109 |
+
- Include all valid job details from JobScraper observations in the final answer.
|
| 110 |
+
|
| 111 |
+
Available tools: {tools}
|
| 112 |
+
Tool names: {tool_names}
|
| 113 |
+
|
| 114 |
+
User input: {input}
|
| 115 |
+
Chat history: {chat_history}
|
| 116 |
+
Agent scratchpad: {agent_scratchpad}
|
| 117 |
+
"""
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
job_memory = ConversationBufferMemory(memory_key="chat_history")
|
| 121 |
+
job_agent = initialize_agent(
|
| 122 |
+
tools=[duckduckgo_tool],
|
| 123 |
+
llm=llm,
|
| 124 |
+
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
|
| 125 |
+
verbose=True,
|
| 126 |
+
memory=job_memory,
|
| 127 |
+
handle_parsing_errors=True,
|
| 128 |
+
custom_prompt=job_prompt
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
# RapidAPI job searcher
|
| 133 |
+
def rapid_job_seacrher(job: str, location: str, pages: int = 1, country: str = "in") -> str:
|
| 134 |
+
conn = http.client.HTTPSConnection("jsearch.p.rapidapi.com")
|
| 135 |
+
headers = {
|
| 136 |
+
'x-rapidapi-key': rapidapi_key,
|
| 137 |
+
'x-rapidapi-host': "jsearch.p.rapidapi.com"
|
| 138 |
+
}
|
| 139 |
+
query = urllib.parse.quote(f"{job} jobs in {location}")
|
| 140 |
+
conn.request("GET", f"/search?query={query}&page=1&num_pages={pages}&country={country}&date_posted=all", headers=headers)
|
| 141 |
+
res = conn.getresponse()
|
| 142 |
+
data = res.read()
|
| 143 |
+
results = []
|
| 144 |
+
|
| 145 |
+
try:
|
| 146 |
+
data_json = json.loads(data.decode("utf-8"))
|
| 147 |
+
except json.JSONDecodeError as e:
|
| 148 |
+
return json.dumps({"error": f"Error parsing JSON: {str(e)}"}, indent=2)
|
| 149 |
+
|
| 150 |
+
for job in data_json.get('data', []):
|
| 151 |
+
title = job.get('job_title', 'N/A')
|
| 152 |
+
company = job.get('employer_name', 'N/A')
|
| 153 |
+
city = job.get('job_city', '')
|
| 154 |
+
state = job.get('job_state', '')
|
| 155 |
+
location_parts = [part for part in [city, state] if part]
|
| 156 |
+
location = ", ".join(location_parts) if location_parts else "N/A"
|
| 157 |
+
job_url = job.get('job_apply_link', 'N/A')
|
| 158 |
+
results.append({
|
| 159 |
+
"title": title,
|
| 160 |
+
"company": company,
|
| 161 |
+
"location": location,
|
| 162 |
+
"link": job_url,
|
| 163 |
+
"source": "RapidAPI"
|
| 164 |
+
})
|
| 165 |
+
|
| 166 |
+
return json.dumps(results if results else {"error": "No jobs found."}, indent=2)
|
| 167 |
+
|
| 168 |
+
# #Remove common markdown characters from text using regex
|
| 169 |
+
# def remove_markdown(text: str) -> str:
|
| 170 |
+
# patterns = [
|
| 171 |
+
# (r'^#+ ?', ''),
|
| 172 |
+
# (r'\*\*(.*?)\*\*', r'\1'),
|
| 173 |
+
# (r'\*(.*?)\*', r'\1'),
|
| 174 |
+
# (r'^- ?', ''),
|
| 175 |
+
# (r'\[([^\]]+)\]\([^\)]+\)', r'\1'),
|
| 176 |
+
# (r'^\s*:\s*', ''),
|
| 177 |
+
# (r'`{1,3}[^`]+`{1,3}', lambda m: m.group(0).replace('`', ''))
|
| 178 |
+
# ]
|
| 179 |
+
# cleaned_text = text
|
| 180 |
+
# for pattern, replacement in patterns:
|
| 181 |
+
# cleaned_text = re.sub(pattern, replacement, cleaned_text, flags=re.MULTILINE)
|
| 182 |
+
# cleaned_text = re.sub(r'\n\s*\n', '\n', cleaned_text).strip()
|
| 183 |
+
# return cleaned_text
|
| 184 |
+
|
| 185 |
+
# Interview preparation
|
| 186 |
+
def interview_preparer(job_field: str) -> str:
|
| 187 |
+
try:
|
| 188 |
+
if not job_field or not isinstance(job_field, str):
|
| 189 |
+
return "Error: Invalid job field provided."
|
| 190 |
+
|
| 191 |
+
search_queries = [
|
| 192 |
+
f"{job_field} interview questions 2022-2025",
|
| 193 |
+
f"site:reddit.com {job_field} interview questions",
|
| 194 |
+
f"site:quora.com {job_field} interview questions"
|
| 195 |
+
]
|
| 196 |
+
search_results = []
|
| 197 |
+
for query in search_queries:
|
| 198 |
+
try:
|
| 199 |
+
result = duckduckgo_search(query)
|
| 200 |
+
if not result.startswith("Error"):
|
| 201 |
+
search_results.append(json.loads(result))
|
| 202 |
+
except Exception as e:
|
| 203 |
+
search_results.append({"source": query, "error": str(e)})
|
| 204 |
+
|
| 205 |
+
combined_results = json.dumps(search_results, indent=2)
|
| 206 |
+
interview_prompt = PromptTemplate(
|
| 207 |
+
input_variables=["job_field", "search_results"],
|
| 208 |
+
template="""
|
| 209 |
+
You are an interview preparation expert. Generate exactly 10 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.
|
| 210 |
+
Requirements:
|
| 211 |
+
- Include 4 technical questions, 3 behavioral questions, and 3 situational questions.
|
| 212 |
+
- Incorporate trends and frequently asked questions from 2022-2025.
|
| 213 |
+
- Use the search results for context to inform answers, but do not include raw search data or URLs in the output: {search_results}.
|
| 214 |
+
- Format as plain text with question numbers, type (Technical/Behavioral/Situational), questions, and answers.
|
| 215 |
+
Example:
|
| 216 |
+
1. Technical: [Question]
|
| 217 |
+
Answer: [Detailed answer]
|
| 218 |
+
"""
|
| 219 |
+
)
|
| 220 |
+
response = llm.invoke(interview_prompt.format(job_field=job_field, search_results=combined_results))
|
| 221 |
+
return response.content
|
| 222 |
+
except Exception as e:
|
| 223 |
+
return f"Error generating interview questions: {str(e)}"
|
| 224 |
+
|
| 225 |
+
interview_tool = Tool(
|
| 226 |
+
name="InterviewPreparer",
|
| 227 |
+
func=interview_preparer,
|
| 228 |
+
description="Generate 10 interview questions and answers for a job field (4 technical, 3 behavioral, 3 situational)."
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
interview_prompt = PromptTemplate(
|
| 232 |
+
input_variables=["input", "chat_history", "tools", "tool_names", "agent_scratchpad"],
|
| 233 |
+
template="""
|
| 234 |
+
You are an interview preparation assistant. Your task is to:
|
| 235 |
+
- Extract the job field from the user input (e.g., 'Prepare interview for data science' → job_field='data science').
|
| 236 |
+
- Use the InterviewPreparer tool exactly once to generate 10 interview questions with answers (4 technical, 3 behavioral, 3 situational).
|
| 237 |
+
- Do NOT attempt to create questions manually or simulate the tool's output.
|
| 238 |
+
- If the job field is unclear, return an error message asking for clarification.
|
| 239 |
+
- In the Final Answer, return only the tool's output as plain text, with no additional commentary.
|
| 240 |
+
|
| 241 |
+
Follow this strict format:
|
| 242 |
+
Thought: [Your reasoning]
|
| 243 |
+
Action: InterviewPreparer
|
| 244 |
+
Action Input: job_field="[job_field]"
|
| 245 |
+
Observation: [Tool output]
|
| 246 |
+
Final Answer: [Tool output]
|
| 247 |
+
|
| 248 |
+
Available tools: {tools}
|
| 249 |
+
Tool names: {tool_names}
|
| 250 |
+
|
| 251 |
+
User input: {input}
|
| 252 |
+
Chat history: {chat_history}
|
| 253 |
+
Agent scratchpad: {agent_scratchpad}
|
| 254 |
+
"""
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
interview_memory = ConversationBufferMemory(memory_key="chat_history")
|
| 258 |
+
interview_agent = initialize_agent(
|
| 259 |
+
tools=[interview_tool],
|
| 260 |
+
llm=llm,
|
| 261 |
+
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
|
| 262 |
+
verbose=True,
|
| 263 |
+
memory=interview_memory,
|
| 264 |
+
handle_parsing_errors=True,
|
| 265 |
+
custom_prompt=interview_prompt
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
# CV creator
|
| 269 |
+
cv_llm = ChatGoogleGenerativeAI(
|
| 270 |
+
model="gemini-1.5-flash",
|
| 271 |
+
google_api_key=google_api_key,
|
| 272 |
+
temperature=0.1,
|
| 273 |
+
max_output_tokens=2048
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
cv_prompt = PromptTemplate(
|
| 277 |
+
input_variables=["job_field", "experience"],
|
| 278 |
+
template="""
|
| 279 |
+
You are a professional CV writer. Create a concise, ATS-friendly CV for a {job_field} position based on the following details:
|
| 280 |
+
- User Details: Name: John Doe, Email: john.doe@example.com, Phone: +91-9876543210
|
| 281 |
+
- Experience and skills: {experience}
|
| 282 |
+
Include sections for Summary, Skills, Experience, and Education. Format as plain text for clarity.
|
| 283 |
+
"""
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
def generate_cv(job_field: str, experience: str) -> str:
|
| 287 |
+
try:
|
| 288 |
+
prompt = cv_prompt.format(job_field=job_field, experience=experience)
|
| 289 |
+
response = cv_llm.invoke(prompt)
|
| 290 |
+
return response.content
|
| 291 |
+
except Exception as e:
|
| 292 |
+
return f"Error generating CV: {str(e)}"
|
LICENSE.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CareerBoost Code License
|
| 2 |
+
**Attribution-NonCommercial-API Limited License v2.0**
|
| 3 |
+
|
| 4 |
+
Copyright (c) 2025 Musabbir KM (<musabbirmushu@gmail.com>)
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## 1. Definitions
|
| 9 |
+
- **Software**: The CareerBoost application, including all source code, documentation, and associated files in the repository.
|
| 10 |
+
- **Agent Code**: Proprietary AI logic, including but not limited to AI agents (Job Finding, Interview Preparation, CV Creator), training pipelines, prompts, and algorithms.
|
| 11 |
+
- **Public Interface Code**: Non-proprietary frontend code (e.g., Streamlit UI in `app.py`) excluding Agent Code.
|
| 12 |
+
- **Derivative Work**: Any modification, adaptation, or extension of the Software, including forks or integrations into other projects.
|
| 13 |
+
- **Third-Party APIs**: External services integrated into the Software, including but not limited to Google Gemini LLM, RapidAPI JSearch, and DuckDuckGo Search.
|
| 14 |
+
- **You**: Any individual, entity, or organization accessing, using, or modifying the Software.
|
| 15 |
+
|
| 16 |
+
## 2. Grant of License
|
| 17 |
+
Subject to the terms of this License, Musabbir KM grants You a **non-exclusive, non-transferable, revocable, limited license** to:
|
| 18 |
+
- ✅ Use the hosted Software on Hugging Face Spaces (`https://musabbirkm-deephirex.hf.space`) for personal, non-commercial purposes.
|
| 19 |
+
- ✅ Study the Public Interface Code for educational purposes.
|
| 20 |
+
- ✅ Fork the repository for non-commercial testing or personal projects, provided all requirements below are met.
|
| 21 |
+
- ✅ Modify the Public Interface Code for personal learning, subject to attribution.
|
| 22 |
+
|
| 23 |
+
## 3. Attribution Requirements
|
| 24 |
+
If You use, modify, or distribute any part of the Software or create a Derivative Work, You **MUST**:
|
| 25 |
+
- 📜 **Credit the Author**: Prominently display the following notice in the user interface (e.g., GUI, webpage), documentation, and source code of Your application or Derivative Work:
|
| 26 |
+
### CareerBoost: Originally developed by Musabbir KM (musabbirmushu@gmail.com)
|
| 27 |
+
|
| 28 |
+
- 📜 Include a link to the original repository (`https://huggingface.co/spaces/musabbirkm/DeepHireX`) where feasible (e.g., in documentation or UI).
|
| 29 |
+
- 📜 Retain this License file, including the copyright notice, in any copy or Derivative Work.
|
| 30 |
+
- 📜 Ensure the attribution is visible to end users (e.g., in an "About" section, footer, or credits page) and not obscured or minimized.
|
| 31 |
+
|
| 32 |
+
Failure to comply with these attribution requirements voids this License.
|
| 33 |
+
|
| 34 |
+
## 4. Permitted Use
|
| 35 |
+
You are permitted to:
|
| 36 |
+
- ✅ Access the Software via Hugging Face Spaces for job searching, interview preparation, or CV creation.
|
| 37 |
+
- ✅ Run the Software locally for personal use, provided You supply Your own API keys for Third-Party APIs.
|
| 38 |
+
- ✅ Share non-commercial Derivative Works, provided they comply with all attribution requirements and restrictions.
|
| 39 |
+
|
| 40 |
+
## 5. Restrictions
|
| 41 |
+
You **MAY NOT**:
|
| 42 |
+
- ❌ Use the Software or any Derivative Work for commercial purposes, including but not limited to SaaS offerings, paid services, or revenue-generating products, without express written permission.
|
| 43 |
+
- ❌ Redistribute, sublicense, or transfer the Agent Code or any proprietary algorithms without prior authorization.
|
| 44 |
+
- ❌ Remove, alter, or obscure any copyright notices, attribution requirements, or this License in the Software or Derivative Works.
|
| 45 |
+
- ❌ Claim ownership of the Software, Agent Code, or core AI methodologies.
|
| 46 |
+
- ❌ Use the Software in a manner that violates the terms of any Third-Party APIs.
|
| 47 |
+
- ❌ Deploy the Software on platforms other than Hugging Face Spaces for public use without permission.
|
| 48 |
+
- ❌ Reverse-engineer, decompile, or attempt to extract the Agent Code or proprietary logic.
|
| 49 |
+
|
| 50 |
+
## 6. Third-Party APIs
|
| 51 |
+
⚠️ You acknowledge and agree that:
|
| 52 |
+
- The Software integrates Third-Party APIs (e.g., Google Gemini, RapidAPI), each governed by their own terms of service.
|
| 53 |
+
- You are solely responsible for:
|
| 54 |
+
- Obtaining and maintaining valid API keys.
|
| 55 |
+
- Complying with all Third-Party API terms, including usage limits and data privacy obligations.
|
| 56 |
+
- Any costs, liabilities, or damages arising from Your use of Third-Party APIs.
|
| 57 |
+
- Musabbir KM is not liable for interruptions, errors, or changes in Third-Party API availability.
|
| 58 |
+
|
| 59 |
+
## 7. Ownership
|
| 60 |
+
- The Software and Agent Code are the intellectual property of **Musabbir KM**.
|
| 61 |
+
- All rights not expressly granted in this License are reserved.
|
| 62 |
+
- Contributions to the Software (e.g., pull requests) may require assignment of rights to Musabbir KM, to be agreed upon separately.
|
| 63 |
+
|
| 64 |
+
## 8. Warranty Disclaimer
|
| 65 |
+
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. MUSABBIR KM SHALL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR INABILITY TO USE THE SOFTWARE, INCLUDING DATA LOSS, BUSINESS INTERRUPTION, OR API-RELATED COSTS.
|
| 66 |
+
|
| 67 |
+
## 9. Termination
|
| 68 |
+
- This License is effective until terminated.
|
| 69 |
+
- It will terminate automatically if You violate any terms, including failure to provide attribution or unauthorized commercial use.
|
| 70 |
+
- Upon termination, You must cease all use of the Software and destroy any copies or Derivative Works in Your possession.
|
| 71 |
+
|
| 72 |
+
## 10. Permission for Extended Use
|
| 73 |
+
For commercial use, Agent Code access, or public deployment beyond Hugging Face Spaces, You must obtain written permission by contacting:
|
| 74 |
+
- 📧 **Email**: musabbirmushu@gmail.com
|
| 75 |
+
- ✉️ **Required Information**:
|
| 76 |
+
- Your full name or organization name.
|
| 77 |
+
- Detailed description of the intended use case (e.g., app integration, research, commercial product).
|
| 78 |
+
- Project duration, scope, and expected user base.
|
| 79 |
+
- Whether Agent Code access is requested.
|
| 80 |
+
- Musabbir KM reserves the right to grant or deny permission at his sole discretion, potentially requiring a separate licensing agreement or fees.
|
| 81 |
+
|
| 82 |
+
## 11. Enforcement
|
| 83 |
+
- Violations of this License, including unauthorized commercial use or failure to attribute, will result in legal action under applicable intellectual property laws.
|
| 84 |
+
- You agree to indemnify Musabbir KM for any claims, damages, or costs arising from Your misuse of the Software.
|
| 85 |
+
|
| 86 |
+
## 12. Governing Law
|
| 87 |
+
This License is governed by the laws of **India**, without regard to conflict of law principles. Any disputes shall be resolved in the courts of **Kerala, India**.
|
| 88 |
+
|
| 89 |
+
## 13. Severability
|
| 90 |
+
If any provision of this License is found to be unenforceable, the remaining provisions will continue in full force and effect.
|
| 91 |
+
|
| 92 |
+
## 14. Amendments
|
| 93 |
+
Musabbir KM reserves the right to amend this License at any time. Updated versions will be posted in the repository, and continued use of the Software constitutes acceptance of the new terms.
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
**Important Notice**: This License does not override or modify the terms of any Third-Party APIs integrated into the Software. You must independently comply with those terms.
|
| 98 |
+
|
| 99 |
+
**Contact**:
|
| 100 |
+
- Author: Musabbir KM
|
| 101 |
+
- Email: musabbirmushu@gmail.com
|
| 102 |
+
- Repository: https://huggingface.co/spaces/musabbirkm/DeepHireX
|
| 103 |
+
|
| 104 |
+
**Last Updated**: April 14, 2025
|
| 105 |
+
|
| 106 |
+
---
|
| 107 |
+
|
| 108 |
+
*CareerBoost: Empowering Your Career Journey with AI-Driven Tools*
|
README.md
CHANGED
|
@@ -1,14 +1,121 @@
|
|
| 1 |
---
|
|
|
|
| 2 |
title: CareerBoost
|
| 3 |
-
emoji: 🐠
|
| 4 |
-
colorFrom: gray
|
| 5 |
-
colorTo: blue
|
| 6 |
sdk: streamlit
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
short_description:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
license: other
|
| 3 |
title: CareerBoost
|
|
|
|
|
|
|
|
|
|
| 4 |
sdk: streamlit
|
| 5 |
+
emoji: 📈
|
| 6 |
+
colorFrom: blue
|
| 7 |
+
colorTo: purple
|
| 8 |
+
pinned: true
|
| 9 |
+
short_description: AI-powered, futuristic, and career-accelerating
|
| 10 |
+
---
|
| 11 |
+
# CareerBoost: Your Job Search & Prep Companion
|
| 12 |
+
|
| 13 |
+
**Empowering Your Career Journey with AI-Driven Tools**
|
| 14 |
+
|
| 15 |
+
**Creator**: Musabbir KM
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## Overview
|
| 20 |
+
|
| 21 |
+
CareerBoost is an AI-powered web application designed to assist job seekers in finding job opportunities, preparing for interviews, and creating professional CVs. Built with a focus on usability and efficiency, it leverages advanced AI agents to deliver tailored results for users worldwide, with a special emphasis on the Indian job market.
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
## Features
|
| 26 |
+
|
| 27 |
+
CareerBoost offers four core functionalities, each powered by specialized AI agents:
|
| 28 |
+
|
| 29 |
+
1. **Job Finding Agent**:
|
| 30 |
+
- Scrapes job listings from major job boards like Naukri.com, Shine.com, LinkedIn, and Indeed
|
| 31 |
+
- Supports location-based searches (e.g., "data science in Kochi")
|
| 32 |
+
- Displays detailed job information: title, company, location, salary range, and application link
|
| 33 |
+
- Intelligent fallback to web search when direct scraping fails
|
| 34 |
+
- Real-time alerts for new postings matching your profile
|
| 35 |
+
- Fetches structured job listings using the RapidAPI JSearch endpoint
|
| 36 |
+
|
| 37 |
+
2. **Interview Preparation**:
|
| 38 |
+
- Generates 10 tailored interview questions and answers (4 technical, 3 behavioral, 3 situational)
|
| 39 |
+
- Incorporates latest trends (2022–2025) covering emerging skills like:
|
| 40 |
+
- Machine Learning Ops (MLOps)
|
| 41 |
+
- Ethical AI frameworks
|
| 42 |
+
- Cloud-native technologies
|
| 43 |
+
- Data visualization tools (Tableau, PowerBI)
|
| 44 |
+
- Includes company-specific question banks for top employers
|
| 45 |
+
- Provides sample answers with STAR (Situation-Task-Action-Result) format
|
| 46 |
+
- Delivers plain-text output for easy review and practice
|
| 47 |
+
|
| 48 |
+
3. **CV Creator**:
|
| 49 |
+
- Builds ATS-friendly CVs optimized for applicant tracking systems
|
| 50 |
+
- Custom templates for different experience levels (Entry, Mid, Senior)
|
| 51 |
+
- Includes smart sections:
|
| 52 |
+
- Professional Summary with keywords
|
| 53 |
+
- Skills Matrix with proficiency levels
|
| 54 |
+
- Experience with measurable achievements
|
| 55 |
+
- Education with relevant coursework
|
| 56 |
+
- Auto-formatting for consistent styling
|
| 57 |
+
- Export options (PDF, DOCX, plain text)
|
| 58 |
+
|
| 59 |
+
4. **Career Insights**:
|
| 60 |
+
- Daily curated feed of job market trends and tech news
|
| 61 |
+
- Company watchlists with hiring alerts
|
| 62 |
+
- Salary benchmarking by role and location
|
| 63 |
+
- Emerging technology spotlights (AI, Blockchain, IoT)
|
| 64 |
+
- Industry-specific reports (IT, Healthcare, Finance)
|
| 65 |
+
- Local job market heatmaps
|
| 66 |
+
- Skill gap analysis with learning recommendations
|
| 67 |
+
- Networking event calendars
|
| 68 |
+
|
| 69 |
+
---
|
| 70 |
+
|
| 71 |
+
## Technologies Used
|
| 72 |
+
|
| 73 |
+
CareerBoost is built with a robust tech stack to ensure performance and scalability:
|
| 74 |
+
|
| 75 |
+
- **Frontend**:
|
| 76 |
+
- [Streamlit](https://streamlit.io/) (v1.29.0): For the interactive web interface.
|
| 77 |
+
- Custom CSS: For enhanced UI styling (tabs, cards, logo display).
|
| 78 |
+
|
| 79 |
+
- **Backend & AI**:
|
| 80 |
+
- [LangChain](https://python.langchain.com/) (v0.2.16): For agent orchestration and tool integration.
|
| 81 |
+
- [Google Gemini LLM](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini) (via langchain-google-genai v1.0.8): Powers natural language processing and generation.
|
| 82 |
+
- [DuckDuckGo Search](https://github.com/deedy5/duckduckgo_search) (v6.2.11): For fallback web searches.
|
| 83 |
+
- [RapidAPI JSearch](https://rapidapi.com/letscrape-6bRBaM6guO5/api/jsearch): For structured job data.
|
| 84 |
+
- [aiohttp](https://docs.aiohttp.org/) (v3.10.5): For asynchronous web scraping.
|
| 85 |
+
- [Selectolax](https://github.com/rushter/selectolax) (v0.3.21): For efficient HTML parsing.
|
| 86 |
+
- [Tenacity](https://github.com/jd/tenacity) (v8.5.0): For retry logic in scraping.
|
| 87 |
+
- [python-dotenv](https://github.com/theskumar/python-dotenv) (v1.0.1): For environment variable management.
|
| 88 |
+
|
| 89 |
+
---
|
| 90 |
+
|
| 91 |
+
## Agent Info
|
| 92 |
+
|
| 93 |
+
CareerBoost leverages four specialized AI agents, each designed for a specific task:
|
| 94 |
+
|
| 95 |
+
- **Job Finding Agent**:
|
| 96 |
+
- Uses a ReAct (Reasoning + Acting) framework to scrape job boards asynchronously.
|
| 97 |
+
- Handles errors like rate limits and timeouts with retries and randomized delays.
|
| 98 |
+
- Formats output as a numbered list for clarity.
|
| 99 |
+
|
| 100 |
+
- **RapidAPI Job Search Agent**:
|
| 101 |
+
- Queries the JSearch API for structured job data.
|
| 102 |
+
- Processes results into a consistent format (title, company, location, link, source).
|
| 103 |
+
|
| 104 |
+
- **Interview Preparation Agent**:
|
| 105 |
+
- Generates 10 questions and answers using the Google Gemini LLM.
|
| 106 |
+
- Integrates web search insights to reflect recent trends (e.g., cloud computing, ethical AI).
|
| 107 |
+
- Fixed to prevent iteration limit errors by enforcing strict tool usage.
|
| 108 |
+
|
| 109 |
+
- **CV Creator Agent**:
|
| 110 |
+
- Generates ATS-friendly CVs with a single LLM call.
|
| 111 |
+
- Customizes content based on user input, ensuring relevance to the job field.
|
| 112 |
+
|
| 113 |
---
|
| 114 |
|
| 115 |
+
## Get In Touch
|
| 116 |
+
- We’d love to hear from you! Whether you have questions, feedback, or just want to chat, reach out to us anytime.
|
| 117 |
+
- 📧 **Email**: [musabbirmushu@gmail.com](mailto:musabbirmushu@gmail.com)
|
| 118 |
+
- 💼 **LinkedIn**: [CareerBoost LinkedIn](https://www.linkedin.com/in/muhammed-musabbir-km-0302b8212utm_source=share&utm_campaign=share_via&utm_content=profile&utm_medium=android_appt)
|
| 119 |
+
- 🌐 **Website**: [www.careerboost.ai](https://omnicipher.onrender.com)
|
| 120 |
+
- ⌨️ **GitHub**: [CareerBoost GitHub](https://github.com/musabbirkm)
|
| 121 |
+
|
app.py
ADDED
|
@@ -0,0 +1,879 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import requests
|
| 3 |
+
from urllib.parse import quote_plus
|
| 4 |
+
from duckduckgo_search import DDGS
|
| 5 |
+
import urllib.parse
|
| 6 |
+
import json
|
| 7 |
+
import http.client
|
| 8 |
+
from langchain.prompts import PromptTemplate
|
| 9 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 10 |
+
from reportlab.lib.pagesizes import letter
|
| 11 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, ListFlowable, ListItem
|
| 12 |
+
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 13 |
+
from io import BytesIO
|
| 14 |
+
from newsapi import NewsApiClient
|
| 15 |
+
from datetime import datetime, timedelta
|
| 16 |
+
import urllib.request
|
| 17 |
+
import os
|
| 18 |
+
import base64
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# Streamlit page configuration
|
| 22 |
+
st.set_page_config(
|
| 23 |
+
page_title="CareerBoost",
|
| 24 |
+
page_icon="💼",
|
| 25 |
+
layout="wide",
|
| 26 |
+
initial_sidebar_state="expanded"
|
| 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 {
|
| 43 |
+
background-color: #f0f2f6;
|
| 44 |
+
padding: 20px;
|
| 45 |
+
border-radius: 10px;
|
| 46 |
+
}
|
| 47 |
+
.stButton>button {
|
| 48 |
+
background-color: #4CAF50;
|
| 49 |
+
color: white;
|
| 50 |
+
border-radius: 5px;
|
| 51 |
+
padding: 10px 20px;
|
| 52 |
+
font-weight: bold;
|
| 53 |
+
}
|
| 54 |
+
.stButton>button:hover {
|
| 55 |
+
background-color: #45a049;
|
| 56 |
+
}
|
| 57 |
+
.stTextInput>div>input, .stTextArea textarea {
|
| 58 |
+
border-radius: 5px;
|
| 59 |
+
padding: 10px;
|
| 60 |
+
}
|
| 61 |
+
.stSelectbox>div>div {
|
| 62 |
+
border-radius: 5px;
|
| 63 |
+
}
|
| 64 |
+
.header {
|
| 65 |
+
text-align: center;
|
| 66 |
+
margin-bottom: 20px;
|
| 67 |
+
}
|
| 68 |
+
.caption {
|
| 69 |
+
text-align: center;
|
| 70 |
+
color: #666;
|
| 71 |
+
font-style: italic;
|
| 72 |
+
}
|
| 73 |
+
.logo {
|
| 74 |
+
display: block;
|
| 75 |
+
margin: 0 auto;
|
| 76 |
+
width: 150px;
|
| 77 |
+
}
|
| 78 |
+
.card {
|
| 79 |
+
background-color: white;
|
| 80 |
+
padding: 15px;
|
| 81 |
+
border-radius: 10px;
|
| 82 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
| 83 |
+
margin-bottom: 20px;
|
| 84 |
+
}
|
| 85 |
+
</style>
|
| 86 |
+
""", unsafe_allow_html=True)
|
| 87 |
+
|
| 88 |
+
# Sidebar navigation
|
| 89 |
+
st.sidebar.title("CareerBoost")
|
| 90 |
+
st.sidebar.markdown("""
|
| 91 |
+
**Your AI-powered career assistant**
|
| 92 |
+
*Smart tools for job search, CV optimization, and interview success.*
|
| 93 |
+
""")
|
| 94 |
+
page = st.sidebar.selectbox(
|
| 95 |
+
"Choose a section",
|
| 96 |
+
["Home", "Job Finding", "CV Maker", "Interview Preparation", "Career Insights", "About"]
|
| 97 |
+
)
|
| 98 |
+
st.sidebar.markdown("""
|
| 99 |
+
### ✨ Key Features:
|
| 100 |
+
- **AI-Powered Job Matching**
|
| 101 |
+
- **ATS-Friendly CV Builder**
|
| 102 |
+
- **Personalized Interview Prep**
|
| 103 |
+
- **Real-Time Career Insights**
|
| 104 |
+
""")
|
| 105 |
+
st.sidebar.markdown("""
|
| 106 |
+
---
|
| 107 |
+
**Ready to boost your career?**
|
| 108 |
+
Start with our [CV Maker](#) or [Job Finder](#)!
|
| 109 |
+
""")
|
| 110 |
+
|
| 111 |
+
st.sidebar.caption("v2.1.0 | Last updated: April 2024")
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
# Home page
|
| 115 |
+
if page == "Home":
|
| 116 |
+
st.markdown("""
|
| 117 |
+
<style>
|
| 118 |
+
.header {
|
| 119 |
+
background: linear-gradient(135deg, #2c3e50, #4a6491);
|
| 120 |
+
padding: 20px;
|
| 121 |
+
border-radius: 10px;
|
| 122 |
+
color: white;
|
| 123 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
| 124 |
+
margin-bottom: 20px;
|
| 125 |
+
}
|
| 126 |
+
.feature-card {
|
| 127 |
+
background-color: #2d3748;
|
| 128 |
+
border-radius: 10px;
|
| 129 |
+
padding: 20px;
|
| 130 |
+
margin: 10px 0;
|
| 131 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
| 132 |
+
border-left: 4px solid #4a6491;
|
| 133 |
+
transition: transform 0.3s;
|
| 134 |
+
color: #f7fafc;
|
| 135 |
+
}
|
| 136 |
+
.feature-card:hover {
|
| 137 |
+
transform: translateY(-5px);
|
| 138 |
+
box-shadow: 0 6px 12px rgba(0,0,0,0.3);
|
| 139 |
+
background-color: #3c4a5e;
|
| 140 |
+
}
|
| 141 |
+
.stats-container {
|
| 142 |
+
display: flex;
|
| 143 |
+
justify-content: space-around;
|
| 144 |
+
text-align: center;
|
| 145 |
+
margin: 30px 0;
|
| 146 |
+
}
|
| 147 |
+
.stat-item {
|
| 148 |
+
background: #2d3748;
|
| 149 |
+
padding: 15px;
|
| 150 |
+
border-radius: 8px;
|
| 151 |
+
width: 23%;
|
| 152 |
+
color: #f7fafc;
|
| 153 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
| 154 |
+
}
|
| 155 |
+
.testimonial {
|
| 156 |
+
font-style: italic;
|
| 157 |
+
background: #2d3748;
|
| 158 |
+
padding: 20px;
|
| 159 |
+
border-radius: 10px;
|
| 160 |
+
border-left: 4px solid #4a6491;
|
| 161 |
+
margin: 15px 0;
|
| 162 |
+
color: #f7fafc;
|
| 163 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
| 164 |
+
}
|
| 165 |
+
body {
|
| 166 |
+
color: #f7fafc;
|
| 167 |
+
background-color: #1a202c;
|
| 168 |
+
}
|
| 169 |
+
.stApp {
|
| 170 |
+
background-color: #1a202c;
|
| 171 |
+
}
|
| 172 |
+
.caption {
|
| 173 |
+
color: #a0aec0;
|
| 174 |
+
font-size: 1.1em;
|
| 175 |
+
margin-bottom: 20px;
|
| 176 |
+
}
|
| 177 |
+
h1, h2, h3, h4, h5, h6 {
|
| 178 |
+
color: #f7fafc !important;
|
| 179 |
+
}
|
| 180 |
+
p {
|
| 181 |
+
color: #e2e8f0 !important;
|
| 182 |
+
}
|
| 183 |
+
</style>
|
| 184 |
+
""", unsafe_allow_html=True)
|
| 185 |
+
|
| 186 |
+
st.markdown('<div class="header"><h1>🚀 CareerBoost</h1></div>', unsafe_allow_html=True)
|
| 187 |
+
st.markdown('<div class="caption">Your one-stop platform for job hunting, CV creation, and career insights</div>',
|
| 188 |
+
unsafe_allow_html=True)
|
| 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()
|
| 196 |
+
st.markdown(
|
| 197 |
+
f'<div style="text-align: center;"><img src="data:image/x-icon;base64,{base64.b64encode(logo_bytes).decode()}" width="150"></div>',
|
| 198 |
+
unsafe_allow_html=True
|
| 199 |
+
)
|
| 200 |
+
else:
|
| 201 |
+
st.warning("Logo file (logo.png) not found. Please upload it to the project directory.")
|
| 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.
|
| 209 |
+
""")
|
| 210 |
+
|
| 211 |
+
st.markdown("""
|
| 212 |
+
<div class="stats-container">
|
| 213 |
+
<div class="stat-item">
|
| 214 |
+
<h3>250K+</h3>
|
| 215 |
+
<p>Professionals Helped</p>
|
| 216 |
+
</div>
|
| 217 |
+
<div class="stat-item">
|
| 218 |
+
<h3>3x</h3>
|
| 219 |
+
<p>Faster Job Placement</p>
|
| 220 |
+
</div>
|
| 221 |
+
<div class="stat-item">
|
| 222 |
+
<h3>40%</h3>
|
| 223 |
+
<p>More Interviews</p>
|
| 224 |
+
</div>
|
| 225 |
+
<div class="stat-item">
|
| 226 |
+
<h3>95%</h3>
|
| 227 |
+
<p>User Satisfaction</p>
|
| 228 |
+
</div>
|
| 229 |
+
</div>
|
| 230 |
+
""", unsafe_allow_html=True)
|
| 231 |
+
|
| 232 |
+
st.markdown("## ✨ How CareerBoost Helps You Succeed")
|
| 233 |
+
|
| 234 |
+
features = [
|
| 235 |
+
{
|
| 236 |
+
"title": "Smart Job Matching",
|
| 237 |
+
"desc": "Our AI scans thousands of listings to find the perfect matches for your skills and aspirations.",
|
| 238 |
+
"icon": "🔍"
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
"title": "ATS-Optimized CV Builder",
|
| 242 |
+
"desc": "Create resumes that beat applicant tracking systems with our intelligent templates.",
|
| 243 |
+
"icon": "📄"
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"title": "AI Interview Coach",
|
| 247 |
+
"desc": "Practice with realistic mock interviews and get instant feedback on your responses.",
|
| 248 |
+
"icon": "💬"
|
| 249 |
+
},
|
| 250 |
+
{
|
| 251 |
+
"title": "Career Navigator",
|
| 252 |
+
"desc": "Get personalized career path recommendations based on your profile and goals.",
|
| 253 |
+
"icon": "🧭"
|
| 254 |
+
}
|
| 255 |
+
]
|
| 256 |
+
|
| 257 |
+
for feature in features:
|
| 258 |
+
st.markdown(f"""
|
| 259 |
+
<div class="feature-card">
|
| 260 |
+
<h3>{feature['icon']} {feature['title']}</h3>
|
| 261 |
+
<p>{feature['desc']}</p>
|
| 262 |
+
</div>
|
| 263 |
+
""", unsafe_allow_html=True)
|
| 264 |
+
|
| 265 |
+
st.markdown("""
|
| 266 |
+
## Ready to Boost Your Career?
|
| 267 |
+
Get started today by selecting one of the options from the sidebar:
|
| 268 |
+
""")
|
| 269 |
+
|
| 270 |
+
# Job Finding Section
|
| 271 |
+
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",
|
| 282 |
+
"q": f"{query} {job_category}",
|
| 283 |
+
"location": location,
|
| 284 |
+
"experience": experience,
|
| 285 |
+
"api_key": serp_api_key
|
| 286 |
+
}
|
| 287 |
+
try:
|
| 288 |
+
response = requests.get("https://serpapi.com/search", params=params)
|
| 289 |
+
response.raise_for_status()
|
| 290 |
+
data = response.json()
|
| 291 |
+
jobs = data.get("jobs_results", [])[:num_results]
|
| 292 |
+
results = []
|
| 293 |
+
for job in jobs:
|
| 294 |
+
result = {
|
| 295 |
+
"title": job.get('title', 'N/A'),
|
| 296 |
+
"company": job.get('company_name', 'N/A'),
|
| 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', ''), job.get('company_name', 'Unknown')),
|
| 301 |
+
"source": "SerpAPI",
|
| 302 |
+
"category": job_category
|
| 303 |
+
}
|
| 304 |
+
results.append(result)
|
| 305 |
+
return results
|
| 306 |
+
except requests.exceptions.RequestException:
|
| 307 |
+
return [{"error": "Server error, please try again later."}]
|
| 308 |
+
|
| 309 |
+
def duckduckgo_job_search(query: str, job_category: str) -> list:
|
| 310 |
+
try:
|
| 311 |
+
with DDGS() as ddgs:
|
| 312 |
+
results = [r for r in ddgs.text(f"{query} {job_category}", max_results=20)]
|
| 313 |
+
formatted_results = []
|
| 314 |
+
for result in results:
|
| 315 |
+
formatted_results.append({
|
| 316 |
+
"title": result.get('title', 'N/A'),
|
| 317 |
+
"company": 'N/A', # DuckDuckGo doesn't provide company name reliably
|
| 318 |
+
"location": 'N/A', # DuckDuckGo doesn't provide location reliably
|
| 319 |
+
"apply_link": result.get('href', '#'),
|
| 320 |
+
"description": result.get('body', 'No description available')[:200] + "...",
|
| 321 |
+
"posted": None,
|
| 322 |
+
"source": "DuckDuckGo",
|
| 323 |
+
"category": job_category
|
| 324 |
+
})
|
| 325 |
+
return formatted_results
|
| 326 |
+
except Exception as e:
|
| 327 |
+
return [{"error": f"Error in WebSearch: {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 = {
|
| 336 |
+
'x-rapidapi-key': rapidapi_key,
|
| 337 |
+
'x-rapidapi-host': "jsearch.p.rapidapi.com"
|
| 338 |
+
}
|
| 339 |
+
query = urllib.parse.quote(f"{job} jobs in {location}")
|
| 340 |
+
conn.request("GET", f"/search?query={query}&page=1&num_pages={pages}&country={country}&date_posted=all",
|
| 341 |
+
headers=headers)
|
| 342 |
+
res = conn.getresponse()
|
| 343 |
+
data = res.read()
|
| 344 |
+
results = []
|
| 345 |
+
try:
|
| 346 |
+
data_json = json.loads(data.decode("utf-8"))
|
| 347 |
+
except json.JSONDecodeError as e:
|
| 348 |
+
return [{"error": f"Error parsing JSON: {str(e)}"}]
|
| 349 |
+
for job in data_json.get('data', []):
|
| 350 |
+
city = job.get('job_city', '')
|
| 351 |
+
state = job.get('job_state', '')
|
| 352 |
+
location_parts = [part for part in [city, state] if part]
|
| 353 |
+
results.append({
|
| 354 |
+
"title": job.get('job_title', 'N/A'),
|
| 355 |
+
"company": job.get('employer_name', 'N/A'),
|
| 356 |
+
"location": ", ".join(location_parts) if location_parts else "N/A",
|
| 357 |
+
"posted": job.get('job_posted_at_datetime_utc', 'N/A'),
|
| 358 |
+
"description": job.get('job_description', 'N/A')[:200] + "...",
|
| 359 |
+
"apply_link": job.get('job_apply_link', '#'),
|
| 360 |
+
"source": "RapidAPI",
|
| 361 |
+
"category": None
|
| 362 |
+
})
|
| 363 |
+
return results if results else [{"error": "No jobs found."}]
|
| 364 |
+
|
| 365 |
+
job_categories = ['full-time', 'part-time', 'intern', 'contract', 'temporary']
|
| 366 |
+
experience_levels = ['fresher', 'experienced', 'senior']
|
| 367 |
+
|
| 368 |
+
with st.form("job_search_form"):
|
| 369 |
+
col1, col2 = st.columns(2)
|
| 370 |
+
with col1:
|
| 371 |
+
job = st.text_input("Job Title (e.g., Software Engineer)", "Software Engineer")
|
| 372 |
+
location = st.text_input("Location (e.g., Kochi)", "Kochi")
|
| 373 |
+
with col2:
|
| 374 |
+
experience = st.selectbox("Experience Level", experience_levels)
|
| 375 |
+
category = st.selectbox("Job Category", job_categories)
|
| 376 |
+
submit = st.form_submit_button("Search Jobs")
|
| 377 |
+
|
| 378 |
+
if submit:
|
| 379 |
+
with st.spinner("Searching for jobs..."):
|
| 380 |
+
rapid_results = rapid_job_searcher(job, location)
|
| 381 |
+
ddg_results = job_search(job, location, experience, category)
|
| 382 |
+
serp_results = search_serp(job, location, experience, category)
|
| 383 |
+
all_results = {'RapidAPI': rapid_results, 'DuckDuckGo': ddg_results, 'SerpAPI': serp_results}
|
| 384 |
+
|
| 385 |
+
for source, results in all_results.items():
|
| 386 |
+
st.subheader(f"Jobs from {source}")
|
| 387 |
+
if results and not any("error" in r for r in results):
|
| 388 |
+
for result in results:
|
| 389 |
+
with st.container():
|
| 390 |
+
st.markdown(f"""
|
| 391 |
+
<div class="card">
|
| 392 |
+
<h4>{result.get('title', 'N/A')}</h4>
|
| 393 |
+
<p><strong>Company:</strong> {result.get('company', 'N/A')}</p>
|
| 394 |
+
<p><strong>Location:</strong> {result.get('location', 'N/A')}</p>
|
| 395 |
+
<p><strong>Posted:</strong> {result.get('posted', 'N/A')}</p>
|
| 396 |
+
<p><strong>Description:</strong> {result.get('description', 'No description available')}</p>
|
| 397 |
+
<a href="{result.get('apply_link', '#')}" target="_blank">Apply Now</a>
|
| 398 |
+
</div>
|
| 399 |
+
""", unsafe_allow_html=True)
|
| 400 |
+
else:
|
| 401 |
+
st.error(results[0].get("error", "No results found."))
|
| 402 |
+
|
| 403 |
+
# CV Maker Section
|
| 404 |
+
elif page == "CV Maker":
|
| 405 |
+
st.title("📝 CV Maker")
|
| 406 |
+
st.markdown("Create a professional, ATS-friendly CV tailored to your job role.")
|
| 407 |
+
|
| 408 |
+
cv_llm = ChatGoogleGenerativeAI(
|
| 409 |
+
model="gemini-1.5-flash",
|
| 410 |
+
google_api_key=google_api_key,
|
| 411 |
+
temperature=0.1,
|
| 412 |
+
max_output_tokens=2048
|
| 413 |
+
)
|
| 414 |
+
|
| 415 |
+
cv_prompt = PromptTemplate(
|
| 416 |
+
input_variables=["job_field", "experience_level", "years_experience", "key_skills", "education"],
|
| 417 |
+
template="""
|
| 418 |
+
You are an expert CV writer with deep knowledge of ATS-friendly formatting and industry-specific requirements.
|
| 419 |
+
Create a professional, concise, and tailored CV for a {job_field} position based on the following user details:
|
| 420 |
+
|
| 421 |
+
- Experience: {experience_level} ({years_experience} years)
|
| 422 |
+
- Skills: {key_skills}
|
| 423 |
+
- Education: {education}
|
| 424 |
+
|
| 425 |
+
Structure:
|
| 426 |
+
=== Contact ===
|
| 427 |
+
- Name: John Doe
|
| 428 |
+
- Email: john.doe@example.com
|
| 429 |
+
- Phone: +91-9876543210
|
| 430 |
+
- LinkedIn: https://github.com/musabbirkm
|
| 431 |
+
- Portfolio/GitHub: https://huggingface.co/spaces/Musabbirkm
|
| 432 |
+
|
| 433 |
+
== Professional Summary ==
|
| 434 |
+
- 3-4 sentences highlighting expertise in {job_field}, key achievements, and career goals.
|
| 435 |
+
- Use action verbs (e.g., "Led," "Optimized," "Developed") and quantifiable results.
|
| 436 |
+
|
| 437 |
+
== Projects ==
|
| 438 |
+
- Project Name | [GitHub/Live Link]
|
| 439 |
+
• Technologies used: {key_skills}
|
| 440 |
+
• Key outcome: [Measurable result]
|
| 441 |
+
|
| 442 |
+
=== Skills ===
|
| 443 |
+
- 6-10 relevant skills including {key_skills}
|
| 444 |
+
|
| 445 |
+
=== Experience ===
|
| 446 |
+
[2-3 roles based on {experience_level}]
|
| 447 |
+
- Title @ Company (Years)
|
| 448 |
+
• Metric-driven achievements
|
| 449 |
+
• Action-oriented responsibilities
|
| 450 |
+
|
| 451 |
+
=== Education ===
|
| 452 |
+
{Education}
|
| 453 |
+
- Degree Name, University Name | Year
|
| 454 |
+
• Relevant coursework: [Course 1], [Course 2]
|
| 455 |
+
• Thesis/Project: [If applicable]
|
| 456 |
+
|
| 457 |
+
=== Certifications ===
|
| 458 |
+
- [Relevant certifications]
|
| 459 |
+
|
| 460 |
+
Guidelines:
|
| 461 |
+
1. Use action verbs and metrics
|
| 462 |
+
2. Match seniority to {experience_level}
|
| 463 |
+
3. ATS-optimized plain text format
|
| 464 |
+
4. Field-specific keywords
|
| 465 |
+
"""
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
def generate_cv(job_field: str, experience_level: str, years_experience: str, key_skills: str,
|
| 470 |
+
education: str) -> str:
|
| 471 |
+
try:
|
| 472 |
+
prompt = cv_prompt.format(
|
| 473 |
+
job_field=job_field,
|
| 474 |
+
experience_level=experience_level,
|
| 475 |
+
years_experience=years_experience,
|
| 476 |
+
key_skills=key_skills,
|
| 477 |
+
education=education,
|
| 478 |
+
)
|
| 479 |
+
response = cv_llm.invoke(prompt)
|
| 480 |
+
return response.content
|
| 481 |
+
except Exception as e:
|
| 482 |
+
return f"Error generating CV: {str(e)}"
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
def generate_cv_pdf(cv_content: str) -> BytesIO:
|
| 486 |
+
buffer = BytesIO()
|
| 487 |
+
doc = SimpleDocTemplate(buffer, pagesize=letter)
|
| 488 |
+
styles = getSampleStyleSheet()
|
| 489 |
+
heading_style = ParagraphStyle(name='Heading', fontSize=14, leading=16, spaceAfter=12,
|
| 490 |
+
fontName='Helvetica-Bold')
|
| 491 |
+
body_style = ParagraphStyle(name='Body', fontSize=11, leading=14, spaceAfter=8, fontName='Helvetica')
|
| 492 |
+
bullet_style = ParagraphStyle(name='Bullet', fontSize=11, leading=14, leftIndent=20, bulletIndent=10,
|
| 493 |
+
spaceAfter=8, fontName='Helvetica')
|
| 494 |
+
|
| 495 |
+
flowables = []
|
| 496 |
+
sections = cv_content.split('===')
|
| 497 |
+
for i in range(0, len(sections), 2):
|
| 498 |
+
if i + 1 >= len(sections):
|
| 499 |
+
break
|
| 500 |
+
title = sections[i].strip()
|
| 501 |
+
content = sections[i + 1].strip().split('\n')
|
| 502 |
+
flowables.append(Paragraph(title, heading_style))
|
| 503 |
+
flowables.append(Spacer(1, 6))
|
| 504 |
+
if title == "Skills" or title == "Certifications":
|
| 505 |
+
bullet_items = [ListItem(Paragraph(line.strip(), bullet_style)) for line in content if line.strip()]
|
| 506 |
+
flowables.append(ListFlowable(bullet_items, bulletType='bullet', start='circle'))
|
| 507 |
+
else:
|
| 508 |
+
for line in content:
|
| 509 |
+
if line.strip():
|
| 510 |
+
flowables.append(Paragraph(line.strip(), body_style))
|
| 511 |
+
flowables.append(Spacer(1, 12))
|
| 512 |
+
doc.build(flowables)
|
| 513 |
+
buffer.seek(0)
|
| 514 |
+
return buffer
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
with st.form("cv_form"):
|
| 518 |
+
col1, col2 = st.columns(2)
|
| 519 |
+
with col1:
|
| 520 |
+
job_field = st.text_input("Job Field (e.g., Software Engineer)", "Software Engineer")
|
| 521 |
+
experience_level = st.selectbox("Experience Level", ["Fresher", "Experienced", "Senior"])
|
| 522 |
+
years_experience = st.text_input("Years of Experience (e.g., 2)", "2")
|
| 523 |
+
with col2:
|
| 524 |
+
key_skills = st.text_area("Key Skills (comma-separated, e.g., Python, SQL)", "Python, SQL, JavaScript")
|
| 525 |
+
education = st.text_area("Education (e.g., B.Tech in CS, XYZ University, 2020)",
|
| 526 |
+
"B.Tech in CS, XYZ University, 2020")
|
| 527 |
+
submit = st.form_submit_button("Generate CV")
|
| 528 |
+
|
| 529 |
+
if submit:
|
| 530 |
+
with st.spinner("Generating CV..."):
|
| 531 |
+
cv_content = generate_cv(job_field, experience_level, years_experience, key_skills, education)
|
| 532 |
+
st.session_state['last_cv'] = cv_content
|
| 533 |
+
if not cv_content.startswith("Error"):
|
| 534 |
+
st.success("CV generated successfully!")
|
| 535 |
+
st.markdown("### Generated CV")
|
| 536 |
+
st.text_area("CV Content", cv_content, height=400)
|
| 537 |
+
st.download_button(
|
| 538 |
+
label="Download CV (Text)",
|
| 539 |
+
data=cv_content,
|
| 540 |
+
file_name="John_Doe_CV.txt",
|
| 541 |
+
mime="text/plain"
|
| 542 |
+
)
|
| 543 |
+
pdf_buffer = generate_cv_pdf(cv_content)
|
| 544 |
+
st.download_button(
|
| 545 |
+
label="Download CV (PDF)",
|
| 546 |
+
data=pdf_buffer,
|
| 547 |
+
file_name="John_Doe_CV.pdf",
|
| 548 |
+
mime="application/pdf"
|
| 549 |
+
)
|
| 550 |
+
else:
|
| 551 |
+
st.error(cv_content)
|
| 552 |
+
|
| 553 |
+
# Interview Preparation Section
|
| 554 |
+
elif page == "Interview Preparation":
|
| 555 |
+
st.title("🎤 Interview Preparation")
|
| 556 |
+
st.markdown("Prepare for your next interview with tailored questions and answers.")
|
| 557 |
+
|
| 558 |
+
llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash", google_api_key=google_api_key)
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
def duckduckgo_search(query: str) -> str:
|
| 562 |
+
try:
|
| 563 |
+
with DDGS() as ddgs:
|
| 564 |
+
results = [r for r in ddgs.text(query, max_results=5)]
|
| 565 |
+
return json.dumps(results, indent=2)
|
| 566 |
+
except Exception as e:
|
| 567 |
+
return f"Error in DuckDuckGo search: {str(e)}"
|
| 568 |
+
|
| 569 |
+
|
| 570 |
+
def interview_preparer(job_field: str) -> str:
|
| 571 |
+
try:
|
| 572 |
+
if not job_field or not isinstance(job_field, str):
|
| 573 |
+
return "Error: Invalid job field provided."
|
| 574 |
+
search_queries = [
|
| 575 |
+
f"{job_field} interview questions 2022-2025",
|
| 576 |
+
f"site:reddit.com {job_field} interview questions",
|
| 577 |
+
f"site:quora.com {job_field} interview questions"
|
| 578 |
+
]
|
| 579 |
+
search_results = []
|
| 580 |
+
for query in search_queries:
|
| 581 |
+
try:
|
| 582 |
+
result = duckduckgo_search(query)
|
| 583 |
+
if not result.startswith("Error"):
|
| 584 |
+
search_results.append(json.loads(result))
|
| 585 |
+
except Exception as e:
|
| 586 |
+
search_results.append({"source": query, "error": str(e)})
|
| 587 |
+
combined_results = json.dumps(search_results, indent=2)
|
| 588 |
+
interview_prompt = PromptTemplate(
|
| 589 |
+
input_variables=["job_field", "search_results"],
|
| 590 |
+
template="""
|
| 591 |
+
You are an interview preparation expert. Generate exactly 10 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.
|
| 592 |
+
Requirements:
|
| 593 |
+
- Include 4 technical questions, 3 behavioral questions, and 3 situational questions.
|
| 594 |
+
- Incorporate trends and frequently asked questions from 2022-2025.
|
| 595 |
+
- Use the search results for context to inform answers, but do not include raw search data or URLs in the output: {search_results}.
|
| 596 |
+
- Format as plain text with question numbers, type (Technical/Behavioral/Situational), questions, and answers.
|
| 597 |
+
Example:
|
| 598 |
+
1. Technical: [Question]
|
| 599 |
+
Answer: [Detailed answer]
|
| 600 |
+
"""
|
| 601 |
+
)
|
| 602 |
+
response = llm.invoke(interview_prompt.format(job_field=job_field, search_results=combined_results))
|
| 603 |
+
return response.content
|
| 604 |
+
except Exception as e:
|
| 605 |
+
return f"Error generating interview questions: {str(e)}"
|
| 606 |
+
|
| 607 |
+
|
| 608 |
+
with st.form("interview_form"):
|
| 609 |
+
job_field = st.text_input("Job Field (e.g., Software Engineer)", "Software Engineer")
|
| 610 |
+
submit = st.form_submit_button("Generate Questions")
|
| 611 |
+
|
| 612 |
+
if submit:
|
| 613 |
+
with st.spinner("Generating interview questions..."):
|
| 614 |
+
questions = interview_preparer(job_field)
|
| 615 |
+
st.session_state['last_questions'] = questions
|
| 616 |
+
if not questions.startswith("Error"):
|
| 617 |
+
st.success("Interview questions generated successfully!")
|
| 618 |
+
st.markdown("### Interview Questions")
|
| 619 |
+
st.text_area("Questions and Answers", questions, height=400)
|
| 620 |
+
st.download_button(
|
| 621 |
+
label="Download Questions",
|
| 622 |
+
data=questions,
|
| 623 |
+
file_name="Interview_Questions.txt",
|
| 624 |
+
mime="text/plain"
|
| 625 |
+
)
|
| 626 |
+
else:
|
| 627 |
+
st.error(questions)
|
| 628 |
+
|
| 629 |
+
# Career Insights Section
|
| 630 |
+
elif page == "Career Insights":
|
| 631 |
+
st.title("📰 Career Insights")
|
| 632 |
+
st.markdown("Stay updated with the latest job market trends and company hiring news.")
|
| 633 |
+
|
| 634 |
+
newsapi = NewsApiClient(api_key=newsapi_key)
|
| 635 |
+
|
| 636 |
+
def get_gnews_articles():
|
| 637 |
+
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}"
|
| 638 |
+
try:
|
| 639 |
+
with urllib.request.urlopen(url) as response:
|
| 640 |
+
data = json.loads(response.read().decode("utf-8"))
|
| 641 |
+
if "articles" in data:
|
| 642 |
+
return [
|
| 643 |
+
{
|
| 644 |
+
"title": article["title"],
|
| 645 |
+
"description": article.get("description", "No description available"),
|
| 646 |
+
"source": article["source"]["name"],
|
| 647 |
+
"published_at": article["publishedAt"],
|
| 648 |
+
"url": article["url"]
|
| 649 |
+
}
|
| 650 |
+
for article in data["articles"]
|
| 651 |
+
]
|
| 652 |
+
return []
|
| 653 |
+
except urllib.error.URLError as e:
|
| 654 |
+
st.error(f"Failed to fetch GNews: {e.reason}")
|
| 655 |
+
return []
|
| 656 |
+
|
| 657 |
+
def get_indian_job_news():
|
| 658 |
+
try:
|
| 659 |
+
yesterday = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d') # Extended to 7 days
|
| 660 |
+
if not isinstance(yesterday, str):
|
| 661 |
+
raise ValueError("Invalid date format for from_param")
|
| 662 |
+
job_news = newsapi.get_everything(
|
| 663 |
+
q='(jobs OR hiring OR recruitment OR employment OR "job market") AND (India OR Indian)',
|
| 664 |
+
language='en',
|
| 665 |
+
sort_by='publishedAt',
|
| 666 |
+
from_param=yesterday,
|
| 667 |
+
page_size=10
|
| 668 |
+
)
|
| 669 |
+
return [
|
| 670 |
+
{
|
| 671 |
+
"title": article["title"],
|
| 672 |
+
"description": article.get("description", "No description available"),
|
| 673 |
+
"source": article["source"]["name"],
|
| 674 |
+
"published_at": article["publishedAt"],
|
| 675 |
+
"url": article["url"]
|
| 676 |
+
}
|
| 677 |
+
for article in job_news.get("articles", [])
|
| 678 |
+
]
|
| 679 |
+
except newsapi.newsapi_exception.NewsApiException as e:
|
| 680 |
+
st.error(f"NewsAPI error fetching job news: {e.get_message()}")
|
| 681 |
+
return []
|
| 682 |
+
except Exception as e:
|
| 683 |
+
st.error(f"Unexpected error fetching job news: {str(e)}")
|
| 684 |
+
return []
|
| 685 |
+
|
| 686 |
+
def get_indian_tech_news():
|
| 687 |
+
try:
|
| 688 |
+
domains = 'economictimes.indiatimes.com,livemint.com,indiatoday.in'
|
| 689 |
+
tech_news = newsapi.get_everything(
|
| 690 |
+
q='(tech OR IT OR technology OR startup) AND (India OR Indian)',
|
| 691 |
+
domains=domains,
|
| 692 |
+
language='en',
|
| 693 |
+
sort_by='relevancy',
|
| 694 |
+
page_size=10
|
| 695 |
+
)
|
| 696 |
+
return [
|
| 697 |
+
{
|
| 698 |
+
"title": article["title"],
|
| 699 |
+
"description": article.get("description", "No description available"),
|
| 700 |
+
"source": article["source"]["name"],
|
| 701 |
+
"published_at": article.get("publishedAt", "N/A"),
|
| 702 |
+
"url": article["url"]
|
| 703 |
+
}
|
| 704 |
+
for article in tech_news.get("articles", [])
|
| 705 |
+
]
|
| 706 |
+
except newsapi.newsapi_exception.NewsApiException as e:
|
| 707 |
+
st.error(f"NewsAPI error fetching tech news: {e.get_message()}")
|
| 708 |
+
return []
|
| 709 |
+
except Exception as e:
|
| 710 |
+
st.error(f"Unexpected error fetching tech news: {str(e)}")
|
| 711 |
+
return []
|
| 712 |
+
|
| 713 |
+
def get_company_hiring_news():
|
| 714 |
+
companies = ['TCS', 'Infosys', 'Wipro', 'HCL', 'Tech Mahindra']
|
| 715 |
+
company_news = []
|
| 716 |
+
try:
|
| 717 |
+
for company in companies:
|
| 718 |
+
news = newsapi.get_everything(
|
| 719 |
+
q=f'{company} AND (hiring OR recruitment OR jobs)',
|
| 720 |
+
language='en',
|
| 721 |
+
page_size=3
|
| 722 |
+
)
|
| 723 |
+
company_articles = [
|
| 724 |
+
{
|
| 725 |
+
"title": article["title"],
|
| 726 |
+
"description": article.get("description", "No description available"),
|
| 727 |
+
"source": article["source"]["name"],
|
| 728 |
+
"published_at": article.get("publishedAt", "N/A"),
|
| 729 |
+
"url": article["url"],
|
| 730 |
+
"company": company
|
| 731 |
+
}
|
| 732 |
+
for article in news.get("articles", [])
|
| 733 |
+
]
|
| 734 |
+
company_news.extend(company_articles)
|
| 735 |
+
return company_news
|
| 736 |
+
except newsapi.newsapi_exception.NewsApiException as e:
|
| 737 |
+
st.error(f"NewsAPI error fetching company news: {e.get_message()}")
|
| 738 |
+
return []
|
| 739 |
+
except Exception as e:
|
| 740 |
+
st.error(f"Unexpected error fetching company news: {str(e)}")
|
| 741 |
+
return []
|
| 742 |
+
|
| 743 |
+
with st.spinner("Fetching news..."):
|
| 744 |
+
gnews_articles = get_gnews_articles()
|
| 745 |
+
job_news = get_indian_job_news()
|
| 746 |
+
tech_news = get_indian_tech_news()
|
| 747 |
+
company_news = get_company_hiring_news()
|
| 748 |
+
|
| 749 |
+
# Job Market News Section (Combining GNews and NewsAPI)
|
| 750 |
+
st.markdown('<h2 class="section-header">Job Market News</h2>', unsafe_allow_html=True)
|
| 751 |
+
if gnews_articles or job_news:
|
| 752 |
+
st.markdown('<div class="grid-container">', unsafe_allow_html=True)
|
| 753 |
+
for article in gnews_articles:
|
| 754 |
+
st.markdown(f"""
|
| 755 |
+
<div class="article-card">
|
| 756 |
+
<h3>{article['title']}</h3>
|
| 757 |
+
<p class="meta">Source: {article['source']} | Published: {article['published_at']}</p>
|
| 758 |
+
<p class="description">{article['description']}</p>
|
| 759 |
+
<a href="{article['url']}" target="_blank">Read more</a>
|
| 760 |
+
</div>
|
| 761 |
+
""", unsafe_allow_html=True)
|
| 762 |
+
for article in job_news:
|
| 763 |
+
st.markdown(f"""
|
| 764 |
+
<div class="article-card">
|
| 765 |
+
<h3>{article['title']}</h3>
|
| 766 |
+
<p class="meta">Source: {article['source']} | Published: {article['published_at']}</p>
|
| 767 |
+
<p class="description">{article['description']}</p>
|
| 768 |
+
<a href="{article['url']}" target="_blank">Read more</a>
|
| 769 |
+
</div>
|
| 770 |
+
""", unsafe_allow_html=True)
|
| 771 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 772 |
+
else:
|
| 773 |
+
st.markdown('<p class="no-news">No job market news available at the moment. Please try again later.</p>', unsafe_allow_html=True)
|
| 774 |
+
|
| 775 |
+
# Company Hiring News Section
|
| 776 |
+
st.markdown('<h2 class="section-header">Company Hiring News</h2>', unsafe_allow_html=True)
|
| 777 |
+
if company_news:
|
| 778 |
+
st.markdown('<div class="grid-container">', unsafe_allow_html=True)
|
| 779 |
+
for article in company_news:
|
| 780 |
+
st.markdown(f"""
|
| 781 |
+
<div class="article-card">
|
| 782 |
+
<h3>{article['title']}</h3>
|
| 783 |
+
<p class="meta">Company: {article['company']} | Source: {article['source']} | Published: {article['published_at']}</p>
|
| 784 |
+
<p class="description">{article['description']}</p>
|
| 785 |
+
<a href="{article['url']}" target="_blank">Read more</a>
|
| 786 |
+
</div>
|
| 787 |
+
""", unsafe_allow_html=True)
|
| 788 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 789 |
+
else:
|
| 790 |
+
st.markdown('<p class="no-news">No company hiring news available at the moment. Please try again later.</p>', unsafe_allow_html=True)
|
| 791 |
+
|
| 792 |
+
# Tech Industry News Section
|
| 793 |
+
st.markdown('<h2 class="section-header">Tech Industry News</h2>', unsafe_allow_html=True)
|
| 794 |
+
if tech_news:
|
| 795 |
+
st.markdown('<div class="grid-container">', unsafe_allow_html=True)
|
| 796 |
+
for article in tech_news:
|
| 797 |
+
st.markdown(f"""
|
| 798 |
+
<div class="article-card">
|
| 799 |
+
<h3>{article['title']}</h3>
|
| 800 |
+
<p class="meta">Source: {article['source']} | Published: {article['published_at']}</p>
|
| 801 |
+
<p class="description">{article['description']}</p>
|
| 802 |
+
<a href="{article['url']}" target="_blank">Read more</a>
|
| 803 |
+
</div>
|
| 804 |
+
""", unsafe_allow_html=True)
|
| 805 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 806 |
+
else:
|
| 807 |
+
st.markdown('<p class="no-news">No tech industry news available at the moment. Please try again later.</p>', unsafe_allow_html=True)
|
| 808 |
+
|
| 809 |
+
|
| 810 |
+
# About Section
|
| 811 |
+
elif page == "About":
|
| 812 |
+
st.title("ℹ️ About CareerBoost")
|
| 813 |
+
logo_path = "logo.ico"
|
| 814 |
+
if os.path.exists(logo_path):
|
| 815 |
+
with open(logo_path, "rb") as f:
|
| 816 |
+
logo_bytes = f.read()
|
| 817 |
+
st.markdown(
|
| 818 |
+
f'<img src="data:image/x-icon;base64,{base64.b64encode(logo_bytes).decode()}" width="150">',
|
| 819 |
+
unsafe_allow_html=True
|
| 820 |
+
)
|
| 821 |
+
else:
|
| 822 |
+
st.warning("Logo file (logo.png) not found. Please upload it to the project directory.")
|
| 823 |
+
st.markdown("""
|
| 824 |
+
**CareerBoost** is here to make your job search easier, smarter, and more successful!
|
| 825 |
+
Powered by AI and designed with you in mind, our platform helps you find jobs, build standout CVs, ace interviews,
|
| 826 |
+
and stay updated on career trends—all in one place.
|
| 827 |
+
""")
|
| 828 |
+
|
| 829 |
+
# Features Section
|
| 830 |
+
st.markdown('<h2 class="section-header">What We Offer</h2>', unsafe_allow_html=True)
|
| 831 |
+
col1, col2 = st.columns(2)
|
| 832 |
+
with col1:
|
| 833 |
+
st.markdown("""
|
| 834 |
+
- **🔍 Job Finding**
|
| 835 |
+
Discover job opportunities across top platforms with personalized filters.
|
| 836 |
+
- **📝 CV Maker**
|
| 837 |
+
Create professional, ATS-friendly CVs tailored to your dream role.
|
| 838 |
+
""")
|
| 839 |
+
with col2:
|
| 840 |
+
st.markdown("""
|
| 841 |
+
- **🎤 Interview Prep**
|
| 842 |
+
Practice with customized questions and expert answers to boost your confidence.
|
| 843 |
+
- **📰 Career Insights**
|
| 844 |
+
Stay ahead with the latest job market trends and industry news.
|
| 845 |
+
""")
|
| 846 |
+
|
| 847 |
+
# Mission Section
|
| 848 |
+
st.markdown('<h2 class="section-header">Our Mission</h2>', unsafe_allow_html=True)
|
| 849 |
+
st.markdown("""
|
| 850 |
+
At CareerBoost, we believe everyone deserves a shot at their dream career.
|
| 851 |
+
Whether you're just starting out, switching paths, or aiming for the top,
|
| 852 |
+
we’re here to provide the tools and support you need to succeed.
|
| 853 |
+
Our goal is to make career growth accessible, inclusive, and stress-free.
|
| 854 |
+
""")
|
| 855 |
+
|
| 856 |
+
# Contact Section
|
| 857 |
+
st.markdown('<h2 class="section-header">Get In Touch</h2>', unsafe_allow_html=True)
|
| 858 |
+
st.markdown("""
|
| 859 |
+
We’d love to hear from you! Whether you have questions, feedback, or just want to chat,
|
| 860 |
+
reach out to us anytime.
|
| 861 |
+
""")
|
| 862 |
+
col1, col2 = st.columns(2)
|
| 863 |
+
with col1:
|
| 864 |
+
st.markdown("""
|
| 865 |
+
- 📧 **Email**: [musabbirmushu@gmail.com](mailto:musabbirmushu@gmail.com)
|
| 866 |
+
- 💼 **LinkedIn**: [CareerBoost LinkedIn](https://www.linkedin.com/in/muhammed-musabbir-km-0302b8212?utm_source=share&utm_campaign=share_via&utm_content=profile&utm_medium=android_appt)
|
| 867 |
+
""")
|
| 868 |
+
with col2:
|
| 869 |
+
st.markdown("""
|
| 870 |
+
- 🌐 **Website**: [www.careerboost.ai](https://omnicipher.onrender.com)
|
| 871 |
+
- ⌨️ **GitHub**: [CareerBoost GitHub](https://github.com/musabbirkm)
|
| 872 |
+
""")
|
| 873 |
+
|
| 874 |
+
# Call-to-Action
|
| 875 |
+
st.markdown('<div style="text-align: center; margin-top: 20px;">', unsafe_allow_html=True)
|
| 876 |
+
if st.button("Start Your Career Journey Now!"):
|
| 877 |
+
st.session_state["page"] = "Home"
|
| 878 |
+
st.experimental_rerun()
|
| 879 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
logo.ico
ADDED
|
|
Git LFS Details
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.29.0
|
| 2 |
+
langchain==0.2.16
|
| 3 |
+
langchain-google-genai==1.0.8
|
| 4 |
+
langchain-community==0.2.16
|
| 5 |
+
duckduckgo-search
|
| 6 |
+
requests==2.28.2
|
| 7 |
+
reportlab==4.0.9
|
| 8 |
+
newsapi-python==0.2.7
|