diff --git a/contest/models.py b/contest/models.py index bda6c6c0..80ac92ab 100644 --- a/contest/models.py +++ b/contest/models.py @@ -84,8 +84,8 @@ class ACMContestRank(ContestRank): class OIContestRank(ContestRank): total_score = models.IntegerField(default=0) - # {23: {"score": 80, "total_score": 100}} - # key is problem id + # {23: 333}} + # key is problem id, value is current score submission_info = JSONField(default={}) class Meta: diff --git a/judge/dispatcher.py b/judge/dispatcher.py index edf2e2f6..1488621f 100644 --- a/judge/dispatcher.py +++ b/judge/dispatcher.py @@ -12,7 +12,7 @@ from account.models import User from conf.models import JudgeServer, JudgeServerToken from contest.models import ContestRuleType, ACMContestRank, OIContestRank from judge.languages import languages -from problem.models import Problem, ProblemRuleType, ContestProblem +from problem.models import Problem, ProblemRuleType from submission.models import JudgeStatus, Submission from utils.cache import judge_cache from utils.constants import CacheKey @@ -35,12 +35,13 @@ class JudgeDispatcher(object): self.token = hashlib.sha256(token.encode("utf-8")).hexdigest() self.redis_conn = judge_cache self.submission = Submission.objects.get(pk=submission_id) - if self.submission.contest_id: - self.problem = ContestProblem.objects.select_related("contest") \ - .get(_id=problem_id, contest_id=self.submission.contest_id) + self.contest_id = self.submission.contest_id + if self.contest_id: + self.problem = Problem.objects.select_related("contest") \ + .get(id=problem_id, contest_id=self.contest_id) self.contest = self.problem.contest else: - self.problem = Problem.objects.get(_id=problem_id) + self.problem = Problem.objects.get(id=problem_id) def _request(self, url, data=None): kwargs = {"headers": {"X-Judge-Server-Token": self.token, @@ -72,10 +73,28 @@ class JudgeDispatcher(object): server.used_instance_number = F("task_number") - 1 server.save() + def _compute_statistic_info(self, resp_data): + # 用时和内存占用保存为多个测试点中最长的那个 + 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: + score += self.problem.test_case_score[i]["score"] + 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, output=False): server = self.choose_judge_server() if not server: - data = {"submission_id": self.submission.id, "problem_id": self.problem._id} + data = {"submission_id": self.submission.id, "problem_id": self.problem.id} self.redis_conn.lpush(CacheKey.waiting_queue, json.dumps(data)) return @@ -108,11 +127,7 @@ class JudgeDispatcher(object): self.submission.result = JudgeStatus.COMPILE_ERROR self.submission.statistic_info["err_info"] = resp["data"] else: - # 用时和内存占用保存为多个测试点中最长的那个 - 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"]]) - # todo OI statistic_info["score"] - + self._compute_statistic_info(resp["data"]) error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"])) # ACM模式下,多个测试点全部正确则AC,否则取第一个错误的测试点的状态 # OI模式下, 若多个测试点全部正确则AC, 若全部错误则取第一个错误测试点状态,否则为部分正确 @@ -126,7 +141,7 @@ class JudgeDispatcher(object): self.release_judge_res(server.id) self.update_problem_status() - if self.submission.contest_id: + if self.contest_id: self.update_contest_rank() # 至此判题结束,尝试处理任务队列中剩余的任务 @@ -141,15 +156,11 @@ class JudgeDispatcher(object): def update_problem_status(self): with transaction.atomic(): # prepare problem and user_profile - if self.submission.contest_id: - problem = ContestProblem.objects.select_for_update().get(contest_id=self.contest.id, - _id=self.problem._id) - else: - problem = Problem.objects.select_for_update().get(_id=self.problem._id) + problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id) problem_info = problem.statistic_info user = User.objects.select_for_update().select_for_update("userprofile").get(id=self.submission.user_id) user_profile = user.userprofile - if self.submission.contest_id: + if self.contest_id: key = "contest_problems" else: key = "problems" @@ -159,7 +170,7 @@ class JudgeDispatcher(object): # update submission and accepted number counter # only when submission is not in contest, we update user profile, # in other words, users' submission in a contest will not be counted in user profile - if not self.submission.contest_id: + if not self.contest_id: user_profile.submission_number += 1 if self.submission.result == JudgeStatus.ACCEPTED: user_profile.accepted_number += 1 @@ -167,7 +178,7 @@ class JudgeDispatcher(object): if self.submission.result == JudgeStatus.ACCEPTED: problem.accepted_number += 1 - problem_id = str(self.problem._id) + problem_id = str(self.problem.id) if self.problem.rule_type == ProblemRuleType.ACM: # update acm problem info result = str(self.submission.result) @@ -197,13 +208,13 @@ class JudgeDispatcher(object): oi_problems_status[problem_id] = score else: # minus last time score, add this time score - user_profile.add_score(score, oi_problems_status[problem_id]) + user_profile.add_score(this_time_score=score, last_time_score=oi_problems_status[problem_id]) oi_problems_status[problem_id] = score user_profile.oi_problems_status[key] = oi_problems_status problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"]) - user_profile.save( - update_fields=["submission_number", "accepted_number", "acm_problems_status", "oi_problems_status"]) + user_profile.save(update_fields=[ + "submission_number", "accepted_number", "acm_problems_status", "oi_problems_status"]) def update_contest_rank(self): if self.contest.real_time_rank: @@ -221,7 +232,7 @@ class JudgeDispatcher(object): def _update_acm_contest_rank(self, rank): info = rank.submission_info.get(str(self.submission.problem_id)) # 因前面更改过,这里需要重新获取 - problem = ContestProblem.objects.get(contest_id=self.contest.id, _id=self.problem._id) + problem = Problem.objects.get(contest_id=self.contest_id, _id=self.problem.id) # 此题提交过 if info: if info["is_ac"]: @@ -258,4 +269,10 @@ class JudgeDispatcher(object): rank.save() def _update_oi_contest_rank(self, rank): - pass + problem_id = str(self.submission.problem_id) + current_score = self.submission.statistic_info["score"] + last_score = rank.submission_info.get(problem_id) + if last_score: + rank.total_score = rank.total_score - last_score + current_score + rank.submission_info[problem_id] = current_score + rank.save() diff --git a/problem/migrations/0008_auto_20170923_1318.py b/problem/migrations/0008_auto_20170923_1318.py new file mode 100644 index 00000000..4f5bb996 --- /dev/null +++ b/problem/migrations/0008_auto_20170923_1318.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-09-23 13:18 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0005_auto_20170823_0918'), + ('problem', '0006_auto_20170823_0918'), + ] + + operations = [ + migrations.AddField( + model_name='contestproblem', + name='total_score', + field=models.IntegerField(blank=True, default=0), + ), + migrations.AddField( + model_name='problem', + name='total_score', + field=models.IntegerField(blank=True, default=0), + ), + migrations.AlterUniqueTogether( + name='contestproblem', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='contestproblem', + name='contest', + ), + migrations.RemoveField( + model_name='contestproblem', + name='created_by', + ), + migrations.RemoveField( + model_name='contestproblem', + name='tags', + ), + migrations.AddField( + model_name='problem', + name='contest', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contest.Contest'), + preserve_default=False, + ), + migrations.AddField( + model_name='problem', + name='is_public', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='problem', + name='_id', + field=models.CharField(db_index=True, max_length=24), + ), + migrations.AlterUniqueTogether( + name='problem', + unique_together=set([('_id', 'contest')]), + ), + migrations.DeleteModel( + name='ContestProblem', + ), + ] diff --git a/problem/models.py b/problem/models.py index c4463172..0e9c5e2f 100644 --- a/problem/models.py +++ b/problem/models.py @@ -24,7 +24,12 @@ class ProblemDifficulty(object): Low = "Low" -class AbstractProblem(models.Model): +class Problem(models.Model): + # display ID + _id = models.CharField(max_length=24, db_index=True) + contest = models.ForeignKey(Contest, null=True, blank=True) + # for contest problem + is_public = models.BooleanField(default=False) title = models.CharField(max_length=128) # HTML description = RichTextField() @@ -33,6 +38,7 @@ class AbstractProblem(models.Model): # [{input: "test", output: "123"}, {input: "test123", output: "456"}] samples = JSONField() test_case_id = models.CharField(max_length=32) + # [{"input_name": "1.in", "output_name": "1.out", "score": 0}] test_case_score = JSONField() hint = RichTextField(blank=True, null=True) languages = JSONField() @@ -55,6 +61,8 @@ class AbstractProblem(models.Model): difficulty = models.CharField(max_length=32) tags = models.ManyToManyField(ProblemTag) source = models.CharField(max_length=200, blank=True, null=True) + # for OI mode + total_score = models.IntegerField(default=0, blank=True) submission_number = models.BigIntegerField(default=0) accepted_number = models.BigIntegerField(default=0) # ACM rule_type: {JudgeStatus.ACCEPTED: 3, JudgeStaus.WRONG_ANSWER: 11}, the number means count @@ -62,7 +70,7 @@ class AbstractProblem(models.Model): class Meta: db_table = "problem" - abstract = True + unique_together = (("_id", "contest"),) def add_submission_number(self): self.submission_number = models.F("submission_number") + 1 @@ -71,18 +79,3 @@ class AbstractProblem(models.Model): def add_ac_number(self): self.accepted_number = models.F("accepted_number") + 1 self.save(update_fields=["accepted_number"]) - - -class Problem(AbstractProblem): - _id = models.CharField(max_length=24, unique=True, db_index=True) - - -class ContestProblem(AbstractProblem): - _id = models.CharField(max_length=24, db_index=True) - contest = models.ForeignKey(Contest) - # 是否已经公开了题目,防止重复公开 - is_public = models.BooleanField(default=False) - - class Meta: - db_table = "contest_problem" - unique_together = (("_id", "contest"),) diff --git a/problem/serializers.py b/problem/serializers.py index a43e999e..d9e49195 100644 --- a/problem/serializers.py +++ b/problem/serializers.py @@ -4,7 +4,6 @@ from judge.languages import language_names, spj_language_names from utils.api import DateTimeTZField, UsernameSerializer, serializers from .models import Problem, ProblemRuleType, ProblemTag -from .models import ContestProblem class TestCaseUploadForm(forms.Form): @@ -93,16 +92,16 @@ class ProblemAdminSerializer(BaseProblemSerializer): class ContestProblemAdminSerializer(BaseProblemSerializer): class Meta: - model = ContestProblem + model = Problem class ProblemSerializer(BaseProblemSerializer): class Meta: model = Problem - exclude = ("test_case_score", "test_case_id", "visible") + exclude = ("contest", "test_case_score", "test_case_id", "visible", "is_public") class ContestProblemSerializer(BaseProblemSerializer): class Meta: - model = ContestProblem + model = Problem exclude = ("test_case_score", "test_case_id", "visible", "is_public") diff --git a/problem/views/admin.py b/problem/views/admin.py index 51afde8c..572e2fb4 100644 --- a/problem/views/admin.py +++ b/problem/views/admin.py @@ -10,11 +10,10 @@ from contest.models import Contest from utils.api import APIView, CSRFExemptAPIView, validate_serializer from utils.shortcuts import rand_str -from ..models import ContestProblem, Problem, ProblemRuleType, ProblemTag -from ..serializers import (CreateContestProblemSerializer, +from ..models import Problem, ProblemRuleType, ProblemTag +from ..serializers import (CreateContestProblemSerializer, ContestProblemAdminSerializer, CreateProblemSerializer, EditProblemSerializer, - ProblemAdminSerializer, TestCaseUploadForm, - ContestProblemAdminSerializer) + ProblemAdminSerializer, TestCaseUploadForm) class TestCaseUploadAPI(CSRFExemptAPIView): @@ -134,9 +133,13 @@ class ProblemAPI(APIView): data["spj_language"] = None data["spj_code"] = None if data["rule_type"] == ProblemRuleType.OI: + total_score = 0 for item in data["test_case_score"]: if item["score"] <= 0: return self.error("Invalid score") + else: + total_score += item["score"] + data["total_score"] = total_score # todo check filename and score info data["created_by"] = request.user tags = data.pop("tags") @@ -211,9 +214,13 @@ class ProblemAPI(APIView): data["spj_code"] = None if data["rule_type"] == ProblemRuleType.OI: + total_score = 0 for item in data["test_case_score"]: if item["score"] <= 0: return self.error("Invalid score") + else: + total_score += item["score"] + data["total_score"] = total_score # todo check filename and score info tags = data.pop("tags") @@ -250,11 +257,9 @@ class ContestProblemAPI(APIView): _id = data["_id"] if not _id: return self.error("Display id is required for contest problem") - try: - ContestProblem.objects.get(_id=_id, contest=contest) + + if Problem.objects.filter(_id=_id, contest=contest).exists(): return self.error("Duplicate Display id") - except ContestProblem.DoesNotExist: - pass if data["spj"]: if not data["spj_language"] or not data["spj_code"]: @@ -275,7 +280,7 @@ class ContestProblemAPI(APIView): tags = data.pop("tags") data["languages"] = list(data["languages"]) - problem = ContestProblem.objects.create(**data) + problem = Problem.objects.create(**data) for item in tags: try: @@ -291,17 +296,17 @@ class ContestProblemAPI(APIView): user = request.user if problem_id: try: - problem = ContestProblem.objects.get(id=problem_id) + problem = Problem.objects.get(id=problem_id) if user.is_admin() and problem.contest.created_by != user: return self.error("Problem does not exist") - except ContestProblem.DoesNotExist: + except Problem.DoesNotExist: return self.error("Problem does not exist") return self.success(ProblemAdminSerializer(problem).data) if not contest_id: return self.error("Contest id is required") - problems = ContestProblem.objects.filter(contest_id=contest_id).order_by("-create_time") + problems = Problem.objects.filter(contest_id=contest_id).order_by("-create_time") if user.is_admin(): problems = problems.filter(contest__created_by=user) keyword = request.GET.get("keyword") diff --git a/problem/views/oj.py b/problem/views/oj.py index 1a724f87..6eb84675 100644 --- a/problem/views/oj.py +++ b/problem/views/oj.py @@ -1,7 +1,7 @@ from django.db.models import Q from utils.api import APIView from account.decorators import check_contest_permission -from ..models import ProblemTag, Problem, ContestProblem, ProblemRuleType +from ..models import ProblemTag, Problem, ProblemRuleType from ..serializers import ProblemSerializer, TagSerializer from ..serializers import ContestProblemSerializer from contest.models import ContestRuleType @@ -66,14 +66,14 @@ class ContestProblemAPI(APIView): problem_id = request.GET.get("problem_id") if problem_id: try: - problem = ContestProblem.objects.select_related("created_by").get(_id=problem_id, contest=self.contest, - visible=True) - except ContestProblem.DoesNotExist: + problem = Problem.objects.select_related("created_by").get(_id=problem_id, + contest=self.contest, + visible=True) + except Problem.DoesNotExist: return self.error("Problem does not exist.") return self.success(ContestProblemSerializer(problem).data) - contest_problems = ContestProblem.objects.select_related("created_by").filter(contest=self.contest, - visible=True) + contest_problems = Problem.objects.select_related("created_by").filter(contest=self.contest, visible=True) # 根据profile, 为做过的题目添加标记 data = ContestProblemSerializer(contest_problems, many=True).data if request.user.id: diff --git a/submission/migrations/0007_auto_20170923_1318.py b/submission/migrations/0007_auto_20170923_1318.py new file mode 100644 index 00000000..d498e4cb --- /dev/null +++ b/submission/migrations/0007_auto_20170923_1318.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-09-23 13:18 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('submission', '0006_auto_20170830_1154'), + ] + + operations = [ + migrations.AlterField( + model_name='submission', + name='contest_id', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='contest.Contest'), + ), + migrations.AlterField( + model_name='submission', + name='problem_id', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problem.Problem'), + ), + migrations.RenameField( + model_name='submission', + old_name='contest_id', + new_name='contest', + ), + migrations.RenameField( + model_name='submission', + old_name='problem_id', + new_name='problem', + ), + ] diff --git a/submission/models.py b/submission/models.py index f16f3a44..f4cb2f2e 100644 --- a/submission/models.py +++ b/submission/models.py @@ -1,6 +1,8 @@ from django.db import models from jsonfield import JSONField from account.models import AdminType +from problem.models import Problem +from contest.models import Contest from utils.shortcuts import rand_str @@ -21,8 +23,8 @@ class JudgeStatus: class Submission(models.Model): id = models.CharField(max_length=32, default=rand_str, primary_key=True, db_index=True) - contest_id = models.IntegerField(db_index=True, null=True) - problem_id = models.IntegerField(db_index=True) + contest = models.ForeignKey(Contest, null=True) + problem = models.ForeignKey(Problem) create_time = models.DateTimeField(auto_now_add=True) user_id = models.IntegerField(db_index=True) username = models.CharField(max_length=30) diff --git a/submission/serializers.py b/submission/serializers.py index d2fe4ada..1c5493d1 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -18,13 +18,13 @@ class SubmissionModelSerializer(serializers.ModelSerializer): model = Submission -# 不显示submission info详情的serializer +# 不显示submission info的serializer, 用于ACM rule_type class SubmissionSafeSerializer(serializers.ModelSerializer): statistic_info = serializers.JSONField() class Meta: model = Submission - exclude = ("info", "contest_id") + exclude = ("info", "contest") class SubmissionListSerializer(serializers.ModelSerializer): @@ -37,7 +37,7 @@ class SubmissionListSerializer(serializers.ModelSerializer): class Meta: model = Submission - exclude = ("info", "contest_id", "code") + exclude = ("info", "contest", "code") def get_show_link(self, obj): # 没传user或为匿名user diff --git a/submission/test.py b/submission/test.py deleted file mode 100644 index e69de29b..00000000 diff --git a/submission/tests.py b/submission/tests.py new file mode 100644 index 00000000..4c474ba5 --- /dev/null +++ b/submission/tests.py @@ -0,0 +1,57 @@ +from unittest import mock +from copy import deepcopy + +from .models import Submission +from problem.models import Problem, ProblemTag +from utils.api.tests import APITestCase + +DEFAULT_PROBLEM_DATA = {"_id": "110", "title": "test", "description": "

