separate contest submission and regular submission

This commit is contained in:
zema1 2017-09-30 10:26:54 +08:00
parent 2a91fd5e9f
commit d650252a1a
9 changed files with 103 additions and 77 deletions

View File

@ -80,16 +80,17 @@ def check_contest_permission(func):
except Contest.DoesNotExist: except Contest.DoesNotExist:
return self.error("Contest %s doesn't exist" % contest_id) return self.error("Contest %s doesn't exist" % contest_id)
if self.contest.status == ContestStatus.CONTEST_NOT_START and user != self.contest.created_by: # creator or owner
if self.contest.is_contest_admin(user):
return func(*args, **kwargs)
if self.contest.status == ContestStatus.CONTEST_NOT_START:
return self.error("Contest has not started yet.") return self.error("Contest has not started yet.")
if self.contest.contest_type == ContestType.PASSWORD_PROTECTED_CONTEST: if self.contest.contest_type == ContestType.PASSWORD_PROTECTED_CONTEST:
# Anonymous # Anonymous
if not user.is_authenticated(): if not user.is_authenticated():
return self.error("Please login in first.") return self.error("Please login in first.")
# creator
if user == self.contest.created_by:
return func(*args, **kwargs)
# password error # password error
if ("contests" not in request.session) or (self.contest.id not in request.session["contests"]): if ("contests" not in request.session) or (self.contest.id not in request.session["contests"]):
return self.error("Password is required.") return self.error("Password is required.")

View File

@ -2,7 +2,7 @@ from django.db import models
from django.utils.timezone import now from django.utils.timezone import now
from jsonfield import JSONField from jsonfield import JSONField
from account.models import User from account.models import User, AdminType
from utils.models import RichTextField from utils.models import RichTextField
@ -56,6 +56,9 @@ class Contest(models.Model):
return ContestType.PASSWORD_PROTECTED_CONTEST return ContestType.PASSWORD_PROTECTED_CONTEST
return ContestType.PUBLIC_CONTEST return ContestType.PUBLIC_CONTEST
def is_contest_admin(self, user):
return user.is_authenticated() and (self.created_by == user or user.admin_type == AdminType.SUPER_ADMIN)
class Meta: class Meta:
db_table = "contest" db_table = "contest"
ordering = ("-create_time",) ordering = ("-create_time",)

View File

@ -74,17 +74,17 @@ class ContestAPITest(APITestCase):
response = self.client.get("{}?id={}".format(self.url, contest_id)) response = self.client.get("{}?id={}".format(self.url, contest_id))
self.assertSuccess(response) self.assertSuccess(response)
def test_contest_password(self): def test_regular_user_validate_contest_password(self):
contest_id = self.create_contest().data["data"]["id"] contest_id = self.create_contest().data["data"]["id"]
self.create_user("test", "test123") self.create_user("test", "test123")
url = self.reverse("contest_password_api") url = self.reverse("contest_password_api")
resp = self.client.post(url, {"contest_id": contest_id, "password": "error_password"}) resp = self.client.post(url, {"contest_id": contest_id, "password": "error_password"})
self.assertFailed(resp) self.assertDictEqual(resp.data, {"error": "error", "data": "Password doesn't match."})
resp = self.client.post(url, {"contest_id": contest_id, "password": DEFAULT_CONTEST_DATA["password"]}) resp = self.client.post(url, {"contest_id": contest_id, "password": DEFAULT_CONTEST_DATA["password"]})
self.assertSuccess(resp) self.assertSuccess(resp)
def test_contest_access(self): def test_regular_user_access_contest(self):
contest_id = self.create_contest().data["data"]["id"] contest_id = self.create_contest().data["data"]["id"]
self.create_user("test", "test123") self.create_user("test", "test123")
url = self.reverse("contest_access_api") url = self.reverse("contest_access_api")
@ -97,18 +97,6 @@ class ContestAPITest(APITestCase):
resp = self.client.get(url + "?contest_id=" + str(contest_id)) resp = self.client.get(url + "?contest_id=" + str(contest_id))
self.assertSuccess(resp) self.assertSuccess(resp)
# def test_get_not_started_contest(self):
# contest_id = self.create_contest().data["data"]["id"]
# resp = self.client.get(self.url + "?id=" + str(contest_id))
# self.assertSuccess(resp)
#
# self.create_user("test", "1234")
# url = self.reverse("contest_password_api")
# resp = self.client.post(url, {"contest_id": contest_id, "password": DEFAULT_CONTEST_DATA["password"]})
# self.assertSuccess(resp)
# resp = self.client.get(self.url + "?id=" + str(contest_id))
# self.assertFailed(resp)
class ContestAnnouncementAPITest(APITestCase): class ContestAnnouncementAPITest(APITestCase):
def setUp(self): def setUp(self):

