Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,15 @@ def generate_resume():
# Step 2: Analyze job description
job_requirements = job_analyzer.analyze(job_description)

# Step 3: Generate tailored resume content
# Step 3: Refine GitHub projects based on job requirements
if "github" in profile_data and "repositories" in profile_data["github"]:
job_skills = job_requirements.get("skills", {})
relevant_projects = github_scraper.select_relevant_projects(
profile_data["github"]["repositories"], job_skills
)
profile_data["github"]["top_projects"] = relevant_projects

# Step 4: Generate tailored resume content
resume_content = resume_generator.generate(profile_data, job_requirements)

# Step 4: Compile to PDF
Expand Down
22 changes: 21 additions & 1 deletion app_production.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,27 @@ def generate_resume():
logger.error(f"[{request.request_id}] Job analysis failed: {e}")
raise ResumeGenerationError("Failed to analyze job description")

# Step 3: Generate tailored resume content
# Step 3: Refine GitHub projects based on job requirements
if "github" in profile_data and "repositories" in profile_data["github"]:
try:
logger.debug(
f"[{request.request_id}] Refining GitHub projects by relevance"
)
job_skills = job_requirements.get("skills", {})
relevant_projects = github_scraper.select_relevant_projects(
profile_data["github"]["repositories"], job_skills
)
profile_data["github"]["top_projects"] = relevant_projects
logger.debug(
f"[{request.request_id}] Refined to {len(relevant_projects)} relevant projects"
)
except Exception as e:
logger.warning(
f"[{request.request_id}] GitHub project refinement failed: {e}"
)
# Continue with default projects if refinement fails

# Step 4: Generate tailored resume content
try:
logger.debug(f"[{request.request_id}] Generating resume content")
resume_content = resume_generator.generate(profile_data, job_requirements)
Expand Down
60 changes: 60 additions & 0 deletions scrapers/github_scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,63 @@ def scrape_profile(self, username: str) -> Dict:
exc_info=True,
)
raise GitHubAPIError(f"Failed to scrape GitHub profile: {str(e)}")

def select_relevant_projects(
self, repos: List[Dict], job_skills: Dict[str, List[str]]
) -> List[Dict]:
"""
Select projects that best match the job requirements

Args:
repos: List of repositories
job_skills: Dictionary of required skills by category

Returns:
List of relevant repositories sorted by relevance
"""
if not repos:
return []

# Flatten all job skills into a single set for easy matching
all_required_skills = set()
for skills in job_skills.values():
if isinstance(skills, list):
all_required_skills.update([s.lower() for s in skills])

if not all_required_skills:
# Fallback to stars if no skills identified
return sorted(repos, key=lambda x: x.get("stars", 0), reverse=True)[
: config.GITHUB_MAX_TOP_PROJECTS
]

scored_repos = []
for repo in repos:
score = 0
repo_name = repo.get("name", "").lower()
repo_desc = (repo.get("description") or "").lower()
repo_lang = (repo.get("language") or "").lower()
repo_topics = [t.lower() for t in repo.get("topics", [])]

# Match language (High weight)
if repo_lang in all_required_skills:
score += 5

# Match topics (Medium weight)
for topic in repo_topics:
if topic in all_required_skills:
score += 3

# Match keywords in name/description (Low weight)
for skill in all_required_skills:
if skill in repo_name or skill in repo_desc:
score += 1

scored_repos.append((score, repo))

# Sort by score (desc), then stars (desc) as tie-breaker
sorted_repos = sorted(
scored_repos, key=lambda x: (x[0], x[1].get("stars", 0)), reverse=True
)

# Return only the repository dictionaries
return [repo for score, repo in sorted_repos[: config.GITHUB_MAX_TOP_PROJECTS]]