From 9535b64a4b97823cc293d5509c3b7546bd6fa469 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Sat, 4 Jul 2026 11:40:38 -0600 Subject: [PATCH] =?UTF-8?q?=E7=B2=BE=E7=AE=80=20Problem=20=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=EF=BC=9A=E7=A7=BB=E9=99=A4=20rule=5Ftype=20/=20total?= =?UTF-8?q?=5Fscore=20/=20io=5Fmode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 系统只用 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 --- judge/dispatcher.py | 96 +++++-------------- judge/languages.py | 6 +- judge/sql_dispatcher.py | 2 +- problem/migrations/0001_initial.py | 3 +- ..._mode_remove_problem_rule_type_and_more.py | 25 +++++ problem/models.py | 24 +---- problem/serializers.py | 20 +--- problem/views/admin.py | 10 +- submission/serializers.py | 2 +- submission/views/oj.py | 7 +- utils/migrate_data.py | 3 +- 11 files changed, 58 insertions(+), 140 deletions(-) create mode 100644 problem/migrations/0013_remove_problem_io_mode_remove_problem_rule_type_and_more.py diff --git a/judge/dispatcher.py b/judge/dispatcher.py index 323d66a..19f33a8 100644 --- a/judge/dispatcher.py +++ b/judge/dispatcher.py @@ -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,27 +233,13 @@ 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 - if is_accepted(self.submission.result): - profile.accepted_number += 1 - 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"]) + 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 + if is_accepted(self.submission.result): + profile.accepted_number += 1 + profile.acm_problems_status["problems"] = acm_problems_status + profile.save(update_fields=["accepted_number", "acm_problems_status"]) def update_problem_status(self): result = str(self.submission.result) @@ -292,36 +259,17 @@ 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} - if is_accepted(self.submission.result): - user_profile.accepted_number += 1 - elif not is_accepted(acm_problems_status[problem_id]["status"]): - acm_problems_status[problem_id]["status"] = profile_status - if is_accepted(self.submission.result): - user_profile.accepted_number += 1 - 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"]) + 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} + if is_accepted(self.submission.result): + user_profile.accepted_number += 1 + elif not is_accepted(acm_problems_status[problem_id]["status"]): + acm_problems_status[problem_id]["status"] = profile_status + if is_accepted(self.submission.result): + user_profile.accepted_number += 1 + user_profile.acm_problems_status["problems"] = acm_problems_status + user_profile.save(update_fields=["submission_number", "accepted_number", "acm_problems_status"]) def update_contest_problem_status(self): with transaction.atomic(): diff --git a/judge/languages.py b/judge/languages.py index 31aa8e0..236a01b 100644 --- a/judge/languages.py +++ b/judge/languages.py @@ -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}, } diff --git a/judge/sql_dispatcher.py b/judge/sql_dispatcher.py index d357b52..e0539ca 100644 --- a/judge/sql_dispatcher.py +++ b/judge/sql_dispatcher.py @@ -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: diff --git a/problem/migrations/0001_initial.py b/problem/migrations/0001_initial.py index 3fedc53..3df6d24 100644 --- a/problem/migrations/0001_initial.py +++ b/problem/migrations/0001_initial.py @@ -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)), diff --git a/problem/migrations/0013_remove_problem_io_mode_remove_problem_rule_type_and_more.py b/problem/migrations/0013_remove_problem_io_mode_remove_problem_rule_type_and_more.py new file mode 100644 index 0000000..925eb8b --- /dev/null +++ b/problem/migrations/0013_remove_problem_io_mode_remove_problem_rule_type_and_more.py @@ -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', + ), + ] diff --git a/problem/models.py b/problem/models.py index 7741671..a2f84ee 100644 --- a/problem/models.py +++ b/problem/models.py @@ -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 diff --git a/problem/serializers.py b/problem/serializers.py index ee98f9c..617acc1 100644 --- a/problem/serializers.py +++ b/problem/serializers.py @@ -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) diff --git a/problem/views/admin.py b/problem/views/admin.py index f1362a9..c63a753 100644 --- a/problem/views/admin.py +++ b/problem/views/admin.py @@ -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 必须是唯一语言 diff --git a/submission/serializers.py b/submission/serializers.py index c146e00..7c0dd3e 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -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") diff --git a/submission/views/oj.py b/submission/views/oj.py index 24f266d..4c8bb11 100644 --- a/submission/views/oj.py +++ b/submission/views/oj.py @@ -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) diff --git a/utils/migrate_data.py b/utils/migrate_data.py index bf05cd0..36ab006 100644 --- a/utils/migrate_data.py +++ b/utils/migrate_data.py @@ -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")