test

", "input_description": "test", + "output_description": "test", "time_limit": 1000, "memory_limit": 256, "difficulty": "Low", + "visible": True, "tags": ["test"], "languages": ["C", "C++", "Java", "Python2"], "template": {}, + "samples": [{"input": "test", "output": "test"}], "spj": False, "spj_language": "C", + "spj_code": "", "test_case_id": "499b26290cc7994e0b497212e842ea85", + "test_case_score": [{"output_name": "1.out", "input_name": "1.in", "output_size": 0, + "stripped_output_md5": "d41d8cd98f00b204e9800998ecf8427e", + "input_size": 0, "score": 0}], + "rule_type": "ACM", "hint": "

test

", "source": "test"} + +DEFAULT_SUBMISSION_DATA = { + "problem_id": "110", + "user_id": 1, + "username": "test", + "code": "xxxxxxxxxxxxxx", + "result": -2, + "info": {}, + "language": "C", + "statistic_info": {} +} + + +class SubmissionListTest(APITestCase): + def setUp(self): + self.create_user("123", "345") + self.url = self.reverse("submission_list_api") + Submission.objects.create(**DEFAULT_SUBMISSION_DATA) + + def test_get_submission_list(self): + resp = self.client.get(self.url, data={"limit": "10"}) + self.assertSuccess(resp) + + +@mock.patch("submission.views.oj.judge_task.delay") +class SubmissionAPITest(APITestCase): + def setUp(self): + self.user = self.create_user("test", "test123") + tag = ProblemTag.objects.create(name="test") + problem_data = deepcopy(DEFAULT_PROBLEM_DATA) + problem_data.pop("tags") + problem_data["created_by"] = self.user + self.problem = Problem.objects.create(**problem_data) + self.problem.tags.add(tag) + self.problem.save() + self.url = self.reverse("submission_api") + + def test_create_submission(self, judge_task): + resp = self.client.post(self.url, DEFAULT_SUBMISSION_DATA) + self.assertSuccess(resp) + judge_task.assert_called() diff --git a/submission/views/oj.py b/submission/views/oj.py index f4d276d4..11c76e9e 100644 --- a/submission/views/oj.py +++ b/submission/views/oj.py @@ -1,8 +1,7 @@ - from account.decorators import login_required, check_contest_permission from judge.tasks import judge_task # from judge.dispatcher import JudgeDispatcher -from problem.models import Problem, ProblemRuleType, ContestProblem +from problem.models import Problem, ProblemRuleType from contest.models import Contest, ContestStatus from utils.api import APIView, validate_serializer from utils.throttling import TokenBucket, BucketController @@ -20,16 +19,15 @@ def _submit(response, user, problem_id, language, code, contest_id): bucket = TokenBucket(fill_rate=10, capacity=20, last_capacity=controller.last_capacity, last_timestamp=controller.last_timestamp) - if bucket.consume(): controller.last_capacity -= 1 else: return response.error("Please wait %d seconds" % int(bucket.expected_time() + 1)) + try: - if contest_id: - problem = ContestProblem.objects.get(_id=problem_id, visible=True) - else: - problem = Problem.objects.get(_id=problem_id, visible=True) + problem = Problem.objects.get(_id=problem_id, + contest_id=contest_id, + visible=True) except Problem.DoesNotExist: return response.error("Problem not exist") @@ -37,11 +35,11 @@ def _submit(response, user, problem_id, language, code, contest_id): username=user.username, language=language, code=code, - problem_id=problem._id, + problem_id=problem.id, contest_id=contest_id) # use this for debug - # JudgeDispatcher(submission.id, problem._id).judge() - judge_task.delay(submission.id, problem._id) + # JudgeDispatcher(submission.id, problem.id).judge() + judge_task.delay(submission.id, problem.id) return response.success({"submission_id": submission.id}) @@ -65,32 +63,21 @@ class SubmissionAPI(APIView): if not submission_id: return self.error("Parameter id doesn't exist.") try: - submission = Submission.objects.get(id=submission_id) + submission = Submission.objects.select_related("problem").get(id=submission_id) except Submission.DoesNotExist: return self.error("Submission doesn't exist.") if not submission.check_user_permission(request.user): return self.error("No permission for this submission.") - if submission.contest_id: - # check problem'rule is ACM or IO. - if ContestProblem.objects.filter(contest_id=submission.contest_id, - _id=submission.problem_id, - visible=True, - rule_type=ProblemRuleType.ACM - ).exists(): - return self.success(SubmissionSafeSerializer(submission).data) - return self.success(SubmissionModelSerializer(submission).data) - - if Problem.objects.filter(_id=submission.problem_id, - visible=True, - rule_type=ProblemRuleType.ACM - ).exists(): + if submission.problem.rule_type == ProblemRuleType.ACM: return self.success(SubmissionSafeSerializer(submission).data) return self.success(SubmissionModelSerializer(submission).data) class SubmissionListAPI(APIView): def get(self, request): + if not request.GET.get("limit"): + return self.error("Limit is needed") if request.GET.get("contest_id"): return self._get_contest_submission_list(request) @@ -107,7 +94,11 @@ class SubmissionListAPI(APIView): myself = request.GET.get("myself") result = request.GET.get("result") if problem_id: - submissions = submissions.filter(problem_id=problem_id) + try: + problem = Problem.objects.get(_id=problem_id, visible=True) + except Problem.DoesNotExist: + return self.error("Problem doesn't exist") + submissions = problem.submission_set.all() if myself and myself == "1": submissions = submissions.filter(user_id=request.user.id) if result: