This commit is contained in:
2026-06-05 09:46:49 -06:00
parent 2b65acf91a
commit d7e04adf07
3 changed files with 29 additions and 47 deletions

View File

@@ -1,10 +1,9 @@
from django.db import models
from django.utils.timezone import now
from utils.models import JSONField
from utils.constants import ContestStatus, ContestType
from account.models import User
from utils.models import RichTextField
from utils.constants import ContestStatus, ContestType
from utils.models import JSONField, RichTextField
class Contest(models.Model):

View File

@@ -141,6 +141,8 @@ def build_evaluation_prompt(problem):
5. Mermaid节点ID由系统生成不是学生编写内容不要评价节点ID不要因节点ID扣分不要建议修改节点ID
6. feedback控制在100字以内只总结核心优点和主要问题
7. suggestions最多3条每条单独一行重要建议必须以【重点】开头普通建议不要加前缀
- 只针对学生流程图中真实存在的问题给建议若流程图已符合要求suggestions 留空字符串,禁止编造或凑数
- 不要建议学生去做他已经做对的事(如顺序/分支已正确,就不要再建议调整该顺序/分支)
8. criteria_details 中的 comment 保持简短每项不超过25字
评分等级:
@@ -149,17 +151,17 @@ def build_evaluation_prompt(problem):
- B级 (70-79分): 及格,基本正确但存在一些问题
- C级 (0-69分): 需要改进,存在明显问题
请以JSON格式返回评分结果
请以JSON格式返回评分结果以下仅为格式示例feedback/suggestions 的内容必须依据学生实际提交的流程图生成,不要照搬示例文字,尤其不要在没有循环的流程图中提及循环)
{{
"score": 85,
"grade": "A",
"feedback": "整体逻辑清楚,但循环出口表达不够明确。",
"suggestions": "【重点】补充循环结束条件\\n完善异常输入分支",
"feedback": "<根据实际流程图填写100字以内>",
"suggestions": "<根据实际问题填写;若无明显问题可留空字符串>",
"criteria_details": {{
"逻辑正确性": {{"score": 35, "max": 40, "comment": "逻辑基本正确"}},
"完整性": {{"score": 25, "max": 30, "comment": "缺少部分步骤"}},
"规范性": {{"score": 18, "max": 20, "comment": "符号使用规范"}},
"清晰度": {{"score": 8, "max": 10, "comment": "布局清晰"}}
"逻辑正确性": {{"score": 35, "max": 40, "comment": "<简短说明>"}},
"完整性": {{"score": 25, "max": 30, "comment": "<简短说明>"}},
"规范性": {{"score": 18, "max": 20, "comment": "<简短说明>"}},
"清晰度": {{"score": 8, "max": 10, "comment": "<简短说明>"}}
}}
}}
"""

View File

@@ -124,13 +124,9 @@ class TestCaseAPI(CSRFExemptAPIView, TestCaseZipProcessor):
with zipfile.ZipFile(file_name, "w") as file:
for test_case in name_list:
file.write(f"{test_case_dir}/{test_case}", test_case)
response = StreamingHttpResponse(
FileWrapper(open(file_name, "rb")), content_type="application/octet-stream"
)
response = StreamingHttpResponse(FileWrapper(open(file_name, "rb")), content_type="application/octet-stream")
response["Content-Disposition"] = (
f"attachment; filename=problem_{problem.id}_test_cases.zip"
)
response["Content-Disposition"] = f"attachment; filename=problem_{problem.id}_test_cases.zip"
response["Content-Length"] = os.path.getsize(file_name)
return response
@@ -203,9 +199,7 @@ class ProblemAPI(ProblemBase):
except Problem.DoesNotExist:
return self.error("Problem does not exist")
problems = Problem.objects.filter(contest_id__isnull=True).order_by(
"-create_time"
)
problems = Problem.objects.filter(contest_id__isnull=True).order_by("-create_time")
author = request.GET.get("author", "")
if author:
@@ -213,14 +207,10 @@ class ProblemAPI(ProblemBase):
keyword = request.GET.get("keyword", "").strip()
if keyword:
problems = problems.filter(
Q(title__icontains=keyword) | Q(_id__icontains=keyword)
)
problems = problems.filter(Q(title__icontains=keyword) | Q(_id__icontains=keyword))
if not user.can_mgmt_all_problem():
problems = problems.filter(created_by=user)
return self.success(
self.paginate_data(request, problems, ProblemAdminListSerializer)
)
return self.success(self.paginate_data(request, problems, ProblemAdminListSerializer))
@problem_permission_required
@validate_serializer(EditProblemSerializer)
@@ -237,11 +227,7 @@ class ProblemAPI(ProblemBase):
_id = data["_id"]
if not _id:
return self.error("Display ID is required")
if (
Problem.objects.exclude(id=problem_id)
.filter(_id=_id, contest_id__isnull=True)
.exists()
):
if Problem.objects.exclude(id=problem_id).filter(_id=_id, contest_id__isnull=True).exists():
return self.error("Display ID already exists")
error_info = self.common_checks(request)
@@ -342,9 +328,7 @@ class ContestProblemAPI(ProblemBase):
keyword = request.GET.get("keyword")
if keyword:
problems = problems.filter(title__contains=keyword)
return self.success(
self.paginate_data(request, problems, ProblemAdminListSerializer)
)
return self.success(self.paginate_data(request, problems, ProblemAdminListSerializer))
@validate_serializer(EditContestProblemSerializer)
def put(self, request):
@@ -367,11 +351,7 @@ class ContestProblemAPI(ProblemBase):
_id = data["_id"]
if not _id:
return self.error("Display ID is required")
if (
Problem.objects.exclude(id=problem_id)
.filter(_id=_id, contest=contest)
.exists()
):
if Problem.objects.exclude(id=problem_id).filter(_id=_id, contest=contest).exists():
return self.error("Display ID already exists")
error_info = self.common_checks(request)
@@ -500,7 +480,8 @@ class ProblemFlowchartAIGen(APIView):
},
{"role": "user", "content": python_code},
],
temperature=1.0,
temperature=0,
extra_body={"thinking": {"type": "disabled"}},
)
mermaid_code = response.choices[0].message.content
@@ -512,11 +493,13 @@ class StuckProblemsAPI(APIView):
def get(self, request):
from submission.models import JudgeStatus
failed_q = Q(result__in=[
JudgeStatus.WRONG_ANSWER,
JudgeStatus.COMPILE_ERROR,
JudgeStatus.RUNTIME_ERROR,
])
failed_q = Q(
result__in=[
JudgeStatus.WRONG_ANSWER,
JudgeStatus.COMPILE_ERROR,
JudgeStatus.RUNTIME_ERROR,
]
)
rows = (
Submission.objects.values("problem_id", "problem___id", "problem__title")
.annotate(
@@ -535,9 +518,7 @@ class StuckProblemsAPI(APIView):
"total": r["total"],
"failed": r["failed"],
"failed_users": r["failed_users"],
"ac_rate": round(r["accepted"] / r["total"] * 100, 1)
if r["total"]
else 0,
"ac_rate": round(r["accepted"] / r["total"] * 100, 1) if r["total"] else 0,
}
for r in rows
]