fix: support final grade type and flexible repo name parsing

- Add GRADE_TYPE=final support for final_grade.json
- Fix assignment_id extraction for non-hw format (e.g. final-vibevault)
- Fix student_id extraction for xxx-stu_yyy format
- Change default language from python to java
This commit is contained in:
sit002 2025-12-02 10:09:29 +08:00
parent 183959f4a0
commit fa984b79f2

View File

@ -2,7 +2,7 @@
"""
创建完整的成绩元数据文件
grade.json llm_grade.json 生成 metadata.json
grade.json / final_grade.json / llm_grade.json 生成 metadata.json
包含所有详细信息未通过的测试各题详情等
"""
@ -20,20 +20,22 @@ def extract_student_id():
if student_id:
return student_id
# 从仓库名提取格式hw1-stu_sit001
# 从仓库名提取
# 支持格式org/assignment-stu_xxx 或 org/assignment-stu-xxx
repo = os.getenv("REPO", "")
if repo:
# 匹配 hw1-stu_xxxxx 格式
match = re.search(r'hw\d+-stu[_-]?([^/]+)', repo)
# 匹配 xxx-stu_yyy 或 xxx-stu-yyy 格式
match = re.search(r'-stu[_-]([a-zA-Z0-9_]+)$', repo)
if match:
return match.group(1)
# 也尝试匹配 stu_xxx 在路径中的情况
match = re.search(r'stu[_-]([a-zA-Z0-9_]+)', repo)
if match:
return match.group(1)
return None
def extract_assignment_id():
"""从环境变量或仓库名中提取作业 ID"""
# 优先从环境变量获取
@ -41,20 +43,94 @@ def extract_assignment_id():
if assignment_id:
return assignment_id
# 从仓库名提取格式hw1-stu_sit001 或 hw1-template
# 从仓库名提取
# 支持格式org/assignment-stu_xxx 或 org/assignment-template
repo = os.getenv("REPO", "")
if repo:
# 尝试匹配 hwX-stu_ 或 hwX-template
match = re.search(r'(hw\d+)-(?:stu|template)', repo)
if match:
return match.group(1)
# 取仓库名部分(去掉组织)
repo_name = repo.split("/")[-1] if "/" in repo else repo
# 移除 -stu_xxx 或 -template 后缀
assignment = re.sub(r'-stu[_-][a-zA-Z0-9_]+$', '', repo_name)
assignment = re.sub(r'-template$', '', assignment)
if assignment:
return assignment
# 如果只是 hwX 格式
match = re.search(r'(hw\d+)$', repo)
if match:
return match.group(1)
return "hw1" # 默认回退
return "unknown"
def create_final_metadata(final_grade_file='final_grade.json'):
"""从 final_grade.json 创建元数据(期末大作业专用)"""
try:
with open(final_grade_file, 'r', encoding='utf-8') as f:
final_data = json.load(f)
assignment_id = extract_assignment_id()
student_id = extract_student_id()
total_score = final_data.get("total_score", 0)
max_score = final_data.get("max_score", 100)
breakdown = final_data.get("breakdown", {})
# 构建各组成部分
components = []
# 编程测试部分
prog = breakdown.get("programming", {})
if prog:
prog_component = {
"type": "programming_java",
"score": prog.get("score", 0),
"max_score": prog.get("max_score", 80),
"details": {
"groups": prog.get("groups", {})
}
}
components.append(prog_component)
# REPORT.md 部分
report = breakdown.get("report", {})
if report:
report_component = {
"type": "llm_report",
"score": report.get("score", 0),
"max_score": report.get("max_score", 10),
"details": {
"flags": report.get("flags", [])
}
}
components.append(report_component)
# FRONTEND.md 部分
frontend = breakdown.get("frontend", {})
if frontend:
frontend_component = {
"type": "llm_frontend",
"score": frontend.get("score", 0),
"max_score": frontend.get("max_score", 10),
"details": {
"flags": frontend.get("flags", [])
}
}
components.append(frontend_component)
metadata = {
"version": "1.0",
"assignment": assignment_id,
"student_id": student_id,
"components": components,
"total_score": round(total_score, 2),
"total_max_score": max_score,
"timestamp": datetime.now().isoformat(),
"generator": "gitea-autograde"
}
return metadata
except Exception as e:
print(f"Error creating final metadata: {e}", file=sys.stderr)
return {}
def create_grade_metadata(grade_file='grade.json'):
"""从 grade.json 创建元数据,包含所有详细信息"""
@ -64,19 +140,20 @@ def create_grade_metadata(grade_file='grade.json'):
assignment_id = extract_assignment_id()
student_id = extract_student_id()
language = os.getenv("LANGUAGE", "python")
language = os.getenv("LANGUAGE", "java")
# 提取所有相关信息
final_score = grade_data.get("final_score", grade_data.get("score", 0))
final_score = grade_data.get("final_score", grade_data.get("total_score", grade_data.get("score", 0)))
base_score = grade_data.get("base_score", final_score)
penalty = grade_data.get("penalty", 0)
passed = grade_data.get("passed", 0)
total = grade_data.get("total", 0)
fails = grade_data.get("fails", [])
max_score = grade_data.get("max_score", 100)
test_framework = grade_data.get("test_framework", "pytest")
test_framework = grade_data.get("test_framework", "junit")
coverage = grade_data.get("coverage")
raw_score = grade_data.get("raw_score")
groups = grade_data.get("groups", {})
# 动态生成 type 字段
type_map = {
@ -99,7 +176,8 @@ def create_grade_metadata(grade_file='grade.json'):
"coverage": round(coverage, 2) if coverage else None,
"raw_score": round(raw_score, 2) if raw_score else None,
"failed_tests": fails,
"test_framework": test_framework
"test_framework": test_framework,
"groups": groups
}
}
@ -248,7 +326,15 @@ def main():
grade_type = os.getenv("GRADE_TYPE", "programming").lower()
grade_file_override = os.getenv("GRADE_FILE")
if grade_type == "llm":
if grade_type == "final":
# 期末大作业成绩(包含编程+报告)
final_file = grade_file_override or "final_grade.json"
if os.path.exists(final_file):
metadata = create_final_metadata(final_file)
else:
print(f"Error: {final_file} not found", file=sys.stderr)
metadata = {}
elif grade_type == "llm":
# LLM 成绩
llm_file = grade_file_override or "artifacts/llm_grade.json"
if os.path.exists(llm_file):
@ -279,4 +365,4 @@ def main():
if __name__ == "__main__":
main()
main()