Akshay Chame
π Add LinkedIn Profile Enhancer Streamlit app with all agents and dependencies
035c4af | # 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 | |