View File

@ -18,7 +18,7 @@ class ContestAPI(APIView):
data["created_by"] = request.user data["created_by"] = request.user
if data["end_time"] <= data["start_time"]: if data["end_time"] <= data["start_time"]:
return self.error("Start time must occur earlier than end time") return self.error("Start time must occur earlier than end time")
if not data["password"]: if data.get("password") and data["password"] == "":
data["password"] = None data["password"] = None
contest = Contest.objects.create(**data) contest = Contest.objects.create(**data)
return self.success(ContestAdminSerializer(contest).data) return self.success(ContestAdminSerializer(contest).data)

View File

@ -1,6 +1,7 @@
import copy import copy
import os import os
import shutil import shutil
from datetime import timedelta
from zipfile import ZipFile from zipfile import ZipFile
from django.conf import settings from django.conf import settings
@ -9,6 +10,7 @@ from utils.api.tests import APITestCase
from .models import ProblemTag from .models import ProblemTag
from .views.admin import TestCaseUploadAPI from .views.admin import TestCaseUploadAPI
from contest.models import Contest
from contest.tests import DEFAULT_CONTEST_DATA from contest.tests import DEFAULT_CONTEST_DATA
DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "<p>test</p>", "input_description": "test", DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "<p>test</p>", "input_description": "test",
@ -193,32 +195,37 @@ class ContestProblemTest(APITestCase):
self.create_admin() self.create_admin()
url = self.reverse("contest_admin_api") url = self.reverse("contest_admin_api")
self.contest = self.client.post(url, data=DEFAULT_CONTEST_DATA) contest_data = copy.deepcopy(DEFAULT_CONTEST_DATA)
self.data = DEFAULT_PROBLEM_DATA contest_data["password"] = ""
self.data["contest"] = self.contest.data["data"]["id"] contest_data["start_time"] = contest_data["start_time"] + timedelta(hours=1)
self.contest = self.client.post(url, data=contest_data).data["data"]
problem_data = copy.deepcopy(DEFAULT_PROBLEM_DATA)
problem_data["contest"] = self.contest["id"]
url = self.reverse("contest_problem_admin_api") url = self.reverse("contest_problem_admin_api")
self.problem = self.client.post(url, self.data) self.problem = self.client.post(url, problem_data).data["data"]
def test_get_contest_problem_list(self): def test_get_contest_problem_list(self):
contest_id = self.contest.data["data"]["id"] contest_id = self.contest["id"]
resp = self.client.get(self.url + "?contest_id=" + str(contest_id)) resp = self.client.get(self.url + "?contest_id=" + str(contest_id))
self.assertSuccess(resp) self.assertSuccess(resp)
self.assertEqual(len(resp.data["data"]), 1) self.assertEqual(len(resp.data["data"]), 1)
def test_get_one_contest_problem(self): def test_get_one_contest_problem(self):
contest_id = self.contest.data["data"]["id"] contest_id = self.contest["id"]
problem_id = self.problem.data["data"]["_id"] problem_id = self.problem["_id"]
resp = self.client.get("{}?contest_id={}&problem_id={}".format(self.url, contest_id, problem_id)) resp = self.client.get("{}?contest_id={}&problem_id={}".format(self.url, contest_id, problem_id))
self.assertSuccess(resp) self.assertSuccess(resp)
def test_regular_user_get_contest_problem(self): def test_regular_user_get_not_started_contest_problem(self):
self.create_user("test", "test123") self.create_user("test", "test123")
contest_id = self.contest.data["data"]["id"] resp = self.client.get(self.url + "?contest_id=" + str(self.contest["id"]))
problem_id = self.problem.data["data"]["_id"] self.assertDictEqual(resp.data, {"error": "error", "data": "Contest has not started yet."})
resp = self.client.get("{}?contest_id={}&problem_id={}".format(self.url, contest_id, problem_id))
self.assertFailed(resp)
url = self.reverse("contest_password_api") def test_reguar_user_get_started_contest_problem(self):
self.client.post(url, {"contest_id": contest_id, "password": DEFAULT_CONTEST_DATA["password"]}) self.create_user("test", "test123")
resp = self.client.get("{}?contest_id={}&problem_id={}".format(self.url, contest_id, problem_id)) contest = Contest.objects.first()
contest.start_time = contest.start_time - timedelta(hours=1)
contest.save()
resp = self.client.get(self.url + "?contest_id=" + str(self.contest["id"]))
self.assertSuccess(resp) self.assertSuccess(resp)

View File

@ -77,7 +77,7 @@ class ContestProblemAPI(APIView):
contest_problems = Problem.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 为做过的题目添加标记 # 根据profile 为做过的题目添加标记
data = ContestProblemSerializer(contest_problems, many=True).data data = ContestProblemSerializer(contest_problems, many=True).data
if request.user.id: if request.user.is_authenticated() and self.contest.rule_type != ContestRuleType.OI:
profile = request.user.userprofile profile = request.user.userprofile
if self.contest.rule_type == ContestRuleType.ACM: if self.contest.rule_type == ContestRuleType.ACM:
problems_status = profile.acm_problems_status.get("contest_problems", {}) problems_status = profile.acm_problems_status.get("contest_problems", {})

View File

@ -5,7 +5,7 @@ from .models import Submission
from problem.models import Problem, ProblemTag from problem.models import Problem, ProblemTag
from utils.api.tests import APITestCase from utils.api.tests import APITestCase
DEFAULT_PROBLEM_DATA = {"_id": "110", "title": "test", "description": "<p>test</p>", "input_description": "test", DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "<p>test</p>", "input_description": "test",
"output_description": "test", "time_limit": 1000, "memory_limit": 256, "difficulty": "Low", "output_description": "test", "time_limit": 1000, "memory_limit": 256, "difficulty": "Low",
"visible": True, "tags": ["test"], "languages": ["C", "C++", "Java", "Python2"], "template": {}, "visible": True, "tags": ["test"], "languages": ["C", "C++", "Java", "Python2"], "template": {},
"samples": [{"input": "test", "output": "test"}], "spj": False, "spj_language": "C", "samples": [{"input": "test", "output": "test"}], "spj": False, "spj_language": "C",
@ -25,13 +25,28 @@ DEFAULT_SUBMISSION_DATA = {
"language": "C", "language": "C",
"statistic_info": {} "statistic_info": {}
} }
# todo contest submission
class SubmissionListTest(APITestCase): class SubmissionPrepare(APITestCase):
def _create_problem_and_submission(self):
user = self.create_admin("test", "test123", login=False)
problem_data = deepcopy(DEFAULT_PROBLEM_DATA)
problem_data.pop("tags")
problem_data["created_by"] = user
self.problem = Problem.objects.create(**problem_data)
for tag in DEFAULT_PROBLEM_DATA["tags"]:
tag = ProblemTag.objects.create(name=tag)
self.problem.tags.add(tag)
self.problem.save()
self.submission = Submission.objects.create(**DEFAULT_SUBMISSION_DATA)
class SubmissionListTest(SubmissionPrepare):
def setUp(self): def setUp(self):
self._create_problem_and_submission()
self.create_user("123", "345") self.create_user("123", "345")
self.url = self.reverse("submission_list_api") self.url = self.reverse("submission_list_api")
Submission.objects.create(**DEFAULT_SUBMISSION_DATA)
def test_get_submission_list(self): def test_get_submission_list(self):
resp = self.client.get(self.url, data={"limit": "10"}) resp = self.client.get(self.url, data={"limit": "10"})
@ -39,16 +54,10 @@ class SubmissionListTest(APITestCase):
@mock.patch("submission.views.oj.judge_task.delay") @mock.patch("submission.views.oj.judge_task.delay")
class SubmissionAPITest(APITestCase): class SubmissionAPITest(SubmissionPrepare):
def setUp(self): def setUp(self):
self.user = self.create_user("test", "test123") self._create_problem_and_submission()
tag = ProblemTag.objects.create(name="test") self.user = self.create_user("123", "test123")
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") self.url = self.reverse("submission_api")
def test_create_submission(self, judge_task): def test_create_submission(self, judge_task):

