Varunkkanjarla commited on
Commit
cfc159e
·
verified ·
1 Parent(s): 09509be

Upload 3 files

Browse files
Files changed (3) hide show
  1. .streamlit/config.toml +2 -0
  2. app.py +250 -0
  3. requirements.txt +8 -0
.streamlit/config.toml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [theme]
2
+ base="light"
app.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_chat import message
3
+ from langchain_google_genai import ChatGoogleGenerativeAI
4
+ import os
5
+ import PyPDF2
6
+ import docx
7
+ import requests
8
+ from bs4 import BeautifulSoup
9
+ import re
10
+
11
+ # Set up Gemini AI
12
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "AIzaSyBZ92WbBwz0pWqHPjjT2lqGrwlAfM91Rds")
13
+ chat_model = ChatGoogleGenerativeAI(model="gemini-1.5-pro", api_version="v1",
14
+ google_api_key=GEMINI_API_KEY)
15
+
16
+ # Set page config
17
+ st.set_page_config(page_title="AI-Driven Job Assistant", layout="wide")
18
+
19
+ # Initialize session state for messages if not set
20
+ if "messages" not in st.session_state:
21
+ st.session_state.messages = []
22
+ if "resume_text" not in st.session_state:
23
+ st.session_state.resume_text = ""
24
+
25
+ # Extract text from resume
26
+ def extract_text_from_resume(resume_file):
27
+ text = ""
28
+ if resume_file.name.endswith(".pdf"):
29
+ pdf_reader = PyPDF2.PdfReader(resume_file)
30
+ for page in pdf_reader.pages:
31
+ extracted_text = page.extract_text()
32
+ if extracted_text:
33
+ text += extracted_text + "\n"
34
+ elif resume_file.name.endswith(".docx"):
35
+ doc = docx.Document(resume_file)
36
+ for para in doc.paragraphs:
37
+ text += para.text + "\n"
38
+ return text.strip()
39
+
40
+ # Sidebar resume upload
41
+ with st.sidebar:
42
+ st.header("Upload Your Resume")
43
+ resume = st.file_uploader("Upload PDF or DOCX", type=["pdf", "docx"])
44
+ if resume:
45
+ st.success("Resume uploaded successfully!")
46
+ if st.button("Proceed"):
47
+ with st.spinner("Analyzing your resume..."):
48
+ resume_text = extract_text_from_resume(resume)
49
+ if resume_text:
50
+ st.session_state.resume_text = resume_text
51
+ 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}"
52
+ ats_response = chat_model.predict(ats_prompt)
53
+ st.session_state.messages.append({"text": ats_response, "is_user": False})
54
+ st.session_state["resume_analyzed"] = True # Mark resume as analyzed
55
+ st.rerun()
56
+ else:
57
+ st.error("Could not extract text from the uploaded resume.")
58
+
59
+ # Main chat interface
60
+ st.title("💬 AI-Driven Job Assist")
61
+ st.subheader("ATS insights & Chatbot Assist")
62
+
63
+ # Display chat messages
64
+ for msg in st.session_state.messages:
65
+ message(msg["text"], is_user=msg["is_user"])
66
+
67
+ # Ask for aspired job role after ATS score
68
+ if "resume_analyzed" in st.session_state and "aspired job role" not in st.session_state:
69
+ ai_response = "What is your aspired job role?"
70
+ st.session_state.messages.append({"text": ai_response, "is_user": False})
71
+ del st.session_state["resume_analyzed"] # Remove flag after asking
72
+ st.rerun()
73
+
74
+ # Input area
75
+ if "user_message" not in st.session_state:
76
+ st.session_state.user_message = ""
77
+
78
+
79
+ user_input = st.text_input("Type your message here...", key="user_message")
80
+
81
+ if st.button("Send") and user_input:
82
+ st.session_state.messages.append({"text": user_input, "is_user": True})
83
+
84
+ # AI response logic
85
+ if "aspired job role" not in st.session_state:
86
+ st.session_state["aspired job role"] = user_input
87
+ job_role = st.session_state["aspired job role"]
88
+ 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"
89
+ ai_response = chat_model.predict(skills_prompt)
90
+
91
+ st.session_state.messages.append({"text": ai_response, "is_user": False})
92
+ st.session_state.pop("user_message", None)
93
+ st.rerun()
94
+
95
+ # Fetch jobs from LinkedIn
96
+ def fetch_jobs_from_linkedin(keyword, location, max_results=5):
97
+ search_url = f"https://www.linkedin.com/jobs/search?keywords={keyword}&location={location}"
98
+ headers = {
99
+ "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"
100
+ }
101
+
102
+ response = requests.get(search_url, headers=headers)
103
+ if response.status_code != 200:
104
+ st.error("Failed to fetch jobs from LinkedIn. Try again later.")
105
+ return []
106
+
107
+ soup = BeautifulSoup(response.text, "html.parser")
108
+ job_listings = []
109
+
110
+ for job_card in soup.find_all("div", class_="base-card")[:max_results]:
111
+ title_tag = job_card.find("h3", class_="base-search-card__title")
112
+ company_tag = job_card.find("h4", class_="base-search-card__subtitle")
113
+ location_tag = job_card.find("span", class_="job-search-card__location")
114
+ link_tag = job_card.find("a", class_="base-card__full-link")
115
+
116
+ if title_tag and company_tag and location_tag and link_tag:
117
+ job_listings.append({
118
+ "title": title_tag.text.strip(),
119
+ "company": company_tag.text.strip(),
120
+ "location": location_tag.text.strip(),
121
+ "link": link_tag["href"].strip(),
122
+ "recruiter_email": fetch_recruiter_email(job_card) # Placeholder function
123
+ })
124
+
125
+ return job_listings
126
+
127
+ # Placeholder function for fetching recruiter email
128
+ def fetch_recruiter_email(job_card):
129
+ # Replace this with actual logic to find recruiter emails
130
+ email_tag = job_card.find("span", class_="recruiter-email") # Example selector
131
+ return email_tag.text.strip() if email_tag else None
132
+
133
+ # Function to generate cold email and cover letter
134
+ def generate_email_and_cover_letter(job_title, company, recruiter_email):
135
+ cold_email = f"""
136
+ Subject: Application for {job_title} Position at {company}
137
+
138
+ Dear Hiring Manager,
139
+
140
+ 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.
141
+
142
+ Looking forward to your response.
143
+
144
+ Best Regards,
145
+ [Your Name]
146
+ """
147
+
148
+ cover_letter = f"""
149
+ Dear Hiring Manager,
150
+
151
+ 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.
152
+
153
+ I am eager to bring my skills to your esteemed company and contribute effectively. Please find my resume attached for review.
154
+
155
+ Thank you for your time and consideration. I look forward to the opportunity to speak with you.
156
+
157
+ Sincerely,
158
+ [Your Name]
159
+ """
160
+
161
+ return cold_email, cover_letter
162
+
163
+ # Function to generate LinkedIn message & connection request note
164
+ def generate_linkedin_outreach(job_title, company):
165
+ linkedin_message = f"""
166
+ Hi [Recruiter Name],
167
+
168
+ I hope you're doing well. I came across the {job_title} opening at {company} and I am very interested.
169
+ I would love to connect and learn more about this opportunity.
170
+ Looking forward to your response!
171
+
172
+ Best,
173
+ [Your Name]
174
+ """
175
+
176
+ connection_request_note = f"""
177
+ Hi [Recruiter Name], I’m interested in the {job_title} role at {company} and would love to connect.
178
+ """
179
+
180
+ return linkedin_message, connection_request_note
181
+
182
+ # Streamlit UI
183
+ st.title("📌 Job Listings from LinkedIn")
184
+ keyword = st.text_input("Job Title (e.g., Data Scientist)", value=st.session_state.get("aspired job role", ""))
185
+ location = st.text_input("Location (e.g., New York)")
186
+
187
+ if st.button("Fetch Jobs"):
188
+ jobs = fetch_jobs_from_linkedin(keyword, location)
189
+ if jobs:
190
+ for job in jobs[:3]:
191
+ st.markdown(f"**{job['title']}** at {job['company']} ({job['location']})")
192
+ st.markdown(f"[Apply Now]({job['link']})")
193
+
194
+ # Call Gemini to get match score
195
+ 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.
196
+ Job Role: {job['title']} at {job['company']} in {job['location']}
197
+
198
+ Resume: {st.session_state.resume_text}
199
+ Keep the response short and structured like this:
200
+ Score: 85
201
+ Reason: Strong experience in React and REST APIs aligns well.
202
+ """
203
+ match_response = chat_model.predict(match_prompt)
204
+
205
+ # Display the match score. Extract score and reason from the response
206
+ match = re.search(r"Score:\s*(\d+)\s*\nReason:\s*(.*)", match_response)
207
+ if match:
208
+ score = int(match.group(1))
209
+ reason = match.group(2)
210
+
211
+ # Use color coding for different match levels
212
+ if score >= 80:
213
+ color = "green"
214
+ elif score >= 50:
215
+ color = "orange"
216
+ else:
217
+ color = "red"
218
+
219
+ # Display match score with highlight
220
+ st.markdown(f"🔍 **Resume Match Score:** <span style='color:{color}; font-size:22px; font-weight:bold'>{score}/100</span>", unsafe_allow_html=True)
221
+ # Display reason separately
222
+ st.markdown(f"📌 **Reason:** {reason}")
223
+ else:
224
+ st.markdown("⚠️ Could not extract match score. Please check the response format.")
225
+
226
+
227
+
228
+ # Show recruiter email if available
229
+ if job['recruiter_email']:
230
+ st.markdown(f"**Recruiter Email:** {job['recruiter_email']}")
231
+ cold_email, cover_letter = generate_email_and_cover_letter(job['title'], job['company'], job['recruiter_email'])
232
+
233
+ with st.expander("📧 Suggested Cold Email"):
234
+ st.code(cold_email)
235
+
236
+ with st.expander("📜 Suggested Cover Letter"):
237
+ st.code(cover_letter)
238
+ else:
239
+ st.markdown(f"**Recruiters Email not available**")
240
+
241
+ # Provide LinkedIn message & connection note
242
+ linkedin_msg, connection_note = generate_linkedin_outreach(job['title'], job['company'])
243
+
244
+ with st.expander("💬 Suggested LinkedIn Message"):
245
+ st.code(linkedin_msg)
246
+
247
+ with st.expander("🔗 Connection Request Note"):
248
+ st.code(connection_note)
249
+ else:
250
+ st.write("No jobs found. Try a different search.")
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ streamlit_chat
3
+ langchain-google-genai
4
+ PyPDF2
5
+ python-docx
6
+ requests
7
+ beautifulsoup4
8
+ python-dotenv