merge problem and contest_problem

This commit is contained in:
zema1 2017-09-24 09:48:17 +08:00
parent e9c7344815
commit 51c229a2c5
13 changed files with 263 additions and 97 deletions

View File

@ -84,8 +84,8 @@ class ACMContestRank(ContestRank):
class OIContestRank(ContestRank): class OIContestRank(ContestRank):
total_score = models.IntegerField(default=0) total_score = models.IntegerField(default=0)
# {23: {"score": 80, "total_score": 100}} # {23: 333}}
# key is problem id # key is problem id, value is current score
submission_info = JSONField(default={}) submission_info = JSONField(default={})
class Meta: class Meta:

View File

@ -12,7 +12,7 @@ from account.models import User
from conf.models import JudgeServer, JudgeServerToken from conf.models import JudgeServer, JudgeServerToken
from contest.models import ContestRuleType, ACMContestRank, OIContestRank from contest.models import ContestRuleType, ACMContestRank, OIContestRank
from judge.languages import languages 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 submission.models import JudgeStatus, Submission
from utils.cache import judge_cache from utils.cache import judge_cache
from utils.constants import CacheKey from utils.constants import CacheKey
@ -35,12 +35,13 @@ class JudgeDispatcher(object):
self.token = hashlib.sha256(token.encode("utf-8")).hexdigest() self.token = hashlib.sha256(token.encode("utf-8")).hexdigest()
self.redis_conn = judge_cache self.redis_conn = judge_cache
self.submission = Submission.objects.get(pk=submission_id) self.submission = Submission.objects.get(pk=submission_id)
if self.submission.contest_id: self.contest_id = self.submission.contest_id
self.problem = ContestProblem.objects.select_related("contest") \ if self.contest_id:
.get(_id=problem_id, contest_id=self.submission.contest_id) self.problem = Problem.objects.select_related("contest") \
.get(id=problem_id, contest_id=self.contest_id)
self.contest = self.problem.contest self.contest = self.problem.contest
else: else:
self.problem = Problem.objects.get(_id=problem_id) self.problem = Problem.objects.get(id=problem_id)
def _request(self, url, data=None): def _request(self, url, data=None):
kwargs = {"headers": {"X-Judge-Server-Token": self.token, kwargs = {"headers": {"X-Judge-Server-Token": self.token,
@ -72,10 +73,28 @@ class JudgeDispatcher(object):
server.used_instance_number = F("task_number") - 1 server.used_instance_number = F("task_number") - 1
server.save() 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): def judge(self, output=False):
server = self.choose_judge_server() server = self.choose_judge_server()
if not 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)) self.redis_conn.lpush(CacheKey.waiting_queue, json.dumps(data))
return return
@ -108,11 +127,7 @@ class JudgeDispatcher(object):
self.submission.result = JudgeStatus.COMPILE_ERROR self.submission.result = JudgeStatus.COMPILE_ERROR
self.submission.statistic_info["err_info"] = resp["data"] self.submission.statistic_info["err_info"] = resp["data"]
else: else:
# 用时和内存占用保存为多个测试点中最长的那个 self._compute_statistic_info(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"]])
# todo OI statistic_info["score"]
error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"])) error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"]))
# ACM模式下,多个测试点全部正确则AC否则取第一个错误的测试点的状态 # ACM模式下,多个测试点全部正确则AC否则取第一个错误的测试点的状态
# OI模式下, 若多个测试点全部正确则AC 若全部错误则取第一个错误测试点状态,否则为部分正确 # OI模式下, 若多个测试点全部正确则AC 若全部错误则取第一个错误测试点状态,否则为部分正确
@ -126,7 +141,7 @@ class JudgeDispatcher(object):
self.release_judge_res(server.id) self.release_judge_res(server.id)
self.update_problem_status() self.update_problem_status()
if self.submission.contest_id: if self.contest_id:
self.update_contest_rank() self.update_contest_rank()
# 至此判题结束,尝试处理任务队列中剩余的任务 # 至此判题结束,尝试处理任务队列中剩余的任务
@ -141,15 +156,11 @@ class JudgeDispatcher(object):
def update_problem_status(self): def update_problem_status(self):
with transaction.atomic(): with transaction.atomic():
# prepare problem and user_profile # prepare problem and user_profile
if self.submission.contest_id: problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.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_info = problem.statistic_info problem_info = problem.statistic_info
user = User.objects.select_for_update().select_for_update("userprofile").get(id=self.submission.user_id) user = User.objects.select_for_update().select_for_update("userprofile").get(id=self.submission.user_id)
user_profile = user.userprofile user_profile = user.userprofile
if self.submission.contest_id: if self.contest_id:
key = "contest_problems" key = "contest_problems"
else: else:
key = "problems" key = "problems"
@ -159,7 +170,7 @@ class JudgeDispatcher(object):
# update submission and accepted number counter # update submission and accepted number counter
# only when submission is not in contest, we update user profile, # 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 # 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 user_profile.submission_number += 1
if self.submission.result == JudgeStatus.ACCEPTED: if self.submission.result == JudgeStatus.ACCEPTED:
user_profile.accepted_number += 1 user_profile.accepted_number += 1
@ -167,7 +178,7 @@ class JudgeDispatcher(object):
if self.submission.result == JudgeStatus.ACCEPTED: if self.submission.result == JudgeStatus.ACCEPTED:
problem.accepted_number += 1 problem.accepted_number += 1
problem_id = str(self.problem._id) problem_id = str(self.problem.id)
if self.problem.rule_type == ProblemRuleType.ACM: if self.problem.rule_type == ProblemRuleType.ACM:
# update acm problem info # update acm problem info
result = str(self.submission.result) result = str(self.submission.result)
@ -197,13 +208,13 @@ class JudgeDispatcher(object):
oi_problems_status[problem_id] = score oi_problems_status[problem_id] = score
else: else:
# minus last time score, add this time score # 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 oi_problems_status[problem_id] = score
user_profile.oi_problems_status[key] = oi_problems_status user_profile.oi_problems_status[key] = oi_problems_status
problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"]) problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"])
user_profile.save( user_profile.save(update_fields=[
update_fields=["submission_number", "accepted_number", "acm_problems_status", "oi_problems_status"]) "submission_number", "accepted_number", "acm_problems_status", "oi_problems_status"])
def update_contest_rank(self): def update_contest_rank(self):
if self.contest.real_time_rank: if self.contest.real_time_rank:
@ -221,7 +232,7 @@ class JudgeDispatcher(object):
def _update_acm_contest_rank(self, rank): def _update_acm_contest_rank(self, rank):
info = rank.submission_info.get(str(self.submission.problem_id)) 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:
if info["is_ac"]: if info["is_ac"]:
@ -258,4 +269,10 @@ class JudgeDispatcher(object):
rank.save() rank.save()
def _update_oi_contest_rank(self, rank): 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()

View File

@ -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',
),
]

View File

@ -24,7 +24,12 @@ class ProblemDifficulty(object):
Low = "Low" 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) title = models.CharField(max_length=128)
# HTML # HTML
description = RichTextField() description = RichTextField()
@ -33,6 +38,7 @@ class AbstractProblem(models.Model):
# [{input: "test", output: "123"}, {input: "test123", output: "456"}] # [{input: "test", output: "123"}, {input: "test123", output: "456"}]
samples = JSONField() samples = JSONField()
test_case_id = models.CharField(max_length=32) test_case_id = models.CharField(max_length=32)
# [{"input_name": "1.in", "output_name": "1.out", "score": 0}]
test_case_score = JSONField() test_case_score = JSONField()
hint = RichTextField(blank=True, null=True) hint = RichTextField(blank=True, null=True)
languages = JSONField() languages = JSONField()
@ -55,6 +61,8 @@ class AbstractProblem(models.Model):
difficulty = models.CharField(max_length=32) difficulty = models.CharField(max_length=32)
tags = models.ManyToManyField(ProblemTag) tags = models.ManyToManyField(ProblemTag)
source = models.CharField(max_length=200, blank=True, null=True) 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) submission_number = models.BigIntegerField(default=0)
accepted_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 # ACM rule_type: {JudgeStatus.ACCEPTED: 3, JudgeStaus.WRONG_ANSWER: 11}, the number means count
@ -62,7 +70,7 @@ class AbstractProblem(models.Model):
class Meta: class Meta:
db_table = "problem" db_table = "problem"
abstract = True unique_together = (("_id", "contest"),)
def add_submission_number(self): def add_submission_number(self):
self.submission_number = models.F("submission_number") + 1 self.submission_number = models.F("submission_number") + 1
@ -71,18 +79,3 @@ class AbstractProblem(models.Model):
def add_ac_number(self): def add_ac_number(self):
self.accepted_number = models.F("accepted_number") + 1 self.accepted_number = models.F("accepted_number") + 1
self.save(update_fields=["accepted_number"]) 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"),)

