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
5 changes: 1 addition & 4 deletions starter_code/expense_tracker.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
"""
expense_tracker.py
==================
Project: Personal Expense Tracker
Difficulty: Beginner
Skills: Python, CSV module, datetime module
Expand Down Expand Up @@ -322,4 +319,4 @@ def main():


if __name__ == "__main__":
main()
main()
4 changes: 3 additions & 1 deletion utils/file_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import os

# Absolute path to the starter_code directory
STARTER_CODE_DIR = os.path.join(os.path.dirname(__file__), "..", "starter_code")
STARTER_CODE_DIR = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "starter_code")
)


def resolve_starter_file(project):
Expand Down
23 changes: 16 additions & 7 deletions utils/recommender.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
# Maximum number of recommendations returned to the user
MAX_RESULTS = 3

# Point weights for each matching criterion
WEIGHT_SKILL = 3 # Skill matches carry the most influence
WEIGHT_LEVEL = 2 # Experience level is the next strongest signal
WEIGHT_INTEREST = 2 # Area of interest is equally important as level
WEIGHT_TIME = 1 # Time availability is a tiebreaker
# Scoring weights used by the recommendation engine.
# Higher weights mean that criterion has more influence
# on the final recommendation score.
WEIGHT_SKILL = 3 # Skills are weighted highest because they best reflect project compatibility
WEIGHT_LEVEL = 2 # Matching experience level helps avoid projects that are too easy or too difficult
WEIGHT_INTEREST = 2 # Interest alignment improves recommendation relevance
WEIGHT_TIME = 1 # Time availability acts as a smaller tie-breaker factor


def parse_skills(skills_string):
Expand All @@ -38,7 +40,11 @@ def score_single_project(project, user_skills, level, interest, time_availabilit

# Compare user's skills against the project's required skills
project_skills = [s.lower() for s in project.get("skills", [])]
matched_skills = sum(1 for skill in user_skills if skill in project_skills)
# Count how many user skills overlap with the
# skills required by the current project.
matched_skills = sum(1 for skill in user_skills if skill in project_skills)
# Add weighted points based on the number of matching skills.
# More overlapping skills result in a higher recommendation score.
score += matched_skills * WEIGHT_SKILL

# Award points for each additional matching criterion
Expand Down Expand Up @@ -74,10 +80,13 @@ def get_recommendations(skills_string, level, interest, time_availability):
score = score_single_project(
project, user_skills, level, interest, time_availability
)
# Ignore projects with a score of 0 since they
# have no meaningful overlap with the user's inputs.
if score > 0:
scored_projects.append({"project": project, "score": score})

# Sort so the highest-scoring project appears first
# Sort projects in descending order so the
# most relevant recommendations appear first.
scored_projects.sort(key=lambda item: item["score"], reverse=True)

# Return only the project dicts, not the score metadata
Expand Down
Loading