Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -42,7 +42,7 @@ def extract_contact_info(resume_text):
|
|
42 |
name = names[0] if names else "Not Available"
|
43 |
|
44 |
# Extract email using regex
|
45 |
-
email_match = re.search(r"[a-zA-Z0-9._%+-]+@[a-
|
46 |
email = email_match.group(0) if email_match else "Not Available"
|
47 |
|
48 |
# Updated regex pattern for phone number extraction (more robust)
|
@@ -53,7 +53,6 @@ def extract_contact_info(resume_text):
|
|
53 |
|
54 |
# Function to extract relevant skills from the resume text
|
55 |
def extract_relevant_skills(resume_text):
|
56 |
-
# Look for sections related to skills/competencies
|
57 |
skills_section_keywords = ["skills", "technical skills", "competencies", "technologies", "tools"]
|
58 |
skills = []
|
59 |
|
@@ -68,163 +67,102 @@ def extract_relevant_skills(resume_text):
|
|
68 |
|
69 |
return skills
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
# Function to extract educational background from the resume text
|
72 |
def extract_educational_background(resume_text):
|
73 |
-
#
|
74 |
-
education_keywords = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
education = []
|
76 |
|
77 |
for keyword in education_keywords:
|
78 |
pattern = r"(?i)(%s)[^,]*[\.,;]" % re.escape(keyword)
|
79 |
matches = re.findall(pattern, resume_text)
|
|
|
80 |
for match in matches:
|
81 |
education.append(match.strip())
|
82 |
|
83 |
return education
|
84 |
|
85 |
-
#
|
86 |
-
|
87 |
-
# Use regex to extract any number of years mentioned in the job description for management or team leadership
|
88 |
-
year_matches = re.findall(r"\d+\s?year[s]?", job_description)
|
89 |
-
return [match for match in year_matches]
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
r"from (\d{4}) to present" # Matches: 'from 2019 to present'
|
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 |
else:
|
126 |
-
|
127 |
-
|
128 |
-
# Refined Prompt Template for Gemini API (requesting detailed text about management experience)
|
129 |
-
input_prompt = """
|
130 |
-
Act as a sophisticated Applicant Tracking System (ATS) with expertise in evaluating resumes specifically for management and team leadership roles. Your task is to analyze the resume in relation to the job description, focusing on direct and indirect team management experience, skills, and qualifications.
|
131 |
-
|
132 |
-
Instructions:
|
133 |
-
1. **Candidate's Name**: Identify and state the candidate’s full name as it appears in the resume.
|
134 |
-
2. **Direct Team Leadership Experience**: Clearly outline instances where the candidate has formally managed teams, led projects, or directly supervised team members. Quantify this experience in years wherever possible.
|
135 |
-
3. **Direct Management Experience**: Identify instances where the candidate has taken on roles involving management responsibilities, such as directly overseeing projects, setting goals for a team, conducting performance reviews, or handling direct reports. Quantify this experience in years, and provide specific examples when available.
|
136 |
-
4. **Relevant Skills and Qualifications**: List the candidate’s relevant skills and qualifications that align with the job description requirements. Mention specific technologies, methodologies, and leadership frameworks mentioned in the resume.
|
137 |
-
5. **Educational Background**: Summarize the candidate’s educational qualifications relevant to the role.
|
138 |
-
6. **Match Percentage Calculation**: Based on the candidate's experience and the keywords related to management and team leadership found in the job description, calculate a match percentage. Incorporate the years of leadership experience and skills alignment as factors in this match.
|
139 |
-
|
140 |
-
Provide a detailed output that includes:
|
141 |
-
- **Candidate's Name**: State the candidate's name.
|
142 |
-
- **Direct Team Leadership Experience (in years)**: Quantify and describe this experience.
|
143 |
-
- **Direct Management Experience (in years)**: Quantify and describe this experience with specific examples.
|
144 |
-
- **Relevant Skills and Qualifications**: List skills and qualifications relevant to management and team leadership.
|
145 |
-
- **Educational Background**: Provide a brief summary.
|
146 |
-
- **Match Percentage**: Calculate the match percentage (between 30%-100%) based on leadership and management experience.
|
147 |
-
|
148 |
-
Input:
|
149 |
-
- Resume Text: "{text}"
|
150 |
-
- Job Description: "{jd}"
|
151 |
-
"""
|
152 |
-
|
153 |
-
# Streamlit interface
|
154 |
-
st.title("Resume ATS Analysis Tool")
|
155 |
-
st.markdown("### Upload Resume and Job Description for Analysis")
|
156 |
-
|
157 |
-
# File uploader for resume PDF
|
158 |
-
uploaded_file = st.file_uploader("Upload Resume PDF", type=["pdf"])
|
159 |
-
|
160 |
-
# Input for job description
|
161 |
-
job_description = st.text_area("Job Description", height=200)
|
162 |
-
|
163 |
-
# Check if both file and job description are provided
|
164 |
-
if uploaded_file and job_description:
|
165 |
-
# Show Analyze button when both resume and job description are provided
|
166 |
-
analyze_button = st.button("Analyze")
|
167 |
-
|
168 |
-
if analyze_button:
|
169 |
-
# Extract text from the uploaded PDF
|
170 |
-
resume_text = input_pdf_text(uploaded_file)
|
171 |
-
|
172 |
-
# Extract contact info (name, email, contact)
|
173 |
-
name, email, contact = extract_contact_info(resume_text)
|
174 |
-
|
175 |
-
# Extract relevant skills and educational background
|
176 |
-
relevant_skills = extract_relevant_skills(resume_text)
|
177 |
-
educational_background = extract_educational_background(resume_text)
|
178 |
-
|
179 |
-
# Extract years of experience for Direct Team Leadership and Management
|
180 |
-
direct_team_leadership_years = extract_direct_team_leadership_years(resume_text)
|
181 |
-
direct_management_years = direct_team_leadership_years # Reuse extraction logic for simplicity
|
182 |
-
|
183 |
-
# Prepare the prompt with resume and job description text
|
184 |
-
prompt = input_prompt.format(text=resume_text, jd=job_description)
|
185 |
-
|
186 |
-
# Get the response from Gemini model
|
187 |
-
response_text = get_gemini_response(prompt)
|
188 |
-
|
189 |
-
# Clean up the response to remove unnecessary whitespace or formatting
|
190 |
-
response_text_clean = response_text.strip()
|
191 |
-
|
192 |
-
# Set the match percentage based on extracted data
|
193 |
-
match_percentage = calculate_match_percentage(direct_team_leadership_years, direct_management_years)
|
194 |
-
|
195 |
-
# Determine the Job Description Match Score
|
196 |
-
job_description_match_score = "High" if match_percentage >= 80 else "Moderate" if match_percentage >= 50 else "Low"
|
197 |
-
|
198 |
-
# Create a DataFrame with the extracted data
|
199 |
-
data = {
|
200 |
-
'Candidate_Name': [name],
|
201 |
-
'Email': [email],
|
202 |
-
'Contact': [contact],
|
203 |
-
'Direct_Team_Leadership_Experience_Years': [direct_team_leadership_years],
|
204 |
-
'Direct_Management_Experience_Years': [direct_management_years],
|
205 |
-
'Relevant_Skills_and_Qualifications': [", ".join(relevant_skills)],
|
206 |
-
'Educational_Background': [", ".join(educational_background)],
|
207 |
-
'Model_Response': [response_text_clean],
|
208 |
-
'Match_Percentage': [match_percentage],
|
209 |
-
'Job_Description_Match_Score': [job_description_match_score]
|
210 |
-
}
|
211 |
-
|
212 |
-
df = pd.DataFrame(data)
|
213 |
-
|
214 |
-
# Display the DataFrame as a table
|
215 |
-
st.subheader("Analysis Results")
|
216 |
-
st.dataframe(df)
|
217 |
-
|
218 |
-
# Save the DataFrame to a CSV file
|
219 |
-
csv_filename = "ATS_Analysis_Results.csv"
|
220 |
-
df.to_csv(csv_filename, index=False)
|
221 |
-
|
222 |
-
# Button to download CSV
|
223 |
-
st.download_button(
|
224 |
-
label="Download Results as CSV",
|
225 |
-
data=df.to_csv(index=False),
|
226 |
-
file_name=csv_filename,
|
227 |
-
mime="text/csv"
|
228 |
-
)
|
229 |
-
else:
|
230 |
-
st.warning("Please upload a resume and provide a job description for analysis.")
|
|
|
42 |
name = names[0] if names else "Not Available"
|
43 |
|
44 |
# Extract email using regex
|
45 |
+
email_match = re.search(r"[a-zA-Z0-9._%+-]+@[a-zAZ0-9.-]+\.[a-zAZ0-9.-]+", resume_text)
|
46 |
email = email_match.group(0) if email_match else "Not Available"
|
47 |
|
48 |
# Updated regex pattern for phone number extraction (more robust)
|
|
|
53 |
|
54 |
# Function to extract relevant skills from the resume text
|
55 |
def extract_relevant_skills(resume_text):
|
|
|
56 |
skills_section_keywords = ["skills", "technical skills", "competencies", "technologies", "tools"]
|
57 |
skills = []
|
58 |
|
|
|
67 |
|
68 |
return skills
|
69 |
|
70 |
+
# Function to extract leadership and management roles explicitly mentioned in the resume text
|
71 |
+
def extract_management_team_leadership(resume_text):
|
72 |
+
# Define a list of specific roles and leadership-related keywords
|
73 |
+
leadership_keywords = [
|
74 |
+
r"(team\s*lead)", r"(project\s*lead)", r"(team\s*manager)", r"(manager)", r"(supervisor)",
|
75 |
+
r"(director)", r"(head\s*of\s*department)", r"(chief\s*[a-zA-Z]+)", r"(lead\s*[a-zA-Z]+)",
|
76 |
+
r"(department\s*lead)", r"(leadership role)", r"(senior\s*manager)", r"(executive\s*director)"
|
77 |
+
]
|
78 |
+
|
79 |
+
leadership_phrases = []
|
80 |
+
|
81 |
+
# Join the leadership keywords into a single pattern (case-insensitive)
|
82 |
+
pattern = r"(?i)" + r"|".join(leadership_keywords)
|
83 |
+
|
84 |
+
# Find all leadership-related phrases in the resume text
|
85 |
+
matches = re.findall(pattern, resume_text)
|
86 |
+
|
87 |
+
# Clean and deduplicate matches
|
88 |
+
leadership_phrases = list(set([match.strip() for match in matches if match.strip()]))
|
89 |
+
|
90 |
+
return leadership_phrases
|
91 |
+
|
92 |
# Function to extract educational background from the resume text
|
93 |
def extract_educational_background(resume_text):
|
94 |
+
# List of common educational keywords across various categories
|
95 |
+
education_keywords = [
|
96 |
+
"bachelor", "master", "degree", "university", "college", "diploma", "certification", "high school",
|
97 |
+
"junior college", "postgraduate", "PhD", "doctorate", "MSc", "MBA", "MPhil", "doctor of medicine",
|
98 |
+
"diploma", "engineering", "nursing", "law", "medicine", "education", "social sciences", "public health",
|
99 |
+
"vocational", "trade school", "vocational school", "IB", "A-Level", "O-Level", "GCSE", "advanced placement",
|
100 |
+
"higher education", "online course", "MOOC", "Coursera", "edX", "skillshare", "online degree", "research",
|
101 |
+
"elementary school", "primary school", "secondary school", "high school", "secondary education",
|
102 |
+
"international baccalaureate", "college diploma", "associate degree", "graduate studies", "PhD",
|
103 |
+
"postdoctoral research", "master's degree", "bachelor's degree", "doctoral degree", "engineering degree",
|
104 |
+
"medicine degree", "law degree", "business degree", "computer science degree", "art degree", "social science degree",
|
105 |
+
"technical institute", "vocational training", "trade certification", "internship", "fellowship", "study abroad",
|
106 |
+
"research fellowship", "thesis", "dissertation", "capstone project", "e-learning", "blended learning",
|
107 |
+
"distance education", "remote learning", "hybrid learning", "continuing education", "adult education", "STEM",
|
108 |
+
"STEAM", "humanities", "bachelor of science", "bachelor of arts", "associate of science", "associate of arts",
|
109 |
+
"certificate of achievement", "certificate of completion", "online degree", "professional certification",
|
110 |
+
"academic achievement", "graduate diploma", "honorary degree", "dual degree", "honors program", "MOOC", "Coursera", "edX"
|
111 |
+
]
|
112 |
+
|
113 |
education = []
|
114 |
|
115 |
for keyword in education_keywords:
|
116 |
pattern = r"(?i)(%s)[^,]*[\.,;]" % re.escape(keyword)
|
117 |
matches = re.findall(pattern, resume_text)
|
118 |
+
|
119 |
for match in matches:
|
120 |
education.append(match.strip())
|
121 |
|
122 |
return education
|
123 |
|
124 |
+
# Streamlit Interface for File Upload
|
125 |
+
st.title('Resume Analysis and Job Matching Tool')
|
|
|
|
|
|
|
126 |
|
127 |
+
uploaded_file = st.file_uploader("Upload a Resume (PDF)", type="pdf")
|
128 |
+
job_description = st.text_area("Enter Job Description", height=200)
|
129 |
+
|
130 |
+
if uploaded_file and job_description:
|
131 |
+
# Extract text from uploaded PDF file
|
132 |
+
resume_text = input_pdf_text(uploaded_file)
|
|
|
|
|
133 |
|
134 |
+
# Extract educational background
|
135 |
+
education_details = extract_educational_background(resume_text)
|
136 |
|
137 |
+
# Extract name, email, and contact
|
138 |
+
name, email, contact = extract_contact_info(resume_text)
|
139 |
+
|
140 |
+
# Extract relevant skills
|
141 |
+
skills = extract_relevant_skills(resume_text)
|
142 |
+
|
143 |
+
# Extract leadership and management roles explicitly mentioned
|
144 |
+
leadership_roles = extract_management_team_leadership(resume_text)
|
145 |
+
|
146 |
+
# Extract expected years from job description
|
147 |
+
expected_years = extract_expected_years(job_description)
|
148 |
+
|
149 |
+
# Display results
|
150 |
+
st.write("### Candidate Information")
|
151 |
+
st.write(f"**Name:** {name}")
|
152 |
+
st.write(f"**Email:** {email}")
|
153 |
+
st.write(f"**Contact:** {contact}")
|
154 |
+
|
155 |
+
st.write("### Educational Background")
|
156 |
+
for detail in education_details:
|
157 |
+
st.write(f"- {detail}")
|
158 |
+
|
159 |
+
st.write("### Relevant Skills")
|
160 |
+
for skill in skills:
|
161 |
+
st.write(f"- {skill}")
|
162 |
+
|
163 |
+
st.write("### Leadership and Management Roles")
|
164 |
+
if leadership_roles:
|
165 |
+
for role in leadership_roles:
|
166 |
+
st.write(f"- {role}")
|
167 |
else:
|
168 |
+
st.write("No specific leadership or management roles mentioned.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|