Akshay Chame
Sync files from GitHub repository
5e5e890
raw
history blame
13.6 kB
# Job Matching Logic
from typing import Dict, Any, List, Tuple
import re
from collections import Counter
class JobMatcher:
"""Utility class for matching LinkedIn profiles with job descriptions"""
def __init__(self):
self.weight_config = {
'skills': 0.4,
'experience': 0.3,
'keywords': 0.2,
'education': 0.1
}
self.skill_synonyms = {
'javascript': ['js', 'ecmascript', 'node.js', 'nodejs'],
'python': ['py', 'django', 'flask', 'fastapi'],
'react': ['reactjs', 'react.js'],
'angular': ['angularjs', 'angular.js'],
'machine learning': ['ml', 'ai', 'artificial intelligence'],
'database': ['db', 'sql', 'mysql', 'postgresql', 'mongodb']
}
def calculate_match_score(self, profile_data: Dict[str, Any], job_description: str) -> Dict[str, Any]:
"""
Calculate comprehensive match score between profile and job
Args:
profile_data (Dict[str, Any]): Cleaned profile data
job_description (str): Job description text
Returns:
Dict[str, Any]: Match analysis with scores and details
"""
job_requirements = self._parse_job_requirements(job_description)
# Calculate individual scores
skills_score = self._calculate_skills_match(
profile_data.get('skills', []),
job_requirements['skills']
)
experience_score = self._calculate_experience_match(
profile_data.get('experience', []),
job_requirements
)
keywords_score = self._calculate_keywords_match(
profile_data,
job_requirements['keywords']
)
education_score = self._calculate_education_match(
profile_data.get('education', []),
job_requirements
)
# Calculate weighted overall score
overall_score = (
skills_score['score'] * self.weight_config['skills'] +
experience_score['score'] * self.weight_config['experience'] +
keywords_score['score'] * self.weight_config['keywords'] +
education_score['score'] * self.weight_config['education']
)
return {
'overall_score': round(overall_score, 2),
'breakdown': {
'skills': skills_score,
'experience': experience_score,
'keywords': keywords_score,
'education': education_score
},
'recommendations': self._generate_match_recommendations(
skills_score, experience_score, keywords_score, education_score
),
'job_requirements': job_requirements
}
def find_skill_gaps(self, profile_skills: List[str], job_requirements: List[str]) -> Dict[str, List[str]]:
"""
Identify skill gaps between profile and job requirements
Args:
profile_skills (List[str]): Current profile skills
job_requirements (List[str]): Required job skills
Returns:
Dict[str, List[str]]: Missing and matching skills
"""
profile_skills_lower = [skill.lower() for skill in profile_skills]
job_skills_lower = [skill.lower() for skill in job_requirements]
# Find exact matches
matching_skills = []
missing_skills = []
for job_skill in job_skills_lower:
if job_skill in profile_skills_lower:
matching_skills.append(job_skill)
else:
# Check for synonyms
found_synonym = False
for profile_skill in profile_skills_lower:
if self._are_skills_similar(profile_skill, job_skill):
matching_skills.append(job_skill)
found_synonym = True
break
if not found_synonym:
missing_skills.append(job_skill)
return {
'matching_skills': matching_skills,
'missing_skills': missing_skills,
'match_percentage': len(matching_skills) / max(len(job_skills_lower), 1) * 100
}
def suggest_profile_improvements(self, match_analysis: Dict[str, Any]) -> List[str]:
"""
Generate specific improvement suggestions based on match analysis
Args:
match_analysis (Dict[str, Any]): Match analysis results
Returns:
List[str]: Improvement suggestions
"""
suggestions = []
breakdown = match_analysis['breakdown']
# Skills suggestions
if breakdown['skills']['score'] < 70:
missing_skills = breakdown['skills']['details']['missing_skills'][:3]
if missing_skills:
suggestions.append(
f"Add these high-priority skills: {', '.join(missing_skills)}"
)
# Experience suggestions
if breakdown['experience']['score'] < 60:
suggestions.append(
"Highlight more relevant experience in your current/previous roles"
)
suggestions.append(
"Add quantified achievements that demonstrate impact"
)
# Keywords suggestions
if breakdown['keywords']['score'] < 50:
suggestions.append(
"Incorporate more industry-specific keywords throughout your profile"
)
# Education suggestions
if breakdown['education']['score'] < 40:
suggestions.append(
"Consider adding relevant certifications or courses"
)
return suggestions
def _parse_job_requirements(self, job_description: str) -> Dict[str, Any]:
"""Parse job description to extract requirements"""
requirements = {
'skills': [],
'keywords': [],
'experience_years': 0,
'education_level': '',
'industry': '',
'role_type': ''
}
# Extract skills (common technical skills)
skill_patterns = [
r'\b(python|javascript|java|react|angular|node\.?js|sql|aws|docker|kubernetes)\b',
r'\b(machine learning|ai|data science|devops|full.?stack)\b',
r'\b(project management|agile|scrum|leadership)\b'
]
for pattern in skill_patterns:
matches = re.findall(pattern, job_description, re.IGNORECASE)
requirements['skills'].extend([match.lower() for match in matches])
# Extract experience years
exp_pattern = r'(\d+)\+?\s*years?\s*(?:of\s*)?experience'
exp_matches = re.findall(exp_pattern, job_description, re.IGNORECASE)
if exp_matches:
requirements['experience_years'] = int(exp_matches[0])
# Extract keywords (all meaningful words)
keywords = re.findall(r'\b[a-zA-Z]{3,}\b', job_description)
stop_words = {'the', 'and', 'for', 'with', 'you', 'will', 'are', 'have'}
requirements['keywords'] = [
word.lower() for word in keywords
if word.lower() not in stop_words
]
# Remove duplicates
requirements['skills'] = list(set(requirements['skills']))
requirements['keywords'] = list(set(requirements['keywords']))
return requirements
def _calculate_skills_match(self, profile_skills: List[str], job_skills: List[str]) -> Dict[str, Any]:
"""Calculate skills match score"""
if not job_skills:
return {'score': 100, 'details': {'matching_skills': [], 'missing_skills': []}}
skill_gap_analysis = self.find_skill_gaps(profile_skills, job_skills)
return {
'score': skill_gap_analysis['match_percentage'],
'details': skill_gap_analysis
}
def _calculate_experience_match(self, profile_experience: List[Dict], job_requirements: Dict) -> Dict[str, Any]:
"""Calculate experience match score"""
score = 0
details = {
'relevant_roles': 0,
'total_experience': 0,
'required_experience': job_requirements.get('experience_years', 0)
}
# Calculate total years of experience
total_years = 0
relevant_roles = 0
for exp in profile_experience:
duration_info = exp.get('duration_info', {})
if duration_info.get('duration_months'):
total_years += duration_info['duration_months'] / 12
# Check if role is relevant (simple keyword matching)
role_text = f"{exp.get('title', '')} {exp.get('description', '')}".lower()
job_keywords = job_requirements.get('keywords', [])
if any(keyword in role_text for keyword in job_keywords[:10]):
relevant_roles += 1
details['total_experience'] = round(total_years, 1)
details['relevant_roles'] = relevant_roles
# Calculate score based on experience and relevance
if job_requirements.get('experience_years', 0) > 0:
exp_ratio = min(total_years / job_requirements['experience_years'], 1.0)
score = exp_ratio * 70 + (relevant_roles / max(len(profile_experience), 1)) * 30
else:
score = 80 # Default good score if no specific experience required
return {
'score': round(score, 2),
'details': details
}
def _calculate_keywords_match(self, profile_data: Dict, job_keywords: List[str]) -> Dict[str, Any]:
"""Calculate keywords match score"""
if not job_keywords:
return {'score': 100, 'details': {'matched': 0, 'total': 0}}
# Extract all text from profile
profile_text = ""
for key, value in profile_data.items():
if isinstance(value, str):
profile_text += f" {value}"
elif isinstance(value, list):
for item in value:
if isinstance(item, dict):
profile_text += f" {' '.join(str(v) for v in item.values())}"
else:
profile_text += f" {item}"
profile_text = profile_text.lower()
# Count keyword matches
matched_keywords = 0
for keyword in job_keywords:
if keyword.lower() in profile_text:
matched_keywords += 1
score = (matched_keywords / len(job_keywords)) * 100
return {
'score': round(score, 2),
'details': {
'matched': matched_keywords,
'total': len(job_keywords),
'percentage': round(score, 2)
}
}
def _calculate_education_match(self, profile_education: List[Dict], job_requirements: Dict) -> Dict[str, Any]:
"""Calculate education match score"""
score = 70 # Default score
details = {
'has_degree': len(profile_education) > 0,
'degree_count': len(profile_education)
}
if profile_education:
score = 85 # Boost for having education
# Check for relevant fields
job_keywords = job_requirements.get('keywords', [])
for edu in profile_education:
edu_text = f"{edu.get('degree', '')} {edu.get('field', '')}".lower()
if any(keyword in edu_text for keyword in job_keywords[:5]):
score = 95
break
return {
'score': score,
'details': details
}
def _are_skills_similar(self, skill1: str, skill2: str) -> bool:
"""Check if two skills are similar using synonyms"""
skill1_lower = skill1.lower()
skill2_lower = skill2.lower()
# Check direct synonyms
for main_skill, synonyms in self.skill_synonyms.items():
if ((skill1_lower == main_skill or skill1_lower in synonyms) and
(skill2_lower == main_skill or skill2_lower in synonyms)):
return True
# Check partial matches
if skill1_lower in skill2_lower or skill2_lower in skill1_lower:
return True
return False
def _generate_match_recommendations(self, skills_score: Dict, experience_score: Dict,
keywords_score: Dict, education_score: Dict) -> List[str]:
"""Generate recommendations based on individual scores"""
recommendations = []
if skills_score['score'] < 60:
recommendations.append("Focus on developing missing technical skills")
if experience_score['score'] < 50:
recommendations.append("Highlight more relevant work experience")
if keywords_score['score'] < 40:
recommendations.append("Optimize profile with job-specific keywords")
if education_score['score'] < 60:
recommendations.append("Consider additional certifications or training")
return recommendations