From 39f0a10a06cbcf8ae5f31e77eab306a5566a0250 Mon Sep 17 00:00:00 2001 From: Yang Luo <121268261+yanglluo@users.noreply.github.com> Date: Sat, 25 Jul 2026 12:24:37 +0800 Subject: [PATCH 1/4] refactor: centralize score arithmetic --- score.py | 48 +++++++++++------------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/score.py b/score.py index 6baf3e7..1615b08 100644 --- a/score.py +++ b/score.py @@ -1,17 +1,6 @@ import os import sys import json - -# Fix for Windows Console Unicode errors -if sys.platform == "win32": - try: - sys.stdout.reconfigure(encoding='utf-8') - except AttributeError: - pass - -# Fix for Python 3.14 Protobuf TypeError -os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" - import logging import csv @@ -35,6 +24,7 @@ convert_blog_data_to_text, ) from config import DEVELOPMENT_MODE +from scoring import calculate_total_score logger = logging.getLogger(__name__) @@ -56,34 +46,18 @@ def print_evaluation_results( print("❌ No evaluation data available") return - # Calculate overall score - total_score = 0 - max_score = 0 - - if hasattr(evaluation, "scores") and evaluation.scores: - for category_name, category_data in evaluation.scores.model_dump().items(): - category_score = min(category_data["score"], category_data["max"]) - total_score += category_score - max_score += category_data["max"] - - # Log warning if score was capped - if category_score < category_data["score"]: - print( - f"⚠️ Warning: {category_name} score capped from {category_data['score']} to {category_score} (max: {category_data['max']})" - ) - - # Add bonus points - if hasattr(evaluation, "bonus_points") and evaluation.bonus_points: - total_score += evaluation.bonus_points.total + total_score, max_score, capped_scores, capped_at_maximum = calculate_total_score( + evaluation + ) - # Subtract deductions - if hasattr(evaluation, "deductions") and evaluation.deductions: - total_score -= evaluation.deductions.total + for category_name, category_data in evaluation.scores.model_dump().items(): + category_score = capped_scores[category_name] + if category_score < category_data["score"]: + print( + f"⚠️ Warning: {category_name} score capped from {category_data['score']} to {category_score} (max: {category_data['max']})" + ) - # Ensure total score doesn't exceed maximum possible score - max_possible_score = max_score + 20 # 120 (100 categories + 20 bonus) - if total_score > max_possible_score: - total_score = max_possible_score + if capped_at_maximum: print(f"⚠️ Warning: Total score capped at maximum possible value") # Overall Score From 1ec2847c640c7e5e556672e09877a2fa1ffc8607 Mon Sep 17 00:00:00 2001 From: Yang Luo <121268261+yanglluo@users.noreply.github.com> Date: Sat, 25 Jul 2026 12:24:43 +0800 Subject: [PATCH 2/4] refactor: add tested score helper --- scoring.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 scoring.py diff --git a/scoring.py b/scoring.py new file mode 100644 index 0000000..61ceedd --- /dev/null +++ b/scoring.py @@ -0,0 +1,38 @@ +"""Deterministic score arithmetic used by the resume evaluation CLI.""" + +from typing import Dict, Tuple + +from models import EvaluationData + +MAX_BONUS_POINTS = 20 + + +def calculate_total_score( + evaluation: EvaluationData, +) -> Tuple[float, int, Dict[str, float], bool]: + """Return total score, category maximum, capped category scores, and cap status. + + LLM output is validated by Pydantic, but category scores can still be above + their declared maximum. Keeping the cap arithmetic in one pure helper makes + the CLI behavior deterministic and easy to regression-test. + """ + + capped_scores: Dict[str, float] = {} + max_score = 0 + total_score = 0.0 + + for category_name, category_data in evaluation.scores.model_dump().items(): + capped_score = min(category_data["score"], category_data["max"]) + capped_scores[category_name] = capped_score + total_score += capped_score + max_score += category_data["max"] + + total_score += evaluation.bonus_points.total + total_score -= evaluation.deductions.total + + max_possible_score = max_score + MAX_BONUS_POINTS + capped_at_maximum = total_score > max_possible_score + if capped_at_maximum: + total_score = float(max_possible_score) + + return total_score, max_score, capped_scores, capped_at_maximum From 23851c419577c72203d28d6062c935a1a862f9ea Mon Sep 17 00:00:00 2001 From: Yang Luo <121268261+yanglluo@users.noreply.github.com> Date: Sat, 25 Jul 2026 12:24:48 +0800 Subject: [PATCH 3/4] test: cover score arithmetic --- test_scoring.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test_scoring.py diff --git a/test_scoring.py b/test_scoring.py new file mode 100644 index 0000000..b7985bd --- /dev/null +++ b/test_scoring.py @@ -0,0 +1,60 @@ +"""Regression tests for deterministic score arithmetic. + +Run with: python -m unittest test_scoring.py +""" + +import unittest + +from models import EvaluationData +from scoring import calculate_total_score + + +def evaluation(**overrides): + payload = { + "scores": { + "open_source": {"score": 10, "max": 35, "evidence": "evidence"}, + "self_projects": {"score": 20, "max": 30, "evidence": "evidence"}, + "production": {"score": 15, "max": 25, "evidence": "evidence"}, + "technical_skills": {"score": 8, "max": 10, "evidence": "evidence"}, + }, + "bonus_points": {"total": 5, "breakdown": "bonus"}, + "deductions": {"total": 2, "reasons": "deduction"}, + "key_strengths": ["strength"], + "areas_for_improvement": ["improvement"], + } + for key, value in overrides.items(): + payload[key] = value + return EvaluationData(**payload) + + +class ScoreArithmeticTests(unittest.TestCase): + def test_adds_categories_bonus_and_deductions(self): + total, maximum, capped, was_capped = calculate_total_score(evaluation()) + self.assertEqual(total, 56) + self.assertEqual(maximum, 100) + self.assertEqual(capped["open_source"], 10) + self.assertFalse(was_capped) + + def test_caps_category_scores_before_total(self): + item = evaluation() + item.scores.open_source.score = 99 + total, _, capped, _ = calculate_total_score(item) + self.assertEqual(capped["open_source"], 35) + self.assertEqual(total, 81) + + def test_caps_total_at_category_maximum_plus_bonus(self): + item = evaluation() + item.scores.open_source.score = 35 + item.scores.self_projects.score = 30 + item.scores.production.score = 25 + item.scores.technical_skills.score = 10 + item.bonus_points.total = 50 + item.deductions.total = 0 + total, maximum, _, was_capped = calculate_total_score(item) + self.assertEqual(maximum, 100) + self.assertEqual(total, 120) + self.assertTrue(was_capped) + + +if __name__ == "__main__": + unittest.main() From eb002dedebf9601cb037068514b7efa8600849b6 Mon Sep 17 00:00:00 2001 From: Yang Luo <121268261+yanglluo@users.noreply.github.com> Date: Sat, 25 Jul 2026 12:34:05 +0800 Subject: [PATCH 4/4] fix: preserve upstream compatibility setup --- score.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/score.py b/score.py index 1615b08..ce1d9c9 100644 --- a/score.py +++ b/score.py @@ -1,6 +1,17 @@ import os import sys import json + +# Fix for Windows Console Unicode errors +if sys.platform == "win32": + try: + sys.stdout.reconfigure(encoding='utf-8') + except AttributeError: + pass + +# Fix for Python 3.14 Protobuf TypeError +os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python" + import logging import csv