View File

@ -4,7 +4,6 @@ from judge.languages import language_names, spj_language_names
from utils.api import DateTimeTZField, UsernameSerializer, serializers from utils.api import DateTimeTZField, UsernameSerializer, serializers
from .models import Problem, ProblemRuleType, ProblemTag from .models import Problem, ProblemRuleType, ProblemTag
from .models import ContestProblem
class TestCaseUploadForm(forms.Form): class TestCaseUploadForm(forms.Form):
@ -93,16 +92,16 @@ class ProblemAdminSerializer(BaseProblemSerializer):
class ContestProblemAdminSerializer(BaseProblemSerializer): class ContestProblemAdminSerializer(BaseProblemSerializer):
class Meta: class Meta:
model = ContestProblem model = Problem
class ProblemSerializer(BaseProblemSerializer): class ProblemSerializer(BaseProblemSerializer):
class Meta: class Meta:
model = Problem 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 ContestProblemSerializer(BaseProblemSerializer):
class Meta: class Meta:
model = ContestProblem model = Problem
exclude = ("test_case_score", "test_case_id", "visible", "is_public") exclude = ("test_case_score", "test_case_id", "visible", "is_public")

View File

@ -10,11 +10,10 @@ from contest.models import Contest
from utils.api import APIView, CSRFExemptAPIView, validate_serializer from utils.api import APIView, CSRFExemptAPIView, validate_serializer
from utils.shortcuts import rand_str from utils.shortcuts import rand_str
from ..models import ContestProblem, Problem, ProblemRuleType, ProblemTag from ..models import Problem, ProblemRuleType, ProblemTag
from ..serializers import (CreateContestProblemSerializer, from ..serializers import (CreateContestProblemSerializer, ContestProblemAdminSerializer,
CreateProblemSerializer, EditProblemSerializer, CreateProblemSerializer, EditProblemSerializer,
ProblemAdminSerializer, TestCaseUploadForm, ProblemAdminSerializer, TestCaseUploadForm)
ContestProblemAdminSerializer)
class TestCaseUploadAPI(CSRFExemptAPIView): class TestCaseUploadAPI(CSRFExemptAPIView):
@ -134,9 +133,13 @@ class ProblemAPI(APIView):
data["spj_language"] = None data["spj_language"] = None
data["spj_code"] = None data["spj_code"] = None
if data["rule_type"] == ProblemRuleType.OI: if data["rule_type"] == ProblemRuleType.OI:
total_score = 0
for item in data["test_case_score"]: for item in data["test_case_score"]:
if item["score"] <= 0: if item["score"] <= 0:
return self.error("Invalid score") return self.error("Invalid score")
else:
total_score += item["score"]
data["total_score"] = total_score
# todo check filename and score info # todo check filename and score info
data["created_by"] = request.user data["created_by"] = request.user
tags = data.pop("tags") tags = data.pop("tags")
@ -211,9 +214,13 @@ class ProblemAPI(APIView):
data["spj_code"] = None data["spj_code"] = None
if data["rule_type"] == ProblemRuleType.OI: if data["rule_type"] == ProblemRuleType.OI:
total_score = 0
for item in data["test_case_score"]: for item in data["test_case_score"]:
if item["score"] <= 0: if item["score"] <= 0:
return self.error("Invalid score") return self.error("Invalid score")
else:
total_score += item["score"]
data["total_score"] = total_score
# todo check filename and score info # todo check filename and score info
tags = data.pop("tags") tags = data.pop("tags")
@ -250,11 +257,9 @@ class ContestProblemAPI(APIView):
_id = data["_id"] _id = data["_id"]
if not _id: if not _id:
return self.error("Display id is required for contest problem") 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") return self.error("Duplicate Display id")
except ContestProblem.DoesNotExist:
pass
if data["spj"]: if data["spj"]:
if not data["spj_language"] or not data["spj_code"]: if not data["spj_language"] or not data["spj_code"]:
@ -275,7 +280,7 @@ class ContestProblemAPI(APIView):
tags = data.pop("tags") tags = data.pop("tags")
data["languages"] = list(data["languages"]) data["languages"] = list(data["languages"])
problem = ContestProblem.objects.create(**data) problem = Problem.objects.create(**data)
for item in tags: for item in tags:
try: try:
@ -291,17 +296,17 @@ class ContestProblemAPI(APIView):
user = request.user user = request.user
if problem_id: if problem_id:
try: 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: if user.is_admin() and problem.contest.created_by != user:
return self.error("Problem does not exist") return self.error("Problem does not exist")
except ContestProblem.DoesNotExist: except Problem.DoesNotExist:
return self.error("Problem does not exist") return self.error("Problem does not exist")
return self.success(ProblemAdminSerializer(problem).data) return self.success(ProblemAdminSerializer(problem).data)
if not contest_id: if not contest_id:
return self.error("Contest id is required") 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(): if user.is_admin():
problems = problems.filter(contest__created_by=user) problems = problems.filter(contest__created_by=user)
keyword = request.GET.get("keyword") keyword = request.GET.get("keyword")

