rca123456 commited on
Commit
7e56bb6
Β·
verified Β·
1 Parent(s): 5868103

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -164
app.py CHANGED
@@ -11,29 +11,24 @@ from collections import Counter
11
  import numpy as np
12
  import tempfile
13
  import os
 
 
14
 
15
  load_dotenv()
16
  groq_api_key = os.getenv("GROQ_API_KEY")
17
  os.environ["GROQ_API_KEY"] = groq_api_key
18
 
19
- import requests
20
- from bs4 import BeautifulSoup
21
-
22
  def scrape_skills_from_jd_link(url):
23
  try:
24
  response = requests.get(url, timeout=10)
25
  soup = BeautifulSoup(response.text, 'html.parser')
26
  text = soup.get_text(separator=' ', strip=True)
27
-
28
- # Basic keyword match for now
29
  skills_list = ["Python", "SQL", "Machine Learning", "Deep Learning", "NLP", "Cloud", "TensorFlow", "Java", "C++", "HTML", "CSS", "JavaScript"]
30
  extracted_skills = [skill for skill in skills_list if skill.lower() in text.lower()]
31
-
32
  return extracted_skills if extracted_skills else ["No skills detected."]
33
  except Exception as e:
34
  return [f"Error fetching JD: {str(e)}"]
35
 
36
-
37
  def extract_text_from_pdf(pdf_file):
38
  with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp:
39
  temp.write(pdf_file)
@@ -65,38 +60,37 @@ def suggest_certifications(missing_skills):
65
  "Java": "Oracle Certified Java Programmer",
66
  "C++": "C++ Nanodegree (Udacity)"
67
  }
68
-
69
  suggestions = []
70
  for skill in missing_skills:
71
  if skill in cert_mapping:
72
  suggestions.append(f"{skill}: {cert_mapping[skill]}")
73
-
74
  return "\n".join(suggestions) if suggestions else "No specific certifications recommended."
75
 
76
-
 
 
 
 
 
 
77
 
78
  def generate_skill_gap_report(user_skills, job_skills, missing_skills, match_percent):
79
  llm = ChatGroq(model="llama3-8b-8192", temperature=0.2)
80
-
81
  template = """
82
  User Skills: {user_skills}
83
  Job Requirements: {job_skills}
84
  Missing Skills: {missing_skills}
85
  Match Percentage: {match_percent}%
86
-
87
  Generate a short, friendly skill gap report. Suggest next steps for the user to improve their chances.
88
  """
89
-
90
  prompt = PromptTemplate.from_template(template)
91
  chain = prompt | llm | StrOutputParser()
92
-
93
  report = chain.invoke({
94
  "user_skills": ", ".join(user_skills),
95
  "job_skills": ", ".join(job_skills),
96
  "missing_skills": ", ".join(missing_skills),
97
  "match_percent": match_percent
98
  })
99
-
100
  return report
101
 
102
  def create_pdf(full_report_text):
@@ -108,20 +102,23 @@ def create_pdf(full_report_text):
108
  pdf.output(output_path)
109
  return output_path
110
 
111
- def process_skill_gap(resume_pdf, jd_pdfs):
112
  if resume_pdf is None or jd_pdfs is None:
113
- return 0, "❌ Please upload Resume and Job Descriptions.", "", "", "", None, "", ""
 
 
 
 
 
114
 
115
  resume_text = extract_text_from_pdf(resume_pdf)
116
  user_skills = extract_skills(resume_text)
117
-
118
  all_missing_skills = []
119
  full_report = ""
120
 
121
  for idx, jd_pdf in enumerate(jd_pdfs, start=1):
122
  jd_text = extract_text_from_pdf(jd_pdf)
123
  job_skills = extract_skills(jd_text)
124
-
125
  common = set(user_skills) & set(job_skills)
126
  match_percent = (len(common) / len(job_skills)) * 100 if job_skills else 0
127
  missing_skills = list(set(job_skills) - set(user_skills))
@@ -133,15 +130,10 @@ def process_skill_gap(resume_pdf, jd_pdfs):
133
  similarity_percent = round(similarity_score * 100, 2)
134
 
135
  ai_report = generate_skill_gap_report(user_skills, job_skills, missing_skills, match_percent)
136
-
137
- full_report += f"\nJD {idx}:\n"
138
- full_report += f"Skill Match: {match_percent}%\n"
139
- full_report += f"Missing Skills: {', '.join(missing_skills) if missing_skills else 'None'}\n"
140
- full_report += f"Similarity Score: {similarity_percent}%\n"
141
- full_report += f"AI Report:\n{ai_report}\n"
142
- full_report += "-------------------------\n"
143
 
144
  resources = generate_learning_resources(list(set(all_missing_skills)))
 
145
 
146
  most_common_skills = Counter(all_missing_skills).most_common(3)
147
  top_missing_skills_text = "Top Missing Skills Across JDs: " + ", ".join(
@@ -153,162 +145,48 @@ def process_skill_gap(resume_pdf, jd_pdfs):
153
  2
154
  ) if user_skills else 0
155
 
156
- # Clean full_report to remove emojis for PDF saving
157
  full_report_clean = full_report.encode('ascii', 'ignore').decode('ascii')
158
  pdf_path = create_pdf(full_report_clean)
 
159
 
160
- return overall_match, "βœ… Analysis done across all JDs", ", ".join(set(all_missing_skills)), "Multi-JD Comparison Completed", full_report, pdf_path, top_missing_skills_text, resources
161
 
162
  with gr.Blocks() as demo:
163
- gr.HTML("""
164
- <style>
165
- body {
166
- background: linear-gradient(135deg, #0d1b2a, #1b263b, #415a77);
167
- background-size: 300% 300%;
168
- animation: gradientShift 15s ease infinite;
169
- overflow-x: hidden;
170
- position: relative;
171
- }
172
-
173
- @keyframes gradientShift {
174
- 0% {background-position: 0% 50%;}
175
- 50% {background-position: 100% 50%;}
176
- 100% {background-position: 0% 50%;}
177
- }
178
-
179
- /* Floating circles (professional, subtle glowing particles) */
180
- .floating-circle {
181
- position: fixed;
182
- border-radius: 50%;
183
- background: rgba(0, 255, 255, 0.08);
184
- box-shadow: 0 0 20px rgba(0, 255, 255, 0.2);
185
- animation: floatUp linear infinite;
186
- z-index: -1;
187
- filter: blur(2px);
188
- }
189
-
190
- @keyframes floatUp {
191
- 0% {
192
- transform: translateY(100vh);
193
- opacity: 0;
194
- }
195
- 10% {
196
- opacity: 0.2;
197
- }
198
- 90% {
199
- opacity: 0.2;
200
- }
201
- 100% {
202
- transform: translateY(-200px);
203
- opacity: 0;
204
- }
205
- }
206
-
207
- /* Add 5 floating circles with different sizes, positions, and speeds */
208
- .floating-circle:nth-child(1) {
209
- width: 80px;
210
- height: 80px;
211
- left: 20%;
212
- animation-duration: 25s;
213
- animation-delay: 0s;
214
- }
215
- .floating-circle:nth-child(2) {
216
- width: 100px;
217
- height: 100px;
218
- left: 50%;
219
- animation-duration: 30s;
220
- animation-delay: 5s;
221
- }
222
- .floating-circle:nth-child(3) {
223
- width: 60px;
224
- height: 60px;
225
- left: 70%;
226
- animation-duration: 20s;
227
- animation-delay: 10s;
228
- }
229
- .floating-circle:nth-child(4) {
230
- width: 90px;
231
- height: 90px;
232
- left: 35%;
233
- animation-duration: 35s;
234
- animation-delay: 15s;
235
- }
236
- .floating-circle:nth-child(5) {
237
- width: 120px;
238
- height: 120px;
239
- left: 80%;
240
- animation-duration: 40s;
241
- animation-delay: 20s;
242
- }
243
-
244
- /* Text styling */
245
- h1, h2, h3, p, label {
246
- color: #00f7ff !important;
247
- text-shadow: 0 0 8px rgba(0, 255, 255, 0.3);
248
- }
249
 
250
- /* Input, buttons, etc */
251
- input, textarea, .gr-textbox, .gr-slider, .gr-button, .gr-file, .gr-markdown, .gr-image {
252
- background: rgba(255, 255, 255, 0.05) !important;
253
- color: #ffffff !important;
254
- border-radius: 8px !important;
255
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
256
- backdrop-filter: blur(6px);
257
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
258
- }
259
 
260
- .gr-button {
261
- background: linear-gradient(135deg, #00b4d8, #0077b6) !important;
262
- color: #ffffff !important;
263
- font-weight: bold;
264
- letter-spacing: 0.8px;
265
- border: none !important;
266
- cursor: pointer;
267
- text-transform: uppercase;
268
- box-shadow: 0 4px 12px rgba(0, 183, 255, 0.3);
269
- }
270
 
271
- .gr-button:hover {
272
- background: linear-gradient(135deg, #0077b6, #00b4d8) !important;
273
- box-shadow: 0 6px 20px rgba(0, 183, 255, 0.5);
274
- transform: translateY(-2px);
275
- }
276
- </style>
277
-
278
- <!-- Floating Circle Elements -->
279
- <div class="floating-circle"></div>
280
- <div class="floating-circle"></div>
281
- <div class="floating-circle"></div>
282
- <div class="floating-circle"></div>
283
- <div class="floating-circle"></div>
284
-
285
- """)
286
-
287
-
288
- gr.Markdown("# 🧠 TALENTPATCH With Multi-JD Comparison")
289
- gr.Markdown("Upload your **Resume PDF** and **Multiple Job Descriptions (PDFs)**. TALENTPATCH will compare and generate detailed reports with skill gap and recommendations.")
290
-
291
- with gr.Row():
292
- resume_file = gr.File(label="πŸ“„ Upload Resume (PDF)", type="binary")
293
- jd_files = gr.File(label="πŸ“„ Upload Multiple Job Descriptions (PDFs)", type="binary", file_types=[".pdf"], file_count="multiple")
294
-
295
- submit_btn = gr.Button("πŸš€ Analyze Skill Gap")
296
-
297
- match_slider = gr.Slider(minimum=0, maximum=100, step=1, label="Overall Skill Match (%)", interactive=False)
298
  skill_match_text = gr.Textbox(label="Status", interactive=False)
299
  missing_skills_text = gr.Textbox(label="All Missing Skills", interactive=False)
300
  similarity_text = gr.Textbox(label="Status Message", interactive=False)
301
  report_output = gr.Textbox(label="AI-Generated Multi-JD Skill Gap Report", lines=20, interactive=False)
302
  download_pdf = gr.File(label="πŸ“₯ Download Full Report as PDF")
303
  top_skills_output = gr.Textbox(label="Top Missing Skills Across JDs", interactive=False)
304
- learning_resources = gr.Textbox(label="πŸ“š AI Learning Resource Recommendations", interactive=False)
 
 
 
 
305
 
306
  submit_btn.click(
307
  fn=process_skill_gap,
308
- inputs=[resume_file, jd_files],
309
  outputs=[
310
- match_slider, skill_match_text, missing_skills_text, similarity_text,
311
- report_output, download_pdf, top_skills_output, learning_resources
 
 
 
 
 
 
 
 
312
  ]
313
  )
314
 
 
11
  import numpy as np
12
  import tempfile
13
  import os
14
+ import requests
15
+ from bs4 import BeautifulSoup
16
 
17
  load_dotenv()
18
  groq_api_key = os.getenv("GROQ_API_KEY")
19
  os.environ["GROQ_API_KEY"] = groq_api_key
20
 
 
 
 
21
  def scrape_skills_from_jd_link(url):
22
  try:
23
  response = requests.get(url, timeout=10)
24
  soup = BeautifulSoup(response.text, 'html.parser')
25
  text = soup.get_text(separator=' ', strip=True)
 
 
26
  skills_list = ["Python", "SQL", "Machine Learning", "Deep Learning", "NLP", "Cloud", "TensorFlow", "Java", "C++", "HTML", "CSS", "JavaScript"]
27
  extracted_skills = [skill for skill in skills_list if skill.lower() in text.lower()]
 
28
  return extracted_skills if extracted_skills else ["No skills detected."]
29
  except Exception as e:
30
  return [f"Error fetching JD: {str(e)}"]
31
 
 
32
  def extract_text_from_pdf(pdf_file):
33
  with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp:
34
  temp.write(pdf_file)
 
60
  "Java": "Oracle Certified Java Programmer",
61
  "C++": "C++ Nanodegree (Udacity)"
62
  }
 
63
  suggestions = []
64
  for skill in missing_skills:
65
  if skill in cert_mapping:
66
  suggestions.append(f"{skill}: {cert_mapping[skill]}")
 
67
  return "\n".join(suggestions) if suggestions else "No specific certifications recommended."
68
 
69
+ def generate_circular_progress(percentage):
70
+ html_code = f"""
71
+ <div style='position: relative; width: 150px; height: 150px; border-radius: 50%; background: conic-gradient(#00f7ff {percentage}%, #2c5364 {percentage}% 100%); display: flex; align-items: center; justify-content: center;'>
72
+ <div style='position: absolute; color: white; font-weight: bold;'>{percentage}%</div>
73
+ </div>
74
+ """
75
+ return html_code
76
 
77
  def generate_skill_gap_report(user_skills, job_skills, missing_skills, match_percent):
78
  llm = ChatGroq(model="llama3-8b-8192", temperature=0.2)
 
79
  template = """
80
  User Skills: {user_skills}
81
  Job Requirements: {job_skills}
82
  Missing Skills: {missing_skills}
83
  Match Percentage: {match_percent}%
 
84
  Generate a short, friendly skill gap report. Suggest next steps for the user to improve their chances.
85
  """
 
86
  prompt = PromptTemplate.from_template(template)
87
  chain = prompt | llm | StrOutputParser()
 
88
  report = chain.invoke({
89
  "user_skills": ", ".join(user_skills),
90
  "job_skills": ", ".join(job_skills),
91
  "missing_skills": ", ".join(missing_skills),
92
  "match_percent": match_percent
93
  })
 
94
  return report
95
 
96
  def create_pdf(full_report_text):
 
102
  pdf.output(output_path)
103
  return output_path
104
 
105
+ def process_skill_gap(resume_pdf, jd_pdfs, jd_link_url):
106
  if resume_pdf is None or jd_pdfs is None:
107
+ return "", "", "", "", "", None, "", "", "", ""
108
+
109
+ scraped_skills_text = ""
110
+ if jd_link_url:
111
+ scraped_skills = scrape_skills_from_jd_link(jd_link_url)
112
+ scraped_skills_text = ", ".join(scraped_skills)
113
 
114
  resume_text = extract_text_from_pdf(resume_pdf)
115
  user_skills = extract_skills(resume_text)
 
116
  all_missing_skills = []
117
  full_report = ""
118
 
119
  for idx, jd_pdf in enumerate(jd_pdfs, start=1):
120
  jd_text = extract_text_from_pdf(jd_pdf)
121
  job_skills = extract_skills(jd_text)
 
122
  common = set(user_skills) & set(job_skills)
123
  match_percent = (len(common) / len(job_skills)) * 100 if job_skills else 0
124
  missing_skills = list(set(job_skills) - set(user_skills))
 
130
  similarity_percent = round(similarity_score * 100, 2)
131
 
132
  ai_report = generate_skill_gap_report(user_skills, job_skills, missing_skills, match_percent)
133
+ full_report += f"\nJD {idx}:\nSkill Match: {match_percent}%\nMissing Skills: {', '.join(missing_skills) if missing_skills else 'None'}\nSimilarity Score: {similarity_percent}%\nAI Report:\n{ai_report}\n-------------------------\n"
 
 
 
 
 
 
134
 
135
  resources = generate_learning_resources(list(set(all_missing_skills)))
136
+ certifications = suggest_certifications(all_missing_skills)
137
 
138
  most_common_skills = Counter(all_missing_skills).most_common(3)
139
  top_missing_skills_text = "Top Missing Skills Across JDs: " + ", ".join(
 
145
  2
146
  ) if user_skills else 0
147
 
 
148
  full_report_clean = full_report.encode('ascii', 'ignore').decode('ascii')
149
  pdf_path = create_pdf(full_report_clean)
150
+ progress_display = generate_circular_progress(overall_match)
151
 
152
+ return progress_display, "βœ… Analysis done across all JDs", ", ".join(set(all_missing_skills)), "Multi-JD Comparison Completed", full_report, pdf_path, top_missing_skills_text, resources, certifications, scraped_skills_text
153
 
154
  with gr.Blocks() as demo:
155
+ gr.HTML("""<style>body{background: linear-gradient(135deg, #0d1b2a, #1b263b, #415a77);background-size: 300% 300%;animation: gradientShift 15s ease infinite;overflow-x: hidden;position: relative}@keyframes gradientShift{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}.floating-circle{position:fixed;border-radius:50%;background:rgba(0,255,255,0.08);box-shadow:0 0 20px rgba(0,255,255,0.2);animation:floatUp linear infinite;z-index:-1;filter:blur(2px)}@keyframes floatUp{0%{transform:translateY(100vh);opacity:0}10%{opacity:.2}90%{opacity:.2}100%{transform:translateY(-200px);opacity:0}}.floating-circle:nth-child(1){width:80px;height:80px;left:20%;animation-duration:25s}.floating-circle:nth-child(2){width:100px;height:100px;left:50%;animation-duration:30s;animation-delay:5s}.floating-circle:nth-child(3){width:60px;height:60px;left:70%;animation-duration:20s;animation-delay:10s}.floating-circle:nth-child(4){width:90px;height:90px;left:35%;animation-duration:35s;animation-delay:15s}.floating-circle:nth-child(5){width:120px;height:120px;left:80%;animation-duration:40s;animation-delay:20s}h1,h2,h3,p,label{color:#00f7ff!important;text-shadow:0 0 8px rgba(0,255,255,0.3)}</style><div class='floating-circle'></div><div class='floating-circle'></div><div class='floating-circle'></div><div class='floating-circle'></div><div class='floating-circle'></div>""")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
+ gr.Markdown("# 🧠 TALENTPATCH with Multi-JD Comparison and AI Skill Gap Analysis")
 
 
 
 
 
 
 
 
158
 
159
+ resume_file = gr.File(label="πŸ“„ Upload Resume (PDF)", type="binary")
160
+ jd_files = gr.File(label="πŸ“„ Upload Multiple Job Descriptions (PDFs)", type="binary", file_types=[".pdf"], file_count="multiple")
161
+ jd_link_input = gr.Textbox(label="πŸ”— Optional: Paste JD URL for Real-Time Skill Extraction")
 
 
 
 
 
 
 
162
 
163
+ match_progress = gr.HTML(label="Skill Match Progress")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  skill_match_text = gr.Textbox(label="Status", interactive=False)
165
  missing_skills_text = gr.Textbox(label="All Missing Skills", interactive=False)
166
  similarity_text = gr.Textbox(label="Status Message", interactive=False)
167
  report_output = gr.Textbox(label="AI-Generated Multi-JD Skill Gap Report", lines=20, interactive=False)
168
  download_pdf = gr.File(label="πŸ“₯ Download Full Report as PDF")
169
  top_skills_output = gr.Textbox(label="Top Missing Skills Across JDs", interactive=False)
170
+ learning_resources = gr.Markdown(label="πŸ“š AI Learning Resource Recommendations")
171
+ certification_output = gr.Textbox(label="πŸŽ“ Recommended Certifications", interactive=False)
172
+ scraped_skills_output = gr.Textbox(label="πŸ” Skills Extracted from JD Link", interactive=False)
173
+
174
+ submit_btn = gr.Button("πŸš€ Analyze Skill Gap")
175
 
176
  submit_btn.click(
177
  fn=process_skill_gap,
178
+ inputs=[resume_file, jd_files, jd_link_input],
179
  outputs=[
180
+ match_progress,
181
+ skill_match_text,
182
+ missing_skills_text,
183
+ similarity_text,
184
+ report_output,
185
+ download_pdf,
186
+ top_skills_output,
187
+ learning_resources,
188
+ certification_output,
189
+ scraped_skills_output
190
  ]
191
  )
192