Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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 |
-
|
| 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 |
-
.
|
| 261 |
-
|
| 262 |
-
|
| 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 |
-
.
|
| 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
|
| 306 |
submit_btn.click(
|
| 307 |
fn=process_skill_gap,
|
| 308 |
-
inputs=[resume_file, jd_files],
|
| 309 |
outputs=[
|
| 310 |
-
|
| 311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|