Bharath Vikas Tadepalli
First commit
6f0f8d0
Raw
History Blame Contribute Delete
8.45 kB
"""
Graders for Git Conflict Resolution Environment.
Each grader scores a resolved set of files from 0.0 to 1.0.
"""
import ast
import sys
import types
import importlib
import tempfile
import os
CONFLICT_MARKERS = ["<<<<<<<", "=======", ">>>>>>>"]
def has_conflict_markers(content: str) -> bool:
return any(marker in content for marker in CONFLICT_MARKERS)
def load_module_from_string(source: str, module_name: str = "resolved"):
"""Dynamically load a Python module from a source string."""
module = types.ModuleType(module_name)
exec(compile(source, module_name, "exec"), module.__dict__)
return module
def score_easy(resolved_files: dict) -> tuple[float, str]:
"""
Score the easy task: calculate_discount must handle both
the order_total > 100 and is_premium discounts together.
Returns (score 0.0-1.0, feedback string).
"""
if "store.py" not in resolved_files:
return 0.0, "Missing store.py in resolved files."
content = resolved_files["store.py"]
if has_conflict_markers(content):
return 0.0, "File still contains conflict markers."
try:
mod = load_module_from_string(content)
except SyntaxError as e:
return 0.0, f"Syntax error in resolved file: {e}"
except Exception as e:
return 0.0, f"Error loading module: {e}"
if not hasattr(mod, "calculate_discount"):
return 0.0, "Function calculate_discount not found."
fn = mod.calculate_discount
test_cases = [
((50, False), 50.0, "No discount for small non-premium order"),
((150, False), 135.0, "10% discount for large non-premium order"),
((50, True), 40.0, "20% discount for small premium order"),
((150, True), 105.0, "30% combined discount for large premium order"),
]
passed = 0
feedback_lines = []
for args, expected, description in test_cases:
try:
result = fn(*args)
if abs(result - expected) < 0.01:
passed += 1
else:
feedback_lines.append(f"FAIL [{description}]: got {result}, expected {expected}")
except Exception as e:
feedback_lines.append(f"ERROR [{description}]: {e}")
score = passed / len(test_cases)
feedback = f"Passed {passed}/{len(test_cases)} tests."
if feedback_lines:
feedback += " Issues: " + "; ".join(feedback_lines)
return round(score, 2), feedback
def score_medium(resolved_files: dict) -> tuple[float, str]:
"""
Score the medium task: process_order must validate inputs,
log, and compute loyalty points.
"""
if "orders.py" not in resolved_files:
return 0.0, "Missing orders.py in resolved files."
content = resolved_files["orders.py"]
if has_conflict_markers(content):
return 0.0, "File still contains conflict markers."
try:
mod = load_module_from_string(content)
except SyntaxError as e:
return 0.0, f"Syntax error: {e}"
except Exception as e:
return 0.0, f"Error loading module: {e}"
if not hasattr(mod, "process_order"):
return 0.0, "Function process_order not found."
fn = mod.process_order
passed = 0
total = 4
feedback_lines = []
# Test 1: normal order with loyalty points
try:
result = fn(1, ["apple"], 50)
if (result.get("loyalty_points") == 500 and
result.get("status") == "processed"):
passed += 1
else:
feedback_lines.append(f"FAIL [loyalty points]: got {result}")
except Exception as e:
feedback_lines.append(f"ERROR [normal order]: {e}")
# Test 2: fractional total
try:
result = fn(2, ["book", "pen"], 30.5)
if result.get("loyalty_points") == 305:
passed += 1
else:
feedback_lines.append(f"FAIL [fractional total]: loyalty_points={result.get('loyalty_points')}")
except Exception as e:
feedback_lines.append(f"ERROR [fractional total]: {e}")
# Test 3: empty items raises ValueError
try:
fn(1, [], 50)
feedback_lines.append("FAIL [empty items]: should raise ValueError")
except ValueError:
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [empty items]: raised {type(e).__name__} instead of ValueError")
# Test 4: zero total raises ValueError
try:
fn(1, ["x"], 0)
feedback_lines.append("FAIL [zero total]: should raise ValueError")
except ValueError:
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [zero total]: raised {type(e).__name__} instead of ValueError")
score = passed / total
feedback = f"Passed {passed}/{total} tests."
if feedback_lines:
feedback += " Issues: " + "; ".join(feedback_lines)
return round(score, 2), feedback
def score_hard(resolved_files: dict) -> tuple[float, str]:
"""
Score the hard task: all three files must work together.
User must have both role and session_token. Auth and API must be consistent.
"""
required = ["models.py", "auth.py", "api.py"]
for f in required:
if f not in resolved_files:
return 0.0, f"Missing {f} in resolved files."
if has_conflict_markers(resolved_files[f]):
return 0.0, f"{f} still contains conflict markers."
# Write files to a temp dir so they can import each other
with tempfile.TemporaryDirectory() as tmpdir:
for fname, content in resolved_files.items():
with open(os.path.join(tmpdir, fname), "w") as f:
f.write(content)
sys.path.insert(0, tmpdir)
try:
# Clear cached modules
for mod_name in ["models", "auth", "api"]:
if mod_name in sys.modules:
del sys.modules[mod_name]
import models as mod_models
import auth as mod_auth
import api as mod_api
except Exception as e:
sys.path.pop(0)
return 0.0, f"Import error: {e}"
finally:
sys.path.pop(0)
passed = 0
total = 7
feedback_lines = []
# Test 1: User has role attribute
try:
u = mod_models.User(1, "test")
assert hasattr(u, "role")
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [user has role]: {e}")
# Test 2: User has session_token attribute
try:
u = mod_models.User(1, "test")
assert hasattr(u, "session_token")
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [user has session_token]: {e}")
# Test 3: make_admin works
try:
u = mod_auth.login(1, "test")
u = mod_auth.make_admin(u)
assert u.is_admin()
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [make_admin]: {e}")
# Test 4: logout clears session
try:
u = mod_auth.login(1, "test")
u = mod_auth.logout(u)
assert not u.has_session()
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [logout]: {e}")
# Test 5: api login returns user with session
try:
u = mod_api.handle_request("login", 1, "test")
assert u is not None and u.has_session()
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [api login]: {e}")
# Test 6: api make_admin returns admin user
try:
u = mod_api.handle_request("make_admin", 1, "test")
assert u is not None and u.is_admin()
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [api make_admin]: {e}")
# Test 7: api logout returns user with no session
try:
u = mod_api.handle_request("logout", 1, "test")
assert u is not None and not u.has_session()
passed += 1
except Exception as e:
feedback_lines.append(f"FAIL [api logout]: {e}")
score = passed / total
feedback = f"Passed {passed}/{total} tests."
if feedback_lines:
feedback += " Issues: " + "; ".join(feedback_lines)
return round(score, 2), feedback
GRADERS = {
"easy": score_easy,
"medium": score_medium,
"hard": score_hard,
}
def grade(task_id: str, resolved_files: dict) -> tuple[float, str]:
if task_id not in GRADERS:
return 0.0, f"Unknown task: {task_id}"
return GRADERS[task_id](resolved_files)