View File

@ -1,7 +1,7 @@
from django.db.models import Q from django.db.models import Q
from utils.api import APIView from utils.api import APIView
from account.decorators import check_contest_permission 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 ProblemSerializer, TagSerializer
from ..serializers import ContestProblemSerializer from ..serializers import ContestProblemSerializer
from contest.models import ContestRuleType from contest.models import ContestRuleType
@ -66,14 +66,14 @@ class ContestProblemAPI(APIView):
problem_id = request.GET.get("problem_id") problem_id = request.GET.get("problem_id")
if problem_id: if problem_id:
try: try:
problem = ContestProblem.objects.select_related("created_by").get(_id=problem_id, contest=self.contest, problem = Problem.objects.select_related("created_by").get(_id=problem_id,
visible=True) contest=self.contest,
except ContestProblem.DoesNotExist: visible=True)
except Problem.DoesNotExist:
return self.error("Problem does not exist.") return self.error("Problem does not exist.")
return self.success(ContestProblemSerializer(problem).data) return self.success(ContestProblemSerializer(problem).data)
contest_problems = ContestProblem.objects.select_related("created_by").filter(contest=self.contest, contest_problems = Problem.objects.select_related("created_by").filter(contest=self.contest, visible=True)
visible=True)
# 根据profile 为做过的题目添加标记 # 根据profile 为做过的题目添加标记
data = ContestProblemSerializer(contest_problems, many=True).data data = ContestProblemSerializer(contest_problems, many=True).data
if request.user.id: if request.user.id:

View File

@ -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',
),
]

View File

@ -1,6 +1,8 @@
from django.db import models from django.db import models
from jsonfield import JSONField from jsonfield import JSONField
from account.models import AdminType from account.models import AdminType
from problem.models import Problem
from contest.models import Contest
from utils.shortcuts import rand_str from utils.shortcuts import rand_str
@ -21,8 +23,8 @@ class JudgeStatus:
class Submission(models.Model): class Submission(models.Model):
id = models.CharField(max_length=32, default=rand_str, primary_key=True, db_index=True) id = models.CharField(max_length=32, default=rand_str, primary_key=True, db_index=True)
contest_id = models.IntegerField(db_index=True, null=True) contest = models.ForeignKey(Contest, null=True)
problem_id = models.IntegerField(db_index=True) problem = models.ForeignKey(Problem)
create_time = models.DateTimeField(auto_now_add=True) create_time = models.DateTimeField(auto_now_add=True)
user_id = models.IntegerField(db_index=True) user_id = models.IntegerField(db_index=True)
username = models.CharField(max_length=30) username = models.CharField(max_length=30)

View File

@ -18,13 +18,13 @@ class SubmissionModelSerializer(serializers.ModelSerializer):
model = Submission model = Submission
# 不显示submission info详情的serializer # 不显示submission info的serializer, 用于ACM rule_type
class SubmissionSafeSerializer(serializers.ModelSerializer): class SubmissionSafeSerializer(serializers.ModelSerializer):
statistic_info = serializers.JSONField() statistic_info = serializers.JSONField()
class Meta: class Meta:
model = Submission model = Submission
exclude = ("info", "contest_id") exclude = ("info", "contest")
class SubmissionListSerializer(serializers.ModelSerializer): class SubmissionListSerializer(serializers.ModelSerializer):
@ -37,7 +37,7 @@ class SubmissionListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Submission model = Submission
exclude = ("info", "contest_id", "code") exclude = ("info", "contest", "code")
def get_show_link(self, obj): def get_show_link(self, obj):
# 没传user或为匿名user # 没传user或为匿名user

View File

57
submission/tests.py Normal file
View File

@ -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": "<p>test</p>", "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": "<p>test</p>", "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()

View File

@ -1,8 +1,7 @@
from account.decorators import login_required, check_contest_permission from account.decorators import login_required, check_contest_permission
from judge.tasks import judge_task from judge.tasks import judge_task
# from judge.dispatcher import JudgeDispatcher # 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 contest.models import Contest, ContestStatus
from utils.api import APIView, validate_serializer from utils.api import APIView, validate_serializer
from utils.throttling import TokenBucket, BucketController 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, bucket = TokenBucket(fill_rate=10, capacity=20,
last_capacity=controller.last_capacity, last_capacity=controller.last_capacity,
last_timestamp=controller.last_timestamp) last_timestamp=controller.last_timestamp)
if bucket.consume(): if bucket.consume():
controller.last_capacity -= 1 controller.last_capacity -= 1
else: else:
return response.error("Please wait %d seconds" % int(bucket.expected_time() + 1)) return response.error("Please wait %d seconds" % int(bucket.expected_time() + 1))
try: try:
if contest_id: problem = Problem.objects.get(_id=problem_id,
problem = ContestProblem.objects.get(_id=problem_id, visible=True) contest_id=contest_id,
else: visible=True)
problem = Problem.objects.get(_id=problem_id, visible=True)
except Problem.DoesNotExist: except Problem.DoesNotExist:
return response.error("Problem not exist") return response.error("Problem not exist")
@ -37,11 +35,11 @@ def _submit(response, user, problem_id, language, code, contest_id):
username=user.username, username=user.username,
language=language, language=language,
code=code, code=code,
problem_id=problem._id, problem_id=problem.id,
contest_id=contest_id) contest_id=contest_id)
# use this for debug # use this for debug
# JudgeDispatcher(submission.id, problem._id).judge() # JudgeDispatcher(submission.id, problem.id).judge()
judge_task.delay(submission.id, problem._id) judge_task.delay(submission.id, problem.id)
return response.success({"submission_id": submission.id}) return response.success({"submission_id": submission.id})
@ -65,32 +63,21 @@ class SubmissionAPI(APIView):
if not submission_id: if not submission_id:
return self.error("Parameter id doesn't exist.") return self.error("Parameter id doesn't exist.")
try: try:
submission = Submission.objects.get(id=submission_id) submission = Submission.objects.select_related("problem").get(id=submission_id)
except Submission.DoesNotExist: except Submission.DoesNotExist:
return self.error("Submission doesn't exist.") return self.error("Submission doesn't exist.")
if not submission.check_user_permission(request.user): if not submission.check_user_permission(request.user):
return self.error("No permission for this submission.") return self.error("No permission for this submission.")
if submission.contest_id: if submission.problem.rule_type == ProblemRuleType.ACM:
# 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():
return self.success(SubmissionSafeSerializer(submission).data) return self.success(SubmissionSafeSerializer(submission).data)
return self.success(SubmissionModelSerializer(submission).data) return self.success(SubmissionModelSerializer(submission).data)
class SubmissionListAPI(APIView): class SubmissionListAPI(APIView):
def get(self, request): def get(self, request):
if not request.GET.get("limit"):
return self.error("Limit is needed")
if request.GET.get("contest_id"): if request.GET.get("contest_id"):
return self._get_contest_submission_list(request) return self._get_contest_submission_list(request)
@ -107,7 +94,11 @@ class SubmissionListAPI(APIView):
myself = request.GET.get("myself") myself = request.GET.get("myself")
result = request.GET.get("result") result = request.GET.get("result")
if problem_id: 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": if myself and myself == "1":
submissions = submissions.filter(user_id=request.user.id) submissions = submissions.filter(user_id=request.user.id)
if result: if result: