精简 Problem 模型:移除 rule_type / total_score / io_mode
系统只用 ACM 模式且题目固定标准 IO,这三列已无实际用途: - rule_type / total_score:OI 专用,删除后 judge/dispatcher 的 OI 分支全部塌缩为 ACM - io_mode:判题改为固定发送 Standard IO 常量;languages.py 的 seccomp_rule 保留 dict 形态但改用字面量 key,不改判题机 wire 契约 - prompt 字段保留(预留给未来 AI 分析) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ from account.models import User
|
||||
from conf.models import JudgeServer
|
||||
from contest.models import ACMContestRank
|
||||
from options.options import SysOptions
|
||||
from problem.models import Problem, ProblemRuleType
|
||||
from problem.models import Problem
|
||||
from problem.utils import parse_problem_template
|
||||
from submission.models import JudgeStatus, Submission, is_accepted
|
||||
from utils.cache import cache
|
||||
@@ -114,22 +114,6 @@ class JudgeDispatcher(DispatcherBase):
|
||||
self.submission.statistic_info["time_cost"] = max([x["cpu_time"] for x in resp_data])
|
||||
self.submission.statistic_info["memory_cost"] = max([x["memory"] for x in resp_data])
|
||||
|
||||
# sum up the score in OI mode
|
||||
if self.problem.rule_type == ProblemRuleType.OI:
|
||||
score = 0
|
||||
try:
|
||||
for i in range(len(resp_data)):
|
||||
if resp_data[i]["result"] == JudgeStatus.ACCEPTED:
|
||||
resp_data[i]["score"] = self.problem.test_case_score[i]["score"]
|
||||
score += resp_data[i]["score"]
|
||||
else:
|
||||
resp_data[i]["score"] = 0
|
||||
except IndexError:
|
||||
logger.error(f"Index Error raised when summing up the score in problem {self.problem.id}")
|
||||
self.submission.statistic_info["score"] = 0
|
||||
return
|
||||
self.submission.statistic_info["score"] = score
|
||||
|
||||
def judge(self):
|
||||
language = self.submission.language
|
||||
sub_config = list(filter(lambda item: language == item["name"], SysOptions.languages))[0]
|
||||
@@ -147,7 +131,7 @@ class JudgeDispatcher(DispatcherBase):
|
||||
"max_memory": 1024 * 1024 * self.problem.memory_limit,
|
||||
"test_case_id": self.problem.test_case_id,
|
||||
"output": False,
|
||||
"io_mode": self.problem.io_mode,
|
||||
"io_mode": {"io_mode": "Standard IO", "input": "input.txt", "output": "output.txt"},
|
||||
}
|
||||
|
||||
with ChooseJudgeServer() as server:
|
||||
@@ -188,14 +172,11 @@ class JudgeDispatcher(DispatcherBase):
|
||||
self.submission.info = resp
|
||||
self._compute_statistic_info(resp["data"])
|
||||
error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"]))
|
||||
# ACM模式下,多个测试点全部正确则AC,否则取第一个错误的测试点的状态
|
||||
# OI模式下, 若多个测试点全部正确则AC, 若全部错误则取第一个错误测试点状态,否则为部分正确
|
||||
# 多个测试点全部正确则AC,否则取第一个错误的测试点的状态
|
||||
if not error_test_case:
|
||||
self.submission.result = JudgeStatus.ACCEPTED
|
||||
elif self.problem.rule_type == ProblemRuleType.ACM or len(error_test_case) == len(resp["data"]):
|
||||
self.submission.result = error_test_case[0]["result"]
|
||||
else:
|
||||
self.submission.result = JudgeStatus.PARTIALLY_ACCEPTED
|
||||
self.submission.result = error_test_case[0]["result"]
|
||||
|
||||
if self.submission.result == JudgeStatus.ACCEPTED:
|
||||
ast_rules = self.problem.ast_rules
|
||||
@@ -252,7 +233,6 @@ class JudgeDispatcher(DispatcherBase):
|
||||
problem.save(update_fields=["accepted_number", "statistic_info"])
|
||||
|
||||
profile = User.objects.select_for_update().get(id=self.submission.user_id).userprofile
|
||||
if problem.rule_type == ProblemRuleType.ACM:
|
||||
acm_problems_status = profile.acm_problems_status.get("problems", {})
|
||||
if not is_accepted(acm_problems_status[problem_id]["status"]):
|
||||
acm_problems_status[problem_id]["status"] = JudgeStatus.ACCEPTED if is_accepted(self.submission.result) else self.submission.result
|
||||
@@ -261,19 +241,6 @@ class JudgeDispatcher(DispatcherBase):
|
||||
profile.acm_problems_status["problems"] = acm_problems_status
|
||||
profile.save(update_fields=["accepted_number", "acm_problems_status"])
|
||||
|
||||
else:
|
||||
oi_problems_status = profile.oi_problems_status.get("problems", {})
|
||||
score = self.submission.statistic_info["score"]
|
||||
if not is_accepted(oi_problems_status[problem_id]["status"]):
|
||||
# minus last time score, add this tim score
|
||||
profile.add_score(this_time_score=score, last_time_score=oi_problems_status[problem_id]["score"])
|
||||
oi_problems_status[problem_id]["score"] = score
|
||||
oi_problems_status[problem_id]["status"] = JudgeStatus.ACCEPTED if is_accepted(self.submission.result) else self.submission.result
|
||||
if is_accepted(self.submission.result):
|
||||
profile.accepted_number += 1
|
||||
profile.oi_problems_status["problems"] = oi_problems_status
|
||||
profile.save(update_fields=["accepted_number", "oi_problems_status"])
|
||||
|
||||
def update_problem_status(self):
|
||||
result = str(self.submission.result)
|
||||
problem_id = str(self.problem.id)
|
||||
@@ -292,7 +259,6 @@ class JudgeDispatcher(DispatcherBase):
|
||||
user_profile = user.userprofile
|
||||
user_profile.submission_number = F("submission_number") + 1
|
||||
profile_status = JudgeStatus.ACCEPTED if is_accepted(self.submission.result) else self.submission.result
|
||||
if problem.rule_type == ProblemRuleType.ACM:
|
||||
acm_problems_status = user_profile.acm_problems_status.get("problems", {})
|
||||
if problem_id not in acm_problems_status:
|
||||
acm_problems_status[problem_id] = {"status": profile_status, "_id": self.problem._id}
|
||||
@@ -305,24 +271,6 @@ class JudgeDispatcher(DispatcherBase):
|
||||
user_profile.acm_problems_status["problems"] = acm_problems_status
|
||||
user_profile.save(update_fields=["submission_number", "accepted_number", "acm_problems_status"])
|
||||
|
||||
else:
|
||||
oi_problems_status = user_profile.oi_problems_status.get("problems", {})
|
||||
score = self.submission.statistic_info["score"]
|
||||
if problem_id not in oi_problems_status:
|
||||
user_profile.add_score(score)
|
||||
oi_problems_status[problem_id] = {"status": profile_status, "_id": self.problem._id, "score": score}
|
||||
if is_accepted(self.submission.result):
|
||||
user_profile.accepted_number += 1
|
||||
elif not is_accepted(oi_problems_status[problem_id]["status"]):
|
||||
# minus last time score, add this time score
|
||||
user_profile.add_score(this_time_score=score, last_time_score=oi_problems_status[problem_id]["score"])
|
||||
oi_problems_status[problem_id]["score"] = score
|
||||
oi_problems_status[problem_id]["status"] = profile_status
|
||||
if is_accepted(self.submission.result):
|
||||
user_profile.accepted_number += 1
|
||||
user_profile.oi_problems_status["problems"] = oi_problems_status
|
||||
user_profile.save(update_fields=["submission_number", "accepted_number", "oi_problems_status"])
|
||||
|
||||
def update_contest_problem_status(self):
|
||||
with transaction.atomic():
|
||||
user = User.objects.select_for_update().get(id=self.submission.user_id)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from problem.models import ProblemIOMode
|
||||
|
||||
default_env = ["LANG=en_US.UTF-8", "LANGUAGE=en_US:en", "LC_ALL=en_US.UTF-8"]
|
||||
|
||||
_c_lang_config = {
|
||||
@@ -27,7 +25,7 @@ int main() {
|
||||
"max_memory": 256 * 1024 * 1024,
|
||||
"compile_command": "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c17 {src_path} -lm -o {exe_path}",
|
||||
},
|
||||
"run": {"command": "{exe_path}", "seccomp_rule": {ProblemIOMode.STANDARD: "c_cpp", ProblemIOMode.FILE: "c_cpp_file_io"}, "env": default_env},
|
||||
"run": {"command": "{exe_path}", "seccomp_rule": {"Standard IO": "c_cpp", "File IO": "c_cpp_file_io"}, "env": default_env},
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +54,7 @@ int main() {
|
||||
"max_memory": 1024 * 1024 * 1024,
|
||||
"compile_command": "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++20 {src_path} -lm -o {exe_path}",
|
||||
},
|
||||
"run": {"command": "{exe_path}", "seccomp_rule": {ProblemIOMode.STANDARD: "c_cpp", ProblemIOMode.FILE: "c_cpp_file_io"}, "env": default_env},
|
||||
"run": {"command": "{exe_path}", "seccomp_rule": {"Standard IO": "c_cpp", "File IO": "c_cpp_file_io"}, "env": default_env},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ class SQLJudgeDispatcher(JudgeDispatcher):
|
||||
raise SQLProblemConfigError("测试点不是 SQL 类型,请重新上传 SQL 测试点压缩包")
|
||||
|
||||
init_scripts = []
|
||||
# 按 "1","2",… 自然序遍历,保证与 test_case_score 的下标对应(OI 计分依赖顺序)
|
||||
# 按 "1","2",… 自然序遍历,保证测试点顺序稳定
|
||||
for key in sorted(info["test_cases"].keys(), key=natural_sort_key):
|
||||
input_name = info["test_cases"][key]["input_name"]
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Generated by Django 5.2.3 on 2025-06-14 08:51
|
||||
|
||||
import django.db.models.deletion
|
||||
import problem.models
|
||||
import utils.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
@@ -47,7 +46,7 @@ class Migration(migrations.Migration):
|
||||
('last_update_time', models.DateTimeField(auto_now=True, null=True)),
|
||||
('time_limit', models.IntegerField()),
|
||||
('memory_limit', models.IntegerField()),
|
||||
('io_mode', models.JSONField(default=problem.models._default_io_mode)),
|
||||
('io_mode', models.JSONField(default=dict)),
|
||||
('spj', models.BooleanField(default=False)),
|
||||
('spj_language', models.TextField(null=True)),
|
||||
('spj_code', models.TextField(null=True)),
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 6.0.4 on 2026-07-04 17:27
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('problem', '0012_problem_sql_display'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='problem',
|
||||
name='io_mode',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='problem',
|
||||
name='rule_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='problem',
|
||||
name='total_score',
|
||||
),
|
||||
]
|
||||
@@ -13,24 +13,6 @@ class ProblemTag(models.Model):
|
||||
db_table = "problem_tag"
|
||||
|
||||
|
||||
class ProblemRuleType(models.TextChoices):
|
||||
ACM = "ACM", "ACM"
|
||||
OI = "OI", "OI"
|
||||
|
||||
|
||||
class ProblemIOMode(models.TextChoices):
|
||||
STANDARD = "Standard IO", "Standard IO"
|
||||
FILE = "File IO", "File IO"
|
||||
|
||||
|
||||
def _default_io_mode():
|
||||
return {
|
||||
"io_mode": ProblemIOMode.STANDARD,
|
||||
"input": "input.txt",
|
||||
"output": "output.txt",
|
||||
}
|
||||
|
||||
|
||||
class Problem(models.Model):
|
||||
# display ID
|
||||
_id = models.TextField(db_index=True)
|
||||
@@ -58,18 +40,14 @@ class Problem(models.Model):
|
||||
time_limit = models.IntegerField()
|
||||
# MB
|
||||
memory_limit = models.IntegerField()
|
||||
# io mode
|
||||
io_mode = models.JSONField(default=_default_io_mode)
|
||||
rule_type = models.TextField(choices=ProblemRuleType.choices)
|
||||
visible = models.BooleanField(default=True, db_default=True)
|
||||
difficulty = models.TextField(choices=Difficulty.choices)
|
||||
tags = models.ManyToManyField(ProblemTag)
|
||||
source = models.TextField(null=True)
|
||||
# 预留:题目考察知识点,供未来 AI 分析使用(当前未接线)
|
||||
prompt = models.TextField(null=True)
|
||||
# [{language: "python", code: "..."}]
|
||||
answers = models.JSONField(null=True)
|
||||
# for OI mode
|
||||
total_score = models.IntegerField(default=0, db_default=0)
|
||||
submission_number = models.BigIntegerField(default=0, db_default=0)
|
||||
accepted_number = models.BigIntegerField(default=0, db_default=0)
|
||||
# {JudgeStatus.ACCEPTED: 3, JudgeStatus.WRONG_ANSWER: 11}, the number means count
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import re
|
||||
|
||||
from django import forms
|
||||
|
||||
from utils.api import UsernameSerializer, serializers
|
||||
@@ -9,7 +7,7 @@ from utils.serializers import (
|
||||
LanguageNameMultiChoiceField,
|
||||
)
|
||||
|
||||
from .models import Problem, ProblemIOMode, ProblemRuleType, ProblemTag
|
||||
from .models import Problem, ProblemTag
|
||||
from .utils import parse_problem_template
|
||||
|
||||
|
||||
@@ -48,20 +46,6 @@ class SQLTestCasePreviewSerializer(serializers.Serializer):
|
||||
mode = serializers.ChoiceField(choices=["query", "modify"])
|
||||
|
||||
|
||||
class ProblemIOModeSerializer(serializers.Serializer):
|
||||
io_mode = serializers.ChoiceField(choices=ProblemIOMode.choices)
|
||||
input = serializers.CharField()
|
||||
output = serializers.CharField()
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs["input"] == attrs["output"]:
|
||||
raise serializers.ValidationError("Invalid io mode")
|
||||
for item in (attrs["input"], attrs["output"]):
|
||||
if not re.match("^[a-zA-Z0-9.]+$", item):
|
||||
raise serializers.ValidationError("Invalid io file name format")
|
||||
return attrs
|
||||
|
||||
|
||||
class CreateOrEditProblemSerializer(serializers.Serializer):
|
||||
_id = serializers.CharField(max_length=32, allow_blank=True, allow_null=True)
|
||||
title = serializers.CharField(max_length=1024)
|
||||
@@ -75,8 +59,6 @@ class CreateOrEditProblemSerializer(serializers.Serializer):
|
||||
memory_limit = serializers.IntegerField(min_value=1, max_value=1024)
|
||||
languages = LanguageNameMultiChoiceField()
|
||||
template = serializers.DictField(child=serializers.CharField(min_length=1))
|
||||
rule_type = serializers.ChoiceField(choices=ProblemRuleType.choices)
|
||||
io_mode = ProblemIOModeSerializer()
|
||||
visible = serializers.BooleanField()
|
||||
difficulty = serializers.ChoiceField(choices=Difficulty.choices)
|
||||
tags = serializers.ListField(child=serializers.CharField(max_length=32), allow_empty=False)
|
||||
|
||||
@@ -19,7 +19,7 @@ from utils.api import APIError, APIView, CSRFExemptAPIView, validate_serializer
|
||||
from utils.openai import get_ai_client
|
||||
from utils.shortcuts import natural_sort_key, rand_str
|
||||
|
||||
from ..models import Problem, ProblemRuleType, ProblemTag
|
||||
from ..models import Problem, ProblemTag
|
||||
from ..serializers import (
|
||||
AddContestProblemSerializer,
|
||||
ContestProblemMakePublicSerializer,
|
||||
@@ -195,14 +195,6 @@ class TestCaseAPI(CSRFExemptAPIView, TestCaseZipProcessor):
|
||||
class ProblemBase(APIView):
|
||||
def common_checks(self, request):
|
||||
data = request.data
|
||||
if data["rule_type"] == ProblemRuleType.OI:
|
||||
total_score = 0
|
||||
for item in data["test_case_score"]:
|
||||
if item["score"] <= 0:
|
||||
return "Invalid score"
|
||||
else:
|
||||
total_score += item["score"]
|
||||
data["total_score"] = total_score
|
||||
data["languages"] = list(data["languages"])
|
||||
|
||||
# SQL 题校验:.sql 测试点与 .in/.out 沙箱判题互斥,SQL 必须是唯一语言
|
||||
|
||||
@@ -57,7 +57,7 @@ class SubmissionModelSerializer(serializers.ModelSerializer):
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
# 不显示submission info的serializer, 用于ACM rule_type
|
||||
# 不显示submission info的serializer, 用于非管理员查看他人提交
|
||||
class SubmissionSafeModelSerializer(serializers.ModelSerializer):
|
||||
problem = serializers.SlugRelatedField(read_only=True, slug_field="_id")
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from judge.tasks import judge_task
|
||||
from options.options import SysOptions
|
||||
|
||||
# from judge.dispatcher import JudgeDispatcher
|
||||
from problem.models import Problem, ProblemRuleType
|
||||
from problem.models import Problem
|
||||
from utils.api import APIView, AsyncAPIView, validate_serializer
|
||||
from utils.cache import cache
|
||||
from utils.captcha import Captcha
|
||||
@@ -116,10 +116,7 @@ class SubmissionAPI(AsyncAPIView):
|
||||
if not submission.check_user_permission(request.user):
|
||||
return self.error("No permission for this submission")
|
||||
|
||||
if (
|
||||
submission.problem.rule_type == ProblemRuleType.OI
|
||||
or request.user.is_admin_role()
|
||||
):
|
||||
if request.user.is_admin_role():
|
||||
submission_data = await self.async_serialize_data(SubmissionModelSerializer, submission)
|
||||
else:
|
||||
submission_data = await self.async_serialize_data(SubmissionSafeModelSerializer, submission)
|
||||
|
||||
@@ -12,7 +12,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oj.settings")
|
||||
django.setup()
|
||||
from django.conf import settings
|
||||
from account.models import User, UserProfile, AdminType, ProblemPermission
|
||||
from problem.models import Problem, ProblemTag, ProblemRuleType
|
||||
from problem.models import Problem, ProblemTag
|
||||
from utils.constants import Difficulty
|
||||
|
||||
admin_type_map = {0: AdminType.REGULAR_USER, 1: AdminType.STUDENT_ADMIN, 2: AdminType.SUPER_ADMIN}
|
||||
@@ -154,7 +154,6 @@ def import_problems():
|
||||
print("%s test_case files don't exist, omitted" % data["title"])
|
||||
continue
|
||||
data["test_case_score"] = test_case_score
|
||||
data["rule_type"] = ProblemRuleType.ACM
|
||||
data["template"] = {}
|
||||
data.pop("total_submit_number")
|
||||
data.pop("total_accepted_number")
|
||||
|
||||
Reference in New Issue
Block a user