AI_Job_Assist / app.py
Varunkkanjarla's picture
app.py
aa0577f verified
import streamlit as st
from streamlit_chat import message
from langchain_google_genai import ChatGoogleGenerativeAI
import os
import PyPDF2
import docx
import requests
from bs4 import BeautifulSoup
import re
# Set up Gemini AI
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "AIzaSyDs-vtZBkyLmEUH5NgkfUNkMJ8kxg_pR3Y")
chat_model = ChatGoogleGenerativeAI(model="gemini-1.5-pro", api_version="v1",
google_api_key=GEMINI_API_KEY)
# Set page config
st.set_page_config(page_title="AI-Driven Job Assistant", layout="wide")
# Initialize session state for messages if not set
if "messages" not in st.session_state:
st.session_state.messages = []
if "resume_text" not in st.session_state:
st.session_state.resume_text = ""
# Extract text from resume
def extract_text_from_resume(resume_file):
text = ""
if resume_file.name.endswith(".pdf"):
pdf_reader = PyPDF2.PdfReader(resume_file)
for page in pdf_reader.pages:
extracted_text = page.extract_text()
if extracted_text:
text += extracted_text + "\n"
elif resume_file.name.endswith(".docx"):
doc = docx.Document(resume_file)
for para in doc.paragraphs:
text += para.text + "\n"
return text.strip()
# Sidebar resume upload
with st.sidebar:
st.header("Upload Your Resume")
resume = st.file_uploader("Upload PDF or DOCX", type=["pdf", "docx"])
if resume:
st.success("Resume uploaded successfully!")
if st.button("Proceed"):
with st.spinner("Analyzing your resume..."):
resume_text = extract_text_from_resume(resume)
if resume_text:
st.session_state.resume_text = resume_text
ats_prompt = f"Analyze this resume and provide an ATS score (out of 100) along with improvement suggestions keep the response in short:\n\n{resume_text}"
ats_response = chat_model.predict(ats_prompt)
st.session_state.messages.append({"text": ats_response, "is_user": False})
st.session_state["resume_analyzed"] = True # Mark resume as analyzed
st.rerun()
else:
st.error("Could not extract text from the uploaded resume.")
# Main chat interface
st.title("💬 AI-Driven Job Assist")
st.subheader("ATS insights & Chatbot Assist")
# Display chat messages
for msg in st.session_state.messages:
message(msg["text"], is_user=msg["is_user"])
# Ask for aspired job role after ATS score
if "resume_analyzed" in st.session_state and "aspired job role" not in st.session_state:
ai_response = "What is your aspired job role?"
st.session_state.messages.append({"text": ai_response, "is_user": False})
del st.session_state["resume_analyzed"] # Remove flag after asking
st.rerun()
# Input area
if "user_message" not in st.session_state:
st.session_state.user_message = ""
user_input = st.text_input("Type your message here...", key="user_message")
if st.button("Send") and user_input:
st.session_state.messages.append({"text": user_input, "is_user": True})
# AI response logic
if "aspired job role" not in st.session_state:
st.session_state["aspired job role"] = user_input
job_role = st.session_state["aspired job role"]
skills_prompt = f"For the job role of {job_role}, suggest the essential skills and any missing skills based on the user's resume:\n\nResume:\n{st.session_state.resume_text}\n\nAlso, provide relevant learning resources for upskilling with valid links of courses.keep the response in short"
ai_response = chat_model.predict(skills_prompt)
st.session_state.messages.append({"text": ai_response, "is_user": False})
st.session_state.pop("user_message", None)
st.rerun()
# Fetch jobs from LinkedIn
def fetch_jobs_from_linkedin(keyword, location, max_results=5):
search_url = f"https://www.linkedin.com/jobs/search?keywords={keyword}&location={location}"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
response = requests.get(search_url, headers=headers)
if response.status_code != 200:
st.error("Failed to fetch jobs from LinkedIn. Try again later.")
return []
soup = BeautifulSoup(response.text, "html.parser")
job_listings = []
for job_card in soup.find_all("div", class_="base-card")[:max_results]:
title_tag = job_card.find("h3", class_="base-search-card__title")
company_tag = job_card.find("h4", class_="base-search-card__subtitle")
location_tag = job_card.find("span", class_="job-search-card__location")
link_tag = job_card.find("a", class_="base-card__full-link")
if title_tag and company_tag and location_tag and link_tag:
job_listings.append({
"title": title_tag.text.strip(),
"company": company_tag.text.strip(),
"location": location_tag.text.strip(),
"link": link_tag["href"].strip(),
"recruiter_email": fetch_recruiter_email(job_card) # Placeholder function
})
return job_listings
# Placeholder function for fetching recruiter email
def fetch_recruiter_email(job_card):
# Replace this with actual logic to find recruiter emails
email_tag = job_card.find("span", class_="recruiter-email") # Example selector
return email_tag.text.strip() if email_tag else None
# Function to generate cold email and cover letter
def generate_email_and_cover_letter(job_title, company, recruiter_email):
cold_email = f"""
Subject: Application for {job_title} Position at {company}
Dear Hiring Manager,
I am excited to apply for the {job_title} position at {company}. With my skills and experience, I believe I am a great fit for this role. I have attached my resume for your reference and would love the opportunity to discuss further.
Looking forward to your response.
Best Regards,
[Your Name]
"""
cover_letter = f"""
Dear Hiring Manager,
I am writing to express my interest in the {job_title} position at {company}. I have a strong background in [mention relevant skills] and believe my expertise aligns with the job requirements.
I am eager to bring my skills to your esteemed company and contribute effectively. Please find my resume attached for review.
Thank you for your time and consideration. I look forward to the opportunity to speak with you.
Sincerely,
[Your Name]
"""
return cold_email, cover_letter
# Function to generate LinkedIn message & connection request note
def generate_linkedin_outreach(job_title, company):
linkedin_message = f"""
Hi [Recruiter Name],
I hope you're doing well. I came across the {job_title} opening at {company} and I am very interested.
I would love to connect and learn more about this opportunity.
Looking forward to your response!
Best,
[Your Name]
"""
connection_request_note = f"""
Hi [Recruiter Name], I’m interested in the {job_title} role at {company} and would love to connect.
"""
return linkedin_message, connection_request_note
# Streamlit UI
st.title("📌 Job Listings from LinkedIn")
keyword = st.text_input("Job Title (e.g., Data Scientist)", value=st.session_state.get("aspired job role", ""))
location = st.text_input("Location (e.g., New York)")
if st.button("Fetch Jobs"):
jobs = fetch_jobs_from_linkedin(keyword, location)
if jobs:
for job in jobs[:3]:
st.markdown(f"**{job['title']}** at {job['company']} ({job['location']})")
st.markdown(f"[Apply Now]({job['link']})")
# Call Gemini to get match score
match_prompt = f"""Based on the following job role and resume, provide a match score out of 100 indicating how well the resume fits the job. Also give a 1-line reason for the score.
Job Role: {job['title']} at {job['company']} in {job['location']}
Resume: {st.session_state.resume_text}
Keep the response short and structured like this:
Score: 85
Reason: Strong experience in React and REST APIs aligns well.
"""
match_response = chat_model.predict(match_prompt)
# Display the match score. Extract score and reason from the response
match = re.search(r"Score:\s*(\d+)\s*\nReason:\s*(.*)", match_response)
if match:
score = int(match.group(1))
reason = match.group(2)
# Use color coding for different match levels
if score >= 80:
color = "green"
elif score >= 50:
color = "orange"
else:
color = "red"
# Display match score with highlight
st.markdown(f"🔍 **Resume Match Score:** <span style='color:{color}; font-size:22px; font-weight:bold'>{score}/100</span>", unsafe_allow_html=True)
# Display reason separately
st.markdown(f"📌 **Reason:** {reason}")
else:
st.markdown("⚠️ Could not extract match score. Please check the response format.")
# Show recruiter email if available
if job['recruiter_email']:
st.markdown(f"**Recruiter Email:** {job['recruiter_email']}")
cold_email, cover_letter = generate_email_and_cover_letter(job['title'], job['company'], job['recruiter_email'])
with st.expander("📧 Suggested Cold Email"):
st.code(cold_email)
with st.expander("📜 Suggested Cover Letter"):
st.code(cover_letter)
else:
st.markdown(f"**Recruiters Email not available**")
# Provide LinkedIn message & connection note
linkedin_msg, connection_note = generate_linkedin_outreach(job['title'], job['company'])
with st.expander("💬 Suggested LinkedIn Message"):
st.code(linkedin_msg)
with st.expander("🔗 Connection Request Note"):
st.code(connection_note)
else:
st.write("No jobs found. Try a different search.")