Spaces:
Sleeping
Sleeping
File size: 12,146 Bytes
2ce6de2 2fbd716 fb49951 2fbd716 fc99598 fb49951 f6c0fca a7cb257 c46c2e4 fb1840b c46c2e4 fa13b9f c46c2e4 fa13b9f a7cb257 2fbd716 a7cb257 2fbd716 fc99598 0623dcd 7e56bb6 14254fc 07c26b6 e38a317 07c26b6 e38a317 07c26b6 02568ca 07c26b6 02568ca 14254fc 07c26b6 7e56bb6 2fbd716 02568ca 07c26b6 e38a317 2fbd716 fb49951 2fbd716 fb1840b c46c2e4 fb1840b c46c2e4 2ce6de2 fc99598 2ce6de2 2fbd716 fb1840b 2fbd716 fb1840b fc99598 fb1840b 2fbd716 fc99598 c46c2e4 fb1840b 7e56bb6 fb1840b 7e56bb6 fa13b9f fb1840b fc99598 fb1840b fc99598 8c38eea 7e56bb6 8c38eea 2ce6de2 fa13b9f 14254fc 07c26b6 14254fc 7e56bb6 fb1840b 7e56bb6 14254fc 7e56bb6 fa13b9f 2ce6de2 fb1840b 7e56bb6 2ce6de2 fb1840b fa13b9f 2ce6de2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
import gradio as gr
from langchain_community.document_loaders.pdf import PyPDFLoader
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_groq import ChatGroq
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from sklearn.metrics.pairwise import cosine_similarity
from dotenv import load_dotenv
from fpdf import FPDF
from collections import Counter
import numpy as np
import tempfile
import os
load_dotenv()
groq_api_key = os.getenv("GROQ_API_KEY")
os.environ["GROQ_API_KEY"] = groq_api_key
def extract_text_from_pdf(pdf_file):
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp:
temp.write(pdf_file)
temp.flush()
loader = PyPDFLoader(temp.name)
pages = loader.load_and_split()
return " ".join([page.page_content for page in pages])
def extract_skills(text):
skills_list = ["Python", "SQL", "Machine Learning", "Deep Learning", "NLP", "Data Visualization", "Cloud", "TensorFlow", "PyTorch", "Statistics", "Java", "C++", "HTML", "CSS", "JavaScript"]
return [skill for skill in skills_list if skill.lower() in text.lower()]
def generate_learning_resources(missing_skills):
suggestions = []
for skill in missing_skills:
search_link = f"https://www.google.com/search?q={skill}+online+course"
youtube_link = f"https://www.youtube.com/results?search_query={skill}+tutorial"
suggestions.append(f"π [{skill} Courses on Google]({search_link})\nβΆοΈ [YouTube Tutorials]({youtube_link})\n")
return "\n\n".join(suggestions)
def suggest_certifications(missing_skills):
cert_mapping = {
"Python": "Python for Everybody (Coursera)",
"Machine Learning": "Machine Learning by Andrew Ng (Coursera)",
"Cloud": "AWS Certified Solutions Architect",
"SQL": "Google Data Analytics Certificate",
"TensorFlow": "TensorFlow Developer Certificate",
"NLP": "Natural Language Processing Specialization (DeepLearning.AI)",
"Java": "Oracle Certified Java Programmer",
"C++": "C++ Nanodegree (Udacity)"
}
suggestions = []
for skill in missing_skills:
if skill in cert_mapping:
suggestions.append(f"{skill}: {cert_mapping[skill]}")
return "\n".join(suggestions) if suggestions else "No specific certifications recommended."
def generate_circular_progress(percentage):
html_code = f"""
<style>
.circular-progress {{
position: relative;
width: 160px;
height: 160px;
border-radius: 50%;
background: conic-gradient(#00c6ff 0%, #003366 0%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px rgba(0, 198, 255, 0.5);
animation: fillAnimation 2s ease-out forwards;
}}
.circular-progress::after {{
content: "";
position: absolute;
width: 120px;
height: 120px;
background: #1b263b;
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}}
.circular-progress span {{
position: absolute;
color: #00f7ff;
font-size: 1.8em;
font-weight: bold;
text-shadow: 0 0 5px rgba(0,255,255,0.8);
z-index: 2;
}}
@keyframes fillAnimation {{
from {{
background: conic-gradient(#00c6ff 0%, #003366 0%);
}}
to {{
background: conic-gradient(#00c6ff {percentage}%, #003366 {percentage}% 100%);
}}
}}
</style>
<div class='circular-progress' style='--progress: {percentage};'>
<span>{percentage}%</span>
</div>
"""
return html_code
def generate_skill_gap_report(user_skills, job_skills, missing_skills, match_percent):
llm = ChatGroq(model="llama3-8b-8192", temperature=0.2)
template = """
User Skills: {user_skills}
Job Requirements: {job_skills}
Missing Skills: {missing_skills}
Match Percentage: {match_percent}%
Generate a short, friendly skill gap report. Suggest next steps for the user to improve their chances.
"""
prompt = PromptTemplate.from_template(template)
chain = prompt | llm | StrOutputParser()
report = chain.invoke({
"user_skills": ", ".join(user_skills),
"job_skills": ", ".join(job_skills),
"missing_skills": ", ".join(missing_skills),
"match_percent": match_percent
})
return report
def create_pdf(full_report_text):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=12)
pdf.multi_cell(0, 10, full_report_text)
output_path = "multi_jd_skill_gap_report.pdf"
pdf.output(output_path)
return output_path
def process_skill_gap(resume_pdf, jd_pdfs):
if resume_pdf is None or jd_pdfs is None:
return "", "", "", "", "", None, "", "", ""
resume_text = extract_text_from_pdf(resume_pdf)
user_skills = extract_skills(resume_text)
all_missing_skills = []
full_report = ""
for idx, jd_pdf in enumerate(jd_pdfs, start=1):
jd_text = extract_text_from_pdf(jd_pdf)
job_skills = extract_skills(jd_text)
common = set(user_skills) & set(job_skills)
match_percent = (len(common) / len(job_skills)) * 100 if job_skills else 0
missing_skills = list(set(job_skills) - set(user_skills))
all_missing_skills.extend(missing_skills)
embed_model = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
vectors = embed_model.embed_documents([resume_text, jd_text])
similarity_score = cosine_similarity([vectors[0]], [vectors[1]])[0][0]
similarity_percent = round(similarity_score * 100, 2)
ai_report = generate_skill_gap_report(user_skills, job_skills, missing_skills, match_percent)
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"
resources = generate_learning_resources(list(set(all_missing_skills)))
certifications = suggest_certifications(all_missing_skills)
most_common_skills = Counter(all_missing_skills).most_common(3)
top_missing_skills_text = "Top Missing Skills Across JDs: " + ", ".join(
[f"{skill} ({count} times)" for skill, count in most_common_skills]
) if most_common_skills else "No missing skills detected."
overall_match = round(
(sum([len(set(user_skills) & set(extract_skills(extract_text_from_pdf(jd)))) for jd in jd_pdfs]) / (len(user_skills) * len(jd_pdfs))) * 100,
2
) if user_skills else 0
full_report_clean = full_report.encode('ascii', 'ignore').decode('ascii')
pdf_path = create_pdf(full_report_clean)
progress_display = generate_circular_progress(overall_match)
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
with gr.Blocks() as demo:
gr.HTML("""
<style>
body {
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
background-size: 300% 300%;
animation: gradientShift 15s ease infinite;
font-family: 'Segoe UI', 'Roboto', sans-serif;
color: #ffffff;
margin: 0;
padding: 0;
}
@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.07);
box-shadow: 0 0 30px 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: 0.2;}
90% {opacity: 0.2;}
100% {transform: translateY(-200px); opacity: 0;}
}
.floating-circle:nth-child(1) { width: 80px; height: 80px; left: 15%; 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; }
input, textarea, .gr-textbox, .gr-slider, .gr-button, .gr-file, .gr-markdown, .gr-image {
background: rgba(255, 255, 255, 0.05) !important;
color: #ffffff !important;
border-radius: 10px !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
backdrop-filter: blur(8px);
padding: 10px !important;
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
transition: all 0.3s ease;
}
.gr-button {
background: linear-gradient(135deg, #00c6ff, #0072ff) !important;
color: #ffffff !important;
font-weight: bold;
text-transform: uppercase;
cursor: pointer;
border: none !important;
box-shadow: 0 4px 15px rgba(0, 198, 255, 0.4);
}
.gr-button:hover {
background: linear-gradient(135deg, #0072ff, #00c6ff) !important;
box-shadow: 0 6px 20px rgba(0, 198, 255, 0.6);
transform: translateY(-2px);
}
textarea, .gr-textbox {
min-height: 120px !important;
line-height: 1.6;
}
h1, h2, h3, p, label {
color: #00f7ff !important;
text-shadow: 0 0 8px rgba(0, 255, 255, 0.3);
}
.circular-progress {
position: relative;
width: 160px;
height: 160px;
border-radius: 50%;
background: conic-gradient(#00c6ff 0%, #003366 0%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px rgba(0, 198, 255, 0.5);
animation: fillAnimation 2s ease-out forwards;
}
.circular-progress::after {
content: "";
position: absolute;
width: 120px;
height: 120px;
background: #1b263b;
border-radius: 50%;
}
.circular-progress span {
position: absolute;
color: #00f7ff;
font-size: 1.8em;
font-weight: bold;
text-shadow: 0 0 5px rgba(0,255,255,0.8);
}
@keyframes fillAnimation {
from { background: conic-gradient(#00c6ff 0%, #003366 0%); }
to { background: conic-gradient(#00c6ff var(--progress)%, #003366 var(--progress)% 100%); }
}
</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>
""")
gr.Markdown("# π§ TALENTPATCH - Multi-JD AI Skill Gap Checker")
resume_file = gr.File(label="π Upload Resume (PDF)", type="binary")
jd_files = gr.File(label="π Upload Multiple Job Descriptions (PDFs)", type="binary", file_types=[".pdf"], file_count="multiple")
match_progress = gr.HTML(label="Skill Match Progress")
skill_match_text = gr.Textbox(label="Status", interactive=False)
missing_skills_text = gr.Textbox(label="All Missing Skills", interactive=False)
similarity_text = gr.Textbox(label="Status Message", interactive=False)
report_output = gr.Textbox(label="AI-Generated Multi-JD Skill Gap Report", lines=20, interactive=False)
download_pdf = gr.File(label="π₯ Download Full Report as PDF")
top_skills_output = gr.Textbox(label="Top Missing Skills Across JDs", interactive=False)
learning_resources = gr.Markdown(label="π AI Learning Resource Recommendations")
certification_output = gr.Textbox(label="π Recommended Certifications", interactive=False)
submit_btn = gr.Button("π Analyze Skill Gap")
submit_btn.click(
fn=process_skill_gap,
inputs=[resume_file, jd_files],
outputs=[
match_progress,
skill_match_text,
missing_skills_text,
similarity_text,
report_output,
download_pdf,
top_skills_output,
learning_resources,
certification_output
]
)
demo.launch()
|