Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,64 +1,102 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
-
from
|
| 3 |
-
import
|
| 4 |
-
from
|
| 5 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
# Load model
|
| 8 |
-
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
|
| 9 |
|
| 10 |
-
#
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
for page in reader.pages:
|
| 15 |
-
text += page.extract_text()
|
| 16 |
-
return text
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
text = "\n".join([para.text for para in doc.paragraphs])
|
| 21 |
-
return text
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
if filename.endswith(".pdf"):
|
| 26 |
-
return extract_text_from_pdf(file)
|
| 27 |
-
elif filename.endswith(".docx"):
|
| 28 |
-
return extract_text_from_docx(file)
|
| 29 |
-
elif filename.endswith(".txt"):
|
| 30 |
-
return file.read().decode("utf-8")
|
| 31 |
-
else:
|
| 32 |
-
return "Unsupported file format."
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
embedding2 = model.encode(text2, convert_to_tensor=True)
|
| 37 |
-
score = util.pytorch_cos_sim(embedding1, embedding2)
|
| 38 |
-
return float(score[0][0]) * 100 # Convert to percentage
|
| 39 |
|
| 40 |
-
#
|
| 41 |
-
st.
|
| 42 |
-
st.title("🎯 Resume vs Job Description Fit Score")
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
resume_file = st.file_uploader("Upload your resume", type=["pdf", "docx", "txt"])
|
| 47 |
-
jd_file = st.file_uploader("Upload job description", type=["pdf", "docx", "txt"])
|
| 48 |
|
| 49 |
-
if
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
-
|
|
|
|
| 55 |
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
-
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
-
|
| 62 |
-
st.
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
| 3 |
+
from fpdf import FPDF
|
| 4 |
+
from utils import extract_text, render_template
|
| 5 |
import os
|
| 6 |
+
from sentence_transformers import SentenceTransformer
|
| 7 |
+
import pdfplumber
|
| 8 |
+
import PyPDF2
|
| 9 |
+
import docx
|
| 10 |
+
from io import StringIO
|
| 11 |
|
|
|
|
|
|
|
| 12 |
|
| 13 |
+
# Load pre-trained model for text classification or matching (you can change this to the relevant task model)
|
| 14 |
+
model_name = 'bert-base-uncased' # Replace with an appropriate model for your task
|
| 15 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 16 |
+
model = AutoModelForSequenceClassification.from_pretrained(model_name)
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
+
# Load SentenceTransformer model for semantic matching
|
| 19 |
+
embedder = SentenceTransformer('all-MiniLM-L6-v2')
|
|
|
|
|
|
|
| 20 |
|
| 21 |
+
# Set up the streamlit interface
|
| 22 |
+
st.title("Resume Tailoring & Job Fit App")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
# File upload for Resume (accepting multiple formats)
|
| 25 |
+
resume_file = st.file_uploader("Upload your Resume (PDF, DOCX, TXT, HTML)", type=["pdf", "docx", "txt", "html"])
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
+
# Text area for Job Description or Scholarship details (paste text)
|
| 28 |
+
job_description = st.text_area("Paste the Job Description or Scholarship details", height=200)
|
|
|
|
| 29 |
|
| 30 |
+
# Button to submit the job description and resume for processing
|
| 31 |
+
submit_button = st.button("Submit")
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
if submit_button:
|
| 34 |
+
if resume_file is not None and job_description:
|
| 35 |
+
# Extract text from resume file
|
| 36 |
+
if resume_file.type == "application/pdf":
|
| 37 |
+
resume_text = extract_text(resume_file, "pdf")
|
| 38 |
+
elif resume_file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
| 39 |
+
resume_text = extract_text(resume_file, "docx")
|
| 40 |
+
elif resume_file.type == "text/plain":
|
| 41 |
+
resume_text = extract_text(resume_file, "txt")
|
| 42 |
+
elif resume_file.type == "text/html":
|
| 43 |
+
resume_text = extract_text(resume_file, "html")
|
| 44 |
+
|
| 45 |
+
# Analyze the fit between the job description and resume
|
| 46 |
+
# Here, we use a semantic match between resume and job description
|
| 47 |
+
job_embedding = embedder.encode(job_description)
|
| 48 |
+
resume_embedding = embedder.encode(resume_text)
|
| 49 |
+
|
| 50 |
+
# Calculate cosine similarity (fit score)
|
| 51 |
+
from sklearn.metrics.pairwise import cosine_similarity
|
| 52 |
+
fit_score = cosine_similarity([job_embedding], [resume_embedding])[0][0]
|
| 53 |
+
|
| 54 |
+
# Display the fit score
|
| 55 |
+
st.write(f"Fit Score: {fit_score*100:.2f}%")
|
| 56 |
+
|
| 57 |
+
# Extract required skills from job description (simple placeholder method)
|
| 58 |
+
required_skills = ["Python", "Machine Learning", "Data Analysis"] # Replace with actual extraction logic
|
| 59 |
+
missing_skills = [skill for skill in required_skills if skill.lower() not in resume_text.lower()]
|
| 60 |
+
|
| 61 |
+
if missing_skills:
|
| 62 |
+
st.write("Skills you might need to improve for better fit:")
|
| 63 |
+
st.write(", ".join(missing_skills))
|
| 64 |
+
else:
|
| 65 |
+
st.write("You have all the required skills for this job!")
|
| 66 |
|
| 67 |
+
# Resume tailoring based on job description (mock implementation)
|
| 68 |
+
tailored_resume = resume_text + "\n\n[Tailored to fit job description]\n" + job_description
|
| 69 |
|
| 70 |
+
# Template selection for preview
|
| 71 |
+
template_choice = st.selectbox("Choose a template for preview", ["Template 1", "Template 2", "Template 3"])
|
| 72 |
+
|
| 73 |
+
# Generate resume PDF from tailored content
|
| 74 |
+
if template_choice == "Template 1":
|
| 75 |
+
rendered_resume = render_template(tailored_resume, template="template1")
|
| 76 |
+
elif template_choice == "Template 2":
|
| 77 |
+
rendered_resume = render_template(tailored_resume, template="template2")
|
| 78 |
+
else:
|
| 79 |
+
rendered_resume = render_template(tailored_resume, template="template3")
|
| 80 |
+
|
| 81 |
+
st.text_area("Preview of your tailored resume:", rendered_resume, height=300)
|
| 82 |
|
| 83 |
+
# Allow the user to download the generated resume
|
| 84 |
+
pdf = FPDF()
|
| 85 |
+
pdf.add_page()
|
| 86 |
+
pdf.set_auto_page_break(auto=True, margin=15)
|
| 87 |
+
pdf.set_font("Arial", size=12)
|
| 88 |
+
pdf.multi_cell(0, 10, rendered_resume)
|
| 89 |
+
pdf_output = pdf.output(dest='S', name='resume.pdf')
|
| 90 |
+
|
| 91 |
+
st.download_button("Download Tailored Resume", pdf_output, file_name="tailored_resume.pdf")
|
| 92 |
|
| 93 |
+
# Application form questions (example)
|
| 94 |
+
st.subheader("Application Form")
|
| 95 |
+
application_question = st.text_input("Please answer this question:")
|
| 96 |
+
|
| 97 |
+
if application_question:
|
| 98 |
+
# Human-like response generation using a basic chatbot approach (simple placeholder)
|
| 99 |
+
response = f"Your response to '{application_question}' could be structured as follows: 'I am excited about this opportunity because...'"
|
| 100 |
+
st.write(response)
|
| 101 |
+
else:
|
| 102 |
+
st.warning("Please upload a resume and paste the job description.")
|