View File

@ -1,8 +1,9 @@
from django.conf.urls import url from django.conf.urls import url
from ..views.oj import SubmissionAPI, SubmissionListAPI from ..views.oj import SubmissionAPI, SubmissionListAPI, ContestSubmissionListAPI
urlpatterns = [ urlpatterns = [
url(r"^submission/?$", SubmissionAPI.as_view(), name="submission_api"), url(r"^submission/?$", SubmissionAPI.as_view(), name="submission_api"),
url(r"^submissions/?$", SubmissionListAPI.as_view(), name="submission_list_api") url(r"^submissions/?$", SubmissionListAPI.as_view(), name="submission_list_api"),
url(r"^contest_submissions/?$", ContestSubmissionListAPI.as_view(), name="contest_submission_list_api"),
] ]

View File

@ -1,9 +1,8 @@
from account.models import AdminType
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 from problem.models import Problem, ProblemRuleType
from contest.models import Contest, ContestStatus from contest.models import Contest, ContestStatus, ContestRuleType
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
from ..models import Submission from ..models import Submission
@ -82,36 +81,15 @@ class SubmissionListAPI(APIView):
if not request.GET.get("limit"): if not request.GET.get("limit"):
return self.error("Limit is needed") 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.error("Parameter error")
submissions = Submission.objects.filter(contest_id__isnull=True) submissions = Submission.objects.filter(contest_id__isnull=True)
return self.process_submissions(request, submissions)
@check_contest_permission
def _get_contest_submission_list(self, request):
contest = self.contest
# todo OI mode
submissions = Submission.objects.filter(contest_id=contest.id)
# filter the test submissions submitted before contest start
if contest.status != ContestStatus.CONTEST_NOT_START:
print(contest.start_time)
submissions = submissions.filter(create_time__gte=contest.start_time)
# 封榜的时候只能看到自己的提交
if not contest.real_time_rank:
if request.user and not (
request.user.admin_type == AdminType.SUPER_ADMIN or request.user == contest.created_by):
submissions = submissions.filter(user_id=request.user.id)
return self.process_submissions(request, submissions)
def process_submissions(self, request, submissions):
problem_id = request.GET.get("problem_id") problem_id = request.GET.get("problem_id")
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:
try: try:
problem = Problem.objects.get(_id=problem_id, visible=True) problem = Problem.objects.get(_id=problem_id, contest_id__isnull=True, visible=True)
except Problem.DoesNotExist: except Problem.DoesNotExist:
return self.error("Problem doesn't exist") return self.error("Problem doesn't exist")
submissions = submissions.filter(problem=problem) submissions = submissions.filter(problem=problem)
@ -122,3 +100,42 @@ class SubmissionListAPI(APIView):
data = self.paginate_data(request, submissions) data = self.paginate_data(request, submissions)
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
return self.success(data) return self.success(data)
class ContestSubmissionListAPI(APIView):
@check_contest_permission
def get(self, request):
if not request.GET.get("limit"):
return self.error("Limit is needed")
contest = self.contest
if contest.rule_type == ContestRuleType.OI and not contest.is_contest_admin(request.user):
return self.error("No permission for OI contest submissions")
submissions = Submission.objects.filter(contest_id=contest.id)
problem_id = request.GET.get("problem_id")
myself = request.GET.get("myself")
result = request.GET.get("result")
if problem_id:
try:
problem = Problem.objects.get(_id=problem_id, contest_id=contest.id, visible=True)
except Problem.DoesNotExist:
return self.error("Problem doesn't exist")
submissions = submissions.filter(problem=problem)
if myself and myself == "1":
submissions = submissions.filter(user_id=request.user.id)
if result:
submissions = submissions.filter(result=result)
# filter the test submissions submitted before contest start
if contest.status != ContestStatus.CONTEST_NOT_START:
submissions = submissions.filter(create_time__gte=contest.start_time)
# 封榜的时候只能看到自己的提交
if not contest.real_time_rank and not contest.is_contest_admin(request.user):
submissions = submissions.filter(user_id=request.user.id)
data = self.paginate_data(request, submissions)
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
return self.success(data)