Add codereview_env/graders/grader_utils.py
Browse files
codereview_env/graders/grader_utils.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Optional
|
| 2 |
+
from codereview_env.models import GroundTruthIssue, Action
|
| 3 |
+
|
| 4 |
+
def keyword_overlap(body: str, keywords: List[str]) -> float:
|
| 5 |
+
"""Returns 0.0–1.0 confidence score based on keyword coverage."""
|
| 6 |
+
if not body or not keywords:
|
| 7 |
+
return 0.5 # Neutral credit for missing body or keywords
|
| 8 |
+
|
| 9 |
+
body_lower = body.lower()
|
| 10 |
+
hits = sum(1 for kw in keywords if kw.lower() in body_lower)
|
| 11 |
+
|
| 12 |
+
# Roadmap logic: min(1.0, hits / max(4, len(keywords) * 0.6))
|
| 13 |
+
threshold = max(4, len(keywords) * 0.6)
|
| 14 |
+
return min(1.0, hits / threshold)
|
| 15 |
+
|
| 16 |
+
def find_best_match(action: Action, ground_truth: List[GroundTruthIssue], already_matched: set) -> Optional[GroundTruthIssue]:
|
| 17 |
+
"""Line-number match (exact-ish) OR category+file match."""
|
| 18 |
+
for gt in ground_truth:
|
| 19 |
+
if gt.id in already_matched:
|
| 20 |
+
continue
|
| 21 |
+
|
| 22 |
+
# Line number match within 3 lines
|
| 23 |
+
line_match = (action.line_number is not None and
|
| 24 |
+
abs(action.line_number - gt.line_number) <= 3)
|
| 25 |
+
|
| 26 |
+
# Category and filename match
|
| 27 |
+
cat_match = (action.category == gt.category and
|
| 28 |
+
action.filename == gt.filename)
|
| 29 |
+
|
| 30 |
+
if line_match or cat_match:
|
| 31 |
+
return gt
|
| 32 |
+
|
| 33 |
+
return None
|