Spaces:
Sleeping
Sleeping
File size: 8,090 Bytes
cf8a522 4077883 8e1d297 92f45fe cc18787 e0405b6 d2d6501 5d07781 8e1d297 d2d6501 c6d228e d2d6501 5d07781 e0405b6 d2d6501 41d8604 e0405b6 41d8604 e0405b6 d2d6501 c6d228e d2d6501 8e1d297 92f45fe 4077883 7716c5c 92f45fe 7716c5c 9753cc9 92f45fe c6d228e 9753cc9 92f45fe 4077883 92f45fe 4077883 92f45fe 4077883 92f45fe 4077883 92f45fe 8e1d297 d2d6501 7716c5c c6d228e d836318 e0405b6 d836318 e0405b6 c6d228e d2d6501 e0405b6 c6d228e cc18787 d2d6501 c6d228e 0d4f4dd e0405b6 d836318 cccaa8e 41d8604 cccaa8e 41d8604 cccaa8e 41d8604 cccaa8e e0405b6 41d8604 c6d228e 41d8604 c6d228e 41d8604 e0405b6 41d8604 e0405b6 41d8604 cccaa8e 7716c5c e0405b6 8e1d297 d2d6501 cc18787 4077883 d2d6501 41d8604 d2d6501 cccaa8e e0405b6 4077883 d2d6501 e0405b6 3661e7e e0405b6 4077883 e0405b6 d2d6501 e0405b6 41d8604 |
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 |
import os
import io
import streamlit as st
import docx
from transformers import pipeline
import time
# Set page title and hide sidebar
st.set_page_config(
page_title="Resume Analyzer and Company Suitability Checker",
initial_sidebar_state="collapsed"
)
# Hide sidebar completely with custom CSS
st.markdown("""
<style>
[data-testid="collapsedControl"] {display: none;}
section[data-testid="stSidebar"] {display: none;}
</style>
""", unsafe_allow_html=True)
#####################################
# Preload Models
#####################################
@st.cache_resource(show_spinner=True)
def load_models():
"""Load models at startup"""
with st.spinner("Loading AI models... This may take a minute on first run."):
models = {}
# Load summarization model
models['summarizer'] = pipeline("summarization", model="google/pegasus-xsum")
# Load text generation model for suitability assessment
models['text_generator'] = pipeline("text-generation", model="gpt2") # You can use different models
return models
# Preload models immediately when app starts
models = load_models()
#####################################
# Function: Extract Text from File
#####################################
def extract_text_from_file(file_obj):
"""
Extract text from .docx files.
Returns the extracted text or an error message if extraction fails.
"""
filename = file_obj.name
ext = os.path.splitext(filename)[1].lower()
text = ""
if ext == ".docx":
try:
document = docx.Document(file_obj)
text = "\n".join(para.text for para in document.paragraphs if para.text.strip())
except Exception as e:
text = f"Error processing DOCX file: {e}"
elif ext == ".txt":
try:
text = file_obj.getvalue().decode("utf-8")
except Exception as e:
text = f"Error processing TXT file: {e}"
else:
text = "Unsupported file type. Please upload a .docx or .txt file."
return text
#####################################
# Function: Summarize Resume Text
#####################################
def summarize_resume_text(resume_text, models):
"""
Generates a concise summary of the resume text using the selected summarization model.
"""
start_time = time.time()
summarizer = models['summarizer']
# Handle long text
max_input_length = 1024 # Model limit
if len(resume_text) > max_input_length:
# Process in chunks if text is too long
chunks = [resume_text[i:i+max_input_length] for i in range(0, min(len(resume_text), 3*max_input_length), max_input_length)]
summaries = []
for chunk in chunks:
chunk_summary = summarizer(chunk, max_length=100, min_length=30, do_sample=False)[0]['summary_text']
summaries.append(chunk_summary)
candidate_summary = " ".join(summaries)
if len(candidate_summary) > max_input_length:
candidate_summary = summarizer(candidate_summary[:max_input_length], max_length=150, min_length=40, do_sample=False)[0]['summary_text']
else:
candidate_summary = summarizer(resume_text, max_length=150, min_length=40, do_sample=False)[0]['summary_text']
execution_time = time.time() - start_time
return candidate_summary, execution_time
#####################################
# Function: Generate Suitability Assessment
#####################################
def generate_suitability_assessment(candidate_summary, company_prompt, models):
"""
Generate a suitability assessment using text generation instead of similarity.
Returns the generated assessment text and execution time.
"""
start_time = time.time()
text_generator = models['text_generator']
# Create a prompt for the text generation model
prompt = f"""
Resume Summary: {candidate_summary}
Company Description: {company_prompt}
Suitability Assessment:
This candidate is a"""
# Generate text
max_length = 80 + len(prompt.split()) # Limit output length
generated_text = text_generator(
prompt,
max_length=max_length,
num_return_sequences=1,
temperature=0.7,
top_p=0.9,
do_sample=True
)[0]['generated_text']
# Extract only the assessment part (after the prompt)
assessment = generated_text[len(prompt):].strip()
# Determine a numerical score from the text
# This is a simplified approach - we're looking for positive and negative words
positive_words = ['excellent', 'perfect', 'great', 'good', 'strong', 'ideal', 'qualified']
negative_words = ['poor', 'weak', 'bad', 'insufficient', 'inadequate', 'not a good']
assessment_lower = assessment.lower()
# Simple heuristic for score estimation
score = 0.5 # Default middle score
for word in positive_words:
if word in assessment_lower:
score += 0.1 # Increase score for positive words
for word in negative_words:
if word in assessment_lower:
score -= 0.1 # Decrease score for negative words
# Clamp the score between 0 and 1
score = max(0.1, min(0.9, score))
execution_time = time.time() - start_time
return assessment, score, execution_time
#####################################
# Main Streamlit Interface
#####################################
st.title("Resume Analyzer and Company Suitability Checker")
st.markdown(
"""
Upload your resume file in **.docx** or **.txt** format. The app performs the following tasks:
1. Extracts text from the resume.
2. Uses a transformer-based model to generate a concise candidate summary.
3. Uses text generation to assess the candidate's suitability for the company.
"""
)
# File uploader
uploaded_file = st.file_uploader("Upload your resume (.docx or .txt)", type=["docx", "txt"])
# Company description text area
company_prompt = st.text_area(
"Enter the company description or job requirements:",
height=150,
help="Enter a detailed description of the company culture, role requirements, and desired skills.",
)
# Process button
if uploaded_file is not None and company_prompt and st.button("Analyze Resume"):
with st.spinner("Processing..."):
# Extract text from resume
resume_text = extract_text_from_file(uploaded_file)
if resume_text.startswith("Error") or resume_text == "Unsupported file type. Please upload a .docx or .txt file.":
st.error(resume_text)
else:
# Generate summary
summary, summarization_time = summarize_resume_text(resume_text, models)
# Display summary
st.subheader("Candidate Summary")
st.write(summary)
st.info(f"Summarization completed in {summarization_time:.2f} seconds")
# Generate suitability assessment with text generation
assessment, estimated_score, generation_time = generate_suitability_assessment(summary, company_prompt, models)
# Display assessment
st.subheader("Suitability Assessment")
st.write(assessment)
st.markdown(f"**Estimated Matching Score:** {estimated_score:.2%}")
st.info(f"Assessment generated in {generation_time:.2f} seconds")
# Provide interpretation based on estimated score
if estimated_score >= 0.85:
st.success("Excellent match! This candidate's profile is strongly aligned with the company requirements.")
elif estimated_score >= 0.70:
st.success("Good match! This candidate shows strong potential for the position.")
elif estimated_score >= 0.50:
st.warning("Moderate match. The candidate meets some requirements but there may be gaps.")
else:
st.error("Low match. The candidate's profile may not align well with the requirements.") |