OnlineJudge/submission/views/oj.py

196 lines
8.9 KiB
Python

from django.conf import settings
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
from contest.models import Contest, ContestStatus, ContestRuleType
from utils.api import APIView, validate_serializer
from utils.throttling import TokenBucket, BucketController
from utils.captcha import Captcha
from utils.cache import cache
from ..models import Submission
from ..serializers import (CreateSubmissionSerializer, SubmissionModelSerializer,
ShareSubmissionSerializer)
from ..serializers import SubmissionSafeModelSerializer, SubmissionListSerializer
class SubmissionAPI(APIView):
def throttling(self, request):
user_controller = BucketController(factor=request.user.id,
redis_conn=cache,
default_capacity=settings.TOKEN_BUCKET_DEFAULT_CAPACITY)
user_bucket = TokenBucket(fill_rate=settings.TOKEN_BUCKET_FILL_RATE,
capacity=settings.TOKEN_BUCKET_DEFAULT_CAPACITY,
last_capacity=user_controller.last_capacity,
last_timestamp=user_controller.last_timestamp)
if user_bucket.consume():
user_controller.last_capacity -= 1
else:
return "Please wait %d seconds" % int(user_bucket.expected_time() + 1)
ip_controller = BucketController(factor=request.session["ip"],
redis_conn=cache,
default_capacity=settings.TOKEN_BUCKET_DEFAULT_CAPACITY * 3)
ip_bucket = TokenBucket(fill_rate=settings.TOKEN_BUCKET_FILL_RATE * 3,
capacity=settings.TOKEN_BUCKET_DEFAULT_CAPACITY * 3,
last_capacity=ip_controller.last_capacity,
last_timestamp=ip_controller.last_timestamp)
if ip_bucket.consume():
ip_controller.last_capacity -= 1
else:
return "Captcha is required"
@validate_serializer(CreateSubmissionSerializer)
@login_required
def post(self, request):
data = request.data
hide_id = False
if data.get("contest_id"):
try:
contest = Contest.objects.get(id=data["contest_id"])
except Contest.DoesNotExist:
return self.error("Contest doesn't exist.")
if contest.status == ContestStatus.CONTEST_ENDED:
return self.error("The contest have ended")
if contest.status == ContestStatus.CONTEST_NOT_START and not contest.is_contest_admin(request.user):
return self.error("Contest have not started")
if not contest.problem_details_permission(request.user):
hide_id = True
if data.get("captcha"):
if not Captcha(request).check(data["captcha"]):
return self.error("Invalid captcha")
error = self.throttling(request)
if error:
return self.error(error)
try:
problem = Problem.objects.get(id=data["problem_id"],
contest_id=data.get("contest_id"),
visible=True)
except Problem.DoesNotExist:
return self.error("Problem not exist")
submission = Submission.objects.create(user_id=request.user.id,
username=request.user.username,
language=data["language"],
code=data["code"],
problem_id=problem.id,
contest_id=data.get("contest_id"))
# use this for debug
# JudgeDispatcher(submission.id, problem.id).judge()
judge_task.delay(submission.id, problem.id)
if hide_id:
return self.success()
else:
return self.success({"submission_id": submission.id})
@login_required
def get(self, request):
submission_id = request.GET.get("id")
if not submission_id:
return self.error("Parameter id doesn't exist")
try:
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.problem.rule_type == ProblemRuleType.ACM:
submission_data = SubmissionSafeModelSerializer(submission).data
else:
submission_data = SubmissionModelSerializer(submission).data
# 是否有权限取消共享
submission_data["can_unshare"] = submission.check_user_permission(request.user, check_share=False)
return self.success(submission_data)
@validate_serializer(ShareSubmissionSerializer)
@login_required
def put(self, request):
"""
share submission
"""
try:
submission = Submission.objects.select_related("problem").get(id=request.data["id"])
except Submission.DoesNotExist:
return self.error("Submission doesn't exist")
if not submission.check_user_permission(request.user, check_share=False):
return self.error("No permission to share the submission")
if submission.contest and submission.contest.status == ContestStatus.CONTEST_UNDERWAY:
return self.error("Can not share submission now")
submission.shared = request.data["shared"]
submission.save(update_fields=["shared"])
return self.success()
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.error("Parameter error")
submissions = Submission.objects.filter(contest_id__isnull=True).select_related("problem__created_by")
problem_id = request.GET.get("problem_id")
myself = request.GET.get("myself")
result = request.GET.get("result")
username = request.GET.get("username")
if problem_id:
try:
problem = Problem.objects.get(_id=problem_id, contest_id__isnull=True, 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)
elif username:
submissions = submissions.filter(username=username)
if result:
submissions = submissions.filter(result=result)
data = self.paginate_data(request, submissions)
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
return self.success(data)
class ContestSubmissionListAPI(APIView):
@check_contest_permission(check_type="submissions")
def get(self, request):
if not request.GET.get("limit"):
return self.error("Limit is needed")
contest = self.contest
submissions = Submission.objects.filter(contest_id=contest.id).select_related("problem__created_by")
problem_id = request.GET.get("problem_id")
myself = request.GET.get("myself")
result = request.GET.get("result")
username = request.GET.get("username")
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)
elif username:
submissions = submissions.filter(username=username)
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 contest.rule_type == ContestRuleType.ACM:
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)