OnlineJudge/contest/views.py

443 lines
20 KiB
Python
Raw Normal View History

# coding=utf-8
import json
2015-08-22 08:08:39 +00:00
import datetime
2015-09-27 12:41:56 +00:00
import redis
from django.shortcuts import render
from django.db import IntegrityError
from django.utils import dateparse
from django.db.models import Q, Sum
2015-08-22 08:08:39 +00:00
from django.core.paginator import Paginator
2015-09-20 06:11:03 +00:00
from django.utils.timezone import now
2015-09-27 12:41:56 +00:00
from django.conf import settings
2015-09-20 06:11:03 +00:00
from rest_framework.views import APIView
from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, error_page, paginate_data)
from account.models import SUPER_ADMIN, User
2015-08-22 12:46:52 +00:00
from account.decorators import login_required
from group.models import Group
from .models import (Contest, ContestProblem, ContestSubmission, CONTEST_ENDED,
CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank)
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST
2015-08-23 06:31:53 +00:00
from .decorators import check_user_contest_permission
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
2015-08-22 12:46:52 +00:00
CreateContestProblemSerializer, ContestProblemSerializer,
ContestPasswordVerifySerializer,
Merge branch 'dev' into virusdefender-dev * dev: ¢ûÂ䆉∫ÜÊØî˵õÂàóË°®È°µ [ÂêéÁ´Ø]ÂéªÊéâ‰∫ÜÁ∫éÁîü‰∫ßÂ∫èÂè∑ÁöÑjavascript,Êîπ‰∏∫‰ΩøÁî®Ê®°ÊùøËøáʪ§Âô®ÂÆûÁé∞(ÊàëÁöÑÊâÄÊúâÊèê‰∫§) [ÂâçÁ´Ø]‰øÆÊîπÂÆåÂñщ∫ÜÊ∑ªÂä†ÊØî˵õÈ°µÈù¢, ÊØî˵õÂàóË°®ÂäüËÉΩ‰ªç‰∏çÂÖ®Èù¢,Á®çÂêéÊîπËøõ[CI SKIP] ‰øÆÊîπ‰∫ÜcssºïÁî®Êñπºè[CI SKIP] [ÂêéÁ´Ø]‰øÆÊîπ‰∫Ücontest‰∏≠ api-docs ÁöÑÂ∞èbug[CI SKIP] Âàõª∫ÂâçÂè∞ÊØî˵õÂàóË°® ÂéªÊéâÂÜó‰ΩôËØ≠Âè•,Âõ†‰∏∫pageÂèòÈáèÂ∑≤ÁªèÊúâȪòËƧÂĺ‰∫Ü,‰∏çËÉΩ‰∏∫Á©∫ [ÂêéÁ´Ø]ÂâçÂè∞ÊàëÁöÑÊèê‰∫§È°µÈù¢ ¢ûº∫Êèê‰∫§Â∫èÂè∑ÁöÑÊòæÁ§∫,ÂéüÊù•ÊòØÊòæÁ§∫ÁúüÂÆûidÂç≥ÈöèÊú∫ÁöÑÊï£ÂàóÂĺ,‰∏ç•ΩÁúã,Áé∞Âú®ÊîπÊàêËá™ÁÑ∂Êï∞Â∫èÂàó,‰ΩÜÈúÄ˶ÅÁªìÂêàjavascriptÁîüÊàê,‰∏îÊòØÁõ∏ÂØπÂĺ,Âõ†‰∏∫Êï∞ÊçÆÂ∫ìÈáåÊ≤°ÊúâËøô‰∏™Â≠óÊƵ,ÊúâÁÇπÂà´Êâ≠‰∫Ü. Á¨¨‰∫å,Ê∑ªÂ䆉∫ÜÁî®Êà∑Ê≤°ÊúâÊèê‰∫§ËÆ∞ÂΩïÁöÑÂèçȶà. Á¨¨‰∏â,Êú¨ÊâìÁÆó¢ûÂä†Á≠õÈÄâÂäüËÉΩ,‰ΩÜÂõ†‰∏∫URLÈö扪•Áªü‰∏ĉΩúÁΩ¢,Âè™ÊúâÂú®Â¢ûÂä†Êñ∞ÁöÑurlÊâçËÉΩËæÉ•ΩÁöѧÑÁêÜ,‰∏ãʨ°ÂÜçËØ¥Êãú [ÂâçÁ´Ø]ÊØî˵õÂàóË°®È°µÈù¢(ÂêéÂè∞)ÁöÑËøõ‰∏ÄÊ≠•ÂÆåÂñÑ,‰∏çÂåÖÂê´api [ÂêéÁ´Ø]‰øÆÊîπ‰∫ÜÊàëÁöÑÊèê‰∫§ÂàóË°®ÁöÑÊ®°Êùøʆ∑ºè,Êï¥ÁêÜʆºÂºè [ÂêéÁ´Ø]‰øÆÊîπÊàëÁöÑÊèê‰∫§È°µÈù¢,ÂéªÊéâ‰∫ÜÂÜó‰ΩôËØ≠Âè•,Âπ∂Ê∑ªÂä†ÊµãËØï [ÂêéÁ´Ø-ÂâçÂè∞]Ê∑ªÂ䆉∫ÜsubmissionsÂàÜÈ°µÊòæÁ§∫(Âè™ÊòæÁ§∫ÂΩìÂâçÁî®Êà∑ÁöÑÊèê‰∫§),Ë∞ÉÁî®Â∑≤ÊúâÁöÑviewÂÆåÊàêÂçï‰∏™submissionÁöÑÊòæÁ§∫.ÊòæÁ§∫ÁïåÈù¢‰∏éÈóÆÈ¢òÂàÜÈ°µÊòæÁ§∫Áªü‰∏Ä.ÈóÆÈ¢òÊòØidÁöÑÊòæÁ§∫.url:http://127.0.0.1:8000/my_submissions/ [ÂâçÁ´Ø]‰øÆÊîπÊ∑ªÂä†ÊØî˵õÈ°µÈù¢,Êñ∞¢û‰∫܉ΩøÁî®Â∞èÁªÑapiÊü•ËØ¢ËØ•Áî®Êà∑ÊâÄÂàõª∫ÁöÑÊâÄÊúâÁöÑÂ∞èÁªÑÁöÑÂäüËÉΩ[CI SKIP] [ÂâçÁ´Ø]Ê∑ªÂä†ÊØî˵õÈ°µÈù¢Ëøõ‰∏ÄÊ≠•ÂÆåÂñÑ,Ê∑ªÂä†Â≠óÊƵÂåÖÊã¨ÊòØÂê¶ÊòæÁ§∫Êèê‰∫§,ÊØî˵õÊ®°Âºè,ÈóÆÈ¢òÂàÜÂĺ,ÂÖÅËÆ∏ÂèÇÂä†ÊØî˵õÁöÑÁî®Êà∑ÁªÑ,Âπ∂ÂÆåÂñÑÂÜÖÈÉ®ÈĪËæë,Âü∫Êú¨ÂèØÁ∫Ü,Âè™ÊòØÊ≤°ÂÜôajaxÊèê‰∫§Êï∞ÊçÆ,ÂíåÂïÊãâÂÂèñÂ∞èÁªÑ‰ø°ÊÅØÁöÑÈÉ®ÂàÜ[CI SKIP] [ÂâçÁ´Ø]‰øÆÊîπ‰∫ÜÊ∑ªÂä†ÊØî˵õÈ°µÁöÑÂΩ¢ÂºèÁªìÊûÑ,‰ªçÊúâbug[CI SKIP] Ê∑[ÂâçÁ´Ø]Ê∑ªÂä†ÊØîËÂêéÂè∞ÊØî˵õÂàóË°®[CI SKIP] [ÂâçÁ´Ø]Áªü‰∏ÄÈóÆÈ¢òÈá,ÊØî˵õÂàóË°®jsÁöÑÊñቪ∂Âêç_list.js. Ê∑ªÂ䆉∫ÜÊØî˵õÂàóË°®ÂíåÁºñËæëÊØî˵õÁöÑÈ°µÈù¢(§߉Ωì§ʆ∑ºè)[CI SKIP] Conflicts: contest/views.py
2015-08-22 12:49:42 +00:00
EditContestProblemSerializer)
from oj.settings import REDIS_CACHE
import redis
class ContestAdminAPIView(APIView):
def post(self, request):
"""
比赛发布json api接口
---
request_serializer: CreateContestSerializer
response_serializer: ContestSerializer
"""
serializer = CreateContestSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
groups = []
# 首先判断比赛的类型: 0 即为是小组赛(GROUP_CONTEST)1 即为是无密码的公开赛(PUBLIC_CONTEST)
# 2 即为是有密码的公开赛(PASSWORD_PUBLIC_CONTEST)
# 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛
if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]:
2015-08-19 09:53:43 +00:00
if request.user.admin_type != SUPER_ADMIN:
return error_response(u"只有超级管理员才可创建公开赛")
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
if not data["password"]:
2015-08-19 09:53:43 +00:00
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
# 没有密码的公开赛 没有密码的小组赛
elif data["contest_type"] == GROUP_CONTEST:
2015-08-19 09:53:43 +00:00
if request.user.admin_type == SUPER_ADMIN:
groups = Group.objects.filter(id__in=data["groups"])
else:
2015-08-19 09:53:43 +00:00
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
if not groups.count():
return error_response(u"请至少选择一个小组")
if data["start_time"] >= data["end_time"]:
return error_response(u"比赛的开始时间必须早于比赛结束的时间")
try:
contest = Contest.objects.create(title=data["title"], description=data["description"],
mode=data["mode"], contest_type=data["contest_type"],
real_time_rank=data["real_time_rank"], password=data["password"],
show_user_submission=data["show_user_submission"],
start_time=dateparse.parse_datetime(data["start_time"]),
end_time=dateparse.parse_datetime(data["end_time"]),
created_by=request.user, visible=data["visible"])
except IntegrityError:
return error_response(u"比赛名已经存在")
contest.groups.add(*groups)
return success_response(ContestSerializer(contest).data)
else:
2015-08-19 09:53:43 +00:00
return serializer_invalid_response(serializer)
def put(self, request):
"""
比赛编辑json api接口
---
request_serializer: EditContestSerializer
response_serializer: ContestSerializer
"""
serializer = EditContestSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
2015-08-19 09:53:43 +00:00
groups = []
try:
# 超级管理员可以编辑所有的
contest = Contest.objects.get(id=data["id"])
if request.user.admin_type != SUPER_ADMIN:
contest = contest.get(created_by=request.user)
except Contest.DoesNotExist:
2015-08-19 09:53:43 +00:00
return error_response(u"该比赛不存在!")
try:
2015-08-19 09:53:43 +00:00
contest = Contest.objects.get(title=data["title"])
if contest.id != data["id"]:
return error_response(u"该比赛名称已经存在")
except Contest.DoesNotExist:
pass
if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]:
2015-08-19 09:53:43 +00:00
if request.user.admin_type != SUPER_ADMIN:
return error_response(u"只有超级管理员才可创建公开赛")
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
2015-08-19 09:53:43 +00:00
if not data["password"]:
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
elif data["contest_type"] == GROUP_CONTEST:
2015-08-19 09:53:43 +00:00
if request.user.admin_type == SUPER_ADMIN:
groups = Group.objects.filter(id__in=data["groups"])
else:
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
if not groups.count():
return error_response(u"请至少选择一个小组")
if data["start_time"] >= data["end_time"]:
return error_response(u"比赛的开始时间必须早于比赛结束的时间")
2015-10-10 02:36:45 +00:00
# 之前是封榜,现在要开放,需要清除缓存
if contest.real_time_rank == True and data["real_time_rank"] == False:
r = redis.Redis(host=settings.REDIS_CACHE["host"], port=settings.REDIS_CACHE["port"], db=settings.REDIS_CACHE["db"])
cache_key = str(contest.id) + "_rank_cache"
r.delete(cache_key)
2015-08-19 09:53:43 +00:00
contest.title = data["title"]
contest.description = data["description"]
contest.mode = data["mode"]
contest.contest_type = data["contest_type"]
contest.real_time_rank = data["real_time_rank"]
2015-08-19 09:53:43 +00:00
contest.show_user_submission = data["show_user_submission"]
contest.start_time = dateparse.parse_datetime(data["start_time"])
contest.end_time = dateparse.parse_datetime(data["end_time"])
contest.visible = data["visible"]
2015-08-19 09:53:43 +00:00
contest.password = data["password"]
contest.save()
2015-08-19 09:53:43 +00:00
contest.groups.clear()
contest.groups.add(*groups)
return success_response(ContestSerializer(contest).data)
else:
2015-08-19 09:53:43 +00:00
return serializer_invalid_response(serializer)
def get(self, request):
"""
比赛分页json api接口
---
response_serializer: ContestSerializer
"""
if request.user.admin_type == SUPER_ADMIN:
2015-08-25 05:34:55 +00:00
contest = Contest.objects.all().order_by("-create_time")
else:
2015-08-25 05:34:55 +00:00
contest = Contest.objects.filter(created_by=request.user).order_by("-create_time")
visible = request.GET.get("visible", None)
if visible:
contest = contest.filter(visible=(visible == "true"))
keyword = request.GET.get("keyword", None)
if keyword:
contest = contest.filter(Q(title__contains=keyword) |
Q(description__contains=keyword))
return paginate(request, contest, ContestSerializer)
class ContestProblemAdminAPIView(APIView):
def post(self, request):
"""
比赛题目发布json api接口
---
request_serializer: CreateContestProblemSerializer
response_serializer: ContestProblemSerializer
"""
serializer = CreateContestProblemSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
contest = Contest.objects.get(id=data["contest_id"])
if request.user.admin_type != SUPER_ADMIN:
contest = contest.get(created_by=request.user)
except Contest.DoesNotExist:
return error_response(u"比赛不存在")
contest_problem = ContestProblem.objects.create(title=data["title"],
description=data["description"],
input_description=data["input_description"],
output_description=data["output_description"],
test_case_id=data["test_case_id"],
samples=json.dumps(data["samples"]),
time_limit=data["time_limit"],
memory_limit=data["memory_limit"],
created_by=request.user,
hint=data["hint"],
contest=contest,
sort_index=data["sort_index"],
score=data["score"])
return success_response(ContestProblemSerializer(contest_problem).data)
else:
2015-08-19 09:53:43 +00:00
return serializer_invalid_response(serializer)
def put(self, request):
"""
比赛题目编辑json api接口
---
request_serializer: EditContestProblemSerializer
response_serializer: ContestProblemSerializer
"""
serializer = EditContestProblemSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
contest_problem = ContestProblem.objects.get(id=data["id"])
except ContestProblem.DoesNotExist:
return error_response(u"该比赛题目不存在!")
contest = Contest.objects.get(id=contest_problem.contest_id)
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
return error_response(u"比赛不存在")
contest_problem.title = data["title"]
contest_problem.description = data["description"]
contest_problem.input_description = data["input_description"]
contest_problem.output_description = data["output_description"]
contest_problem.test_case_id = data["test_case_id"]
contest_problem.time_limit = data["time_limit"]
contest_problem.memory_limit = data["memory_limit"]
contest_problem.samples = json.dumps(data["samples"])
contest_problem.hint = data["hint"]
contest_problem.visible = data["visible"]
contest_problem.sort_index = data["sort_index"]
contest_problem.score = data["score"]
contest_problem.save()
return success_response(ContestProblemSerializer(contest_problem).data)
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
比赛题目分页json api接口
---
2015-08-22 12:42:21 +00:00
response_serializer: ContestProblemSerializer
"""
contest_problem_id = request.GET.get("contest_problem_id", None)
if contest_problem_id:
try:
2015-08-19 09:53:43 +00:00
contest_problem = ContestProblem.objects.get(id=contest_problem_id)
if request.user.admin_type != SUPER_ADMIN:
contest_problem = contest_problem.get(created_by=request.user)
return success_response(ContestProblemSerializer(contest_problem).data)
2015-08-19 09:53:43 +00:00
except ContestProblem.DoesNotExist:
return error_response(u"比赛题目不存在")
contest_problems = ContestProblem.objects.all().order_by("sort_index")
if request.user.admin_type != SUPER_ADMIN:
contest_problems = contest_problems.filter(created_by=request.user).order_by("sort_index")
visible = request.GET.get("visible", None)
if visible:
contest_problems = contest_problems.filter(visible=(visible == "true"))
keyword = request.GET.get("keyword", None)
if keyword:
contest_problems = contest_problems.filter(Q(title__contains=keyword) |
Q(description__contains=keyword))
contest_id = request.GET.get("contest_id", None)
if contest_id:
contest_problems = contest_problems.filter(contest__id=contest_id).order_by("sort_index")
return paginate(request, contest_problems, ContestProblemSerializer)
2015-08-22 12:46:52 +00:00
class ContestPasswordVerifyAPIView(APIView):
@login_required
def post(self, request):
serializer = ContestPasswordVerifySerializer(data=request.data)
if serializer.is_valid():
data = request.data
try:
contest = Contest.objects.get(id=data["contest_id"], contest_type=PASSWORD_PROTECTED_CONTEST)
2015-08-22 12:46:52 +00:00
except Contest.DoesNotExist:
2015-09-05 12:07:31 +00:00
return error_response(u"比赛不存在")
2015-08-22 12:46:52 +00:00
if data["password"] != contest.password:
2015-09-05 12:07:31 +00:00
return error_response(u"密码错误")
2015-08-22 12:46:52 +00:00
else:
if "contests" not in request.session:
request.session["contests"] = []
request.session["contests"].append(int(data["contest_id"]))
# https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved
request.session.modified = True
2015-08-22 12:46:52 +00:00
return success_response(True)
else:
return serializer_invalid_response(serializer)
2015-08-23 06:31:53 +00:00
@check_user_contest_permission
2015-08-22 12:46:52 +00:00
def contest_page(request, contest_id):
"""
单个比赛的详情页
"""
2015-09-05 12:07:31 +00:00
contest = Contest.objects.get(id=contest_id)
2015-08-22 12:46:52 +00:00
Merge branch 'dev' into virusdefender-dev * dev: ¢ûÂ䆉∫ÜÊØî˵õÂàóË°®È°µ [ÂêéÁ´Ø]ÂéªÊéâ‰∫ÜÁ∫éÁîü‰∫ßÂ∫èÂè∑ÁöÑjavascript,Êîπ‰∏∫‰ΩøÁî®Ê®°ÊùøËøáʪ§Âô®ÂÆûÁé∞(ÊàëÁöÑÊâÄÊúâÊèê‰∫§) [ÂâçÁ´Ø]‰øÆÊîπÂÆåÂñщ∫ÜÊ∑ªÂä†ÊØî˵õÈ°µÈù¢, ÊØî˵õÂàóË°®ÂäüËÉΩ‰ªç‰∏çÂÖ®Èù¢,Á®çÂêéÊîπËøõ[CI SKIP] ‰øÆÊîπ‰∫ÜcssºïÁî®Êñπºè[CI SKIP] [ÂêéÁ´Ø]‰øÆÊîπ‰∫Ücontest‰∏≠ api-docs ÁöÑÂ∞èbug[CI SKIP] Âàõª∫ÂâçÂè∞ÊØî˵õÂàóË°® ÂéªÊéâÂÜó‰ΩôËØ≠Âè•,Âõ†‰∏∫pageÂèòÈáèÂ∑≤ÁªèÊúâȪòËƧÂĺ‰∫Ü,‰∏çËÉΩ‰∏∫Á©∫ [ÂêéÁ´Ø]ÂâçÂè∞ÊàëÁöÑÊèê‰∫§È°µÈù¢ ¢ûº∫Êèê‰∫§Â∫èÂè∑ÁöÑÊòæÁ§∫,ÂéüÊù•ÊòØÊòæÁ§∫ÁúüÂÆûidÂç≥ÈöèÊú∫ÁöÑÊï£ÂàóÂĺ,‰∏ç•ΩÁúã,Áé∞Âú®ÊîπÊàêËá™ÁÑ∂Êï∞Â∫èÂàó,‰ΩÜÈúÄ˶ÅÁªìÂêàjavascriptÁîüÊàê,‰∏îÊòØÁõ∏ÂØπÂĺ,Âõ†‰∏∫Êï∞ÊçÆÂ∫ìÈáåÊ≤°ÊúâËøô‰∏™Â≠óÊƵ,ÊúâÁÇπÂà´Êâ≠‰∫Ü. Á¨¨‰∫å,Ê∑ªÂ䆉∫ÜÁî®Êà∑Ê≤°ÊúâÊèê‰∫§ËÆ∞ÂΩïÁöÑÂèçȶà. Á¨¨‰∏â,Êú¨ÊâìÁÆó¢ûÂä†Á≠õÈÄâÂäüËÉΩ,‰ΩÜÂõ†‰∏∫URLÈö扪•Áªü‰∏ĉΩúÁΩ¢,Âè™ÊúâÂú®Â¢ûÂä†Êñ∞ÁöÑurlÊâçËÉΩËæÉ•ΩÁöѧÑÁêÜ,‰∏ãʨ°ÂÜçËØ¥Êãú [ÂâçÁ´Ø]ÊØî˵õÂàóË°®È°µÈù¢(ÂêéÂè∞)ÁöÑËøõ‰∏ÄÊ≠•ÂÆåÂñÑ,‰∏çÂåÖÂê´api [ÂêéÁ´Ø]‰øÆÊîπ‰∫ÜÊàëÁöÑÊèê‰∫§ÂàóË°®ÁöÑÊ®°Êùøʆ∑ºè,Êï¥ÁêÜʆºÂºè [ÂêéÁ´Ø]‰øÆÊîπÊàëÁöÑÊèê‰∫§È°µÈù¢,ÂéªÊéâ‰∫ÜÂÜó‰ΩôËØ≠Âè•,Âπ∂Ê∑ªÂä†ÊµãËØï [ÂêéÁ´Ø-ÂâçÂè∞]Ê∑ªÂ䆉∫ÜsubmissionsÂàÜÈ°µÊòæÁ§∫(Âè™ÊòæÁ§∫ÂΩìÂâçÁî®Êà∑ÁöÑÊèê‰∫§),Ë∞ÉÁî®Â∑≤ÊúâÁöÑviewÂÆåÊàêÂçï‰∏™submissionÁöÑÊòæÁ§∫.ÊòæÁ§∫ÁïåÈù¢‰∏éÈóÆÈ¢òÂàÜÈ°µÊòæÁ§∫Áªü‰∏Ä.ÈóÆÈ¢òÊòØidÁöÑÊòæÁ§∫.url:http://127.0.0.1:8000/my_submissions/ [ÂâçÁ´Ø]‰øÆÊîπÊ∑ªÂä†ÊØî˵õÈ°µÈù¢,Êñ∞¢û‰∫܉ΩøÁî®Â∞èÁªÑapiÊü•ËØ¢ËØ•Áî®Êà∑ÊâÄÂàõª∫ÁöÑÊâÄÊúâÁöÑÂ∞èÁªÑÁöÑÂäüËÉΩ[CI SKIP] [ÂâçÁ´Ø]Ê∑ªÂä†ÊØî˵õÈ°µÈù¢Ëøõ‰∏ÄÊ≠•ÂÆåÂñÑ,Ê∑ªÂä†Â≠óÊƵÂåÖÊã¨ÊòØÂê¶ÊòæÁ§∫Êèê‰∫§,ÊØî˵õÊ®°Âºè,ÈóÆÈ¢òÂàÜÂĺ,ÂÖÅËÆ∏ÂèÇÂä†ÊØî˵õÁöÑÁî®Êà∑ÁªÑ,Âπ∂ÂÆåÂñÑÂÜÖÈÉ®ÈĪËæë,Âü∫Êú¨ÂèØÁ∫Ü,Âè™ÊòØÊ≤°ÂÜôajaxÊèê‰∫§Êï∞ÊçÆ,ÂíåÂïÊãâÂÂèñÂ∞èÁªÑ‰ø°ÊÅØÁöÑÈÉ®ÂàÜ[CI SKIP] [ÂâçÁ´Ø]‰øÆÊîπ‰∫ÜÊ∑ªÂä†ÊØî˵õÈ°µÁöÑÂΩ¢ÂºèÁªìÊûÑ,‰ªçÊúâbug[CI SKIP] Ê∑[ÂâçÁ´Ø]Ê∑ªÂä†ÊØîËÂêéÂè∞ÊØî˵õÂàóË°®[CI SKIP] [ÂâçÁ´Ø]Áªü‰∏ÄÈóÆÈ¢òÈá,ÊØî˵õÂàóË°®jsÁöÑÊñቪ∂Âêç_list.js. Ê∑ªÂ䆉∫ÜÊØî˵õÂàóË°®ÂíåÁºñËæëÊØî˵õÁöÑÈ°µÈù¢(§߉Ωì§ʆ∑ºè)[CI SKIP] Conflicts: contest/views.py
2015-08-22 12:49:42 +00:00
return render(request, "oj/contest/contest_index.html", {"contest": contest})
2015-08-23 12:45:51 +00:00
@check_user_contest_permission
def contest_problem_page(request, contest_id, contest_problem_id):
"""
单个比赛题目的详情页
"""
2015-09-05 12:07:31 +00:00
contest = Contest.objects.get(id=contest_id)
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except ContestProblem.DoesNotExist:
return error_page(request, u"比赛题目不存在")
warning = u"您已经提交过本题的正确答案,重复提交可能造成时间累计。"
2015-08-23 12:45:51 +00:00
show_warning = False
try:
submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem)
show_warning = submission.ac
except ContestSubmission.DoesNotExist:
pass
Merge branch 'dev' into virusdefender-dev * dev: (21 commits) [前端]整理格式,去掉tab(以前用vim,它自己给加的),去掉调试用的console.log[CI SKIP] [前端]统一admin中js命名方式. 为提交列表添加返回按钮[CI SKIP] [前端]修复bug,更正了不恰当的foreach循环,(js里for(var key in array)不仅遍历了数组元素,还将遍历数组其他的属性以及成员方法),修复了显示编辑区函数对选中小组错误的清除方法.(原来的做法将导致某些情况下旧的小组无法移除编辑区域. 增添了切换编辑比赛的提示,防止用户丢失为保存的信息. 添加问题列表对可见比赛的筛选[CI SKIP] [前端-BUG]修复比赛编辑区可见状态显示错误,(忘记加vm.),增加编辑成功隐藏编辑框的行为,更加方便[CI SKIP] [前端]添加比赛题目列表可见字段的显示,方便比赛管理[CI SKIP] [BUG-fix]返回按钮提示确认,修复不能弹出的问题[CI SKIP] 修复typo in submission/views.py Swagger UI docs中的拼写错误[CI SKIP] [前端]修复userList.js中关于翻页按钮状态控制函数参数的错误. 修复刚刚提交的bug[CI SKIP] [前端]修复userList页面avalon重定义问题[CI SKIP] [前端]修复问题管理(后台)页面的avalon重复定义的问题[CI SKIP] [前端]整理js格式. 修复小bugs,关于比赛密码修改变量名称的错误,小组修改变量名称错误(以上都是在修改比赛页面内)[CI SKIP] [后台]修复contestAdmin,比赛和问题API的逻辑问题,主要针对超级管理员和普通管理员的差别.写了测试,是两个api测试覆盖率达100% [migration]改model漏了一个.....[CI SKIP] [前端-后台]比赛管理,对添加,编辑,列表页面的avalon使用方法做了统一的改变,防止出现页内模板改变但页面不刷新的情况下导致avalon功能间歇性异常的问题,但是代码量变大了一些,还算是整洁.具体是所有页面的avalon只在页面第一次加载的时候初始化,再次加载时只对vm内部变量重新初始化,而不调用avalon.define了[CI SKIP] [后端]添加修改比赛题目添加对题目分数的支持 [后端]为比赛problem model添加分数(score)字段,用于记分模式的比赛 [后端]修复typo,工作正常,没写测试还 [前端]修改比赛列表页面,添加了编辑比赛,编辑比赛题目[CI SKIP] [前端]把添加比赛和添加比赛问题分开了,就是把添加问题模块从添加比赛页面删除了 [前端]添加了后台比赛列表对问题的添加修改页面[CI SKIP] ... Conflicts: static/src/js/app/admin/problem/editProblem.js static/src/js/app/admin/problem/submissionList.js submission/views.py
2015-08-25 04:49:05 +00:00
# 已经结束
if contest.status == CONTEST_ENDED:
Merge branch 'dev' into virusdefender-dev * dev: (21 commits) [前端]整理格式,去掉tab(以前用vim,它自己给加的),去掉调试用的console.log[CI SKIP] [前端]统一admin中js命名方式. 为提交列表添加返回按钮[CI SKIP] [前端]修复bug,更正了不恰当的foreach循环,(js里for(var key in array)不仅遍历了数组元素,还将遍历数组其他的属性以及成员方法),修复了显示编辑区函数对选中小组错误的清除方法.(原来的做法将导致某些情况下旧的小组无法移除编辑区域. 增添了切换编辑比赛的提示,防止用户丢失为保存的信息. 添加问题列表对可见比赛的筛选[CI SKIP] [前端-BUG]修复比赛编辑区可见状态显示错误,(忘记加vm.),增加编辑成功隐藏编辑框的行为,更加方便[CI SKIP] [前端]添加比赛题目列表可见字段的显示,方便比赛管理[CI SKIP] [BUG-fix]返回按钮提示确认,修复不能弹出的问题[CI SKIP] 修复typo in submission/views.py Swagger UI docs中的拼写错误[CI SKIP] [前端]修复userList.js中关于翻页按钮状态控制函数参数的错误. 修复刚刚提交的bug[CI SKIP] [前端]修复userList页面avalon重定义问题[CI SKIP] [前端]修复问题管理(后台)页面的avalon重复定义的问题[CI SKIP] [前端]整理js格式. 修复小bugs,关于比赛密码修改变量名称的错误,小组修改变量名称错误(以上都是在修改比赛页面内)[CI SKIP] [后台]修复contestAdmin,比赛和问题API的逻辑问题,主要针对超级管理员和普通管理员的差别.写了测试,是两个api测试覆盖率达100% [migration]改model漏了一个.....[CI SKIP] [前端-后台]比赛管理,对添加,编辑,列表页面的avalon使用方法做了统一的改变,防止出现页内模板改变但页面不刷新的情况下导致avalon功能间歇性异常的问题,但是代码量变大了一些,还算是整洁.具体是所有页面的avalon只在页面第一次加载的时候初始化,再次加载时只对vm内部变量重新初始化,而不调用avalon.define了[CI SKIP] [后端]添加修改比赛题目添加对题目分数的支持 [后端]为比赛problem model添加分数(score)字段,用于记分模式的比赛 [后端]修复typo,工作正常,没写测试还 [前端]修改比赛列表页面,添加了编辑比赛,编辑比赛题目[CI SKIP] [前端]把添加比赛和添加比赛问题分开了,就是把添加问题模块从添加比赛页面删除了 [前端]添加了后台比赛列表对问题的添加修改页面[CI SKIP] ... Conflicts: static/src/js/app/admin/problem/editProblem.js static/src/js/app/admin/problem/submissionList.js submission/views.py
2015-08-25 04:49:05 +00:00
show_warning = True
warning = u"比赛已经结束"
elif contest.status == CONTEST_NOT_START:
show_warning = True
warning = u"比赛没有开始,您是管理员,可以提交和测试题目,但是目前的提交不会计入排名。"
show_submit_code_area = False
if contest.status == CONTEST_UNDERWAY:
show_submit_code_area = True
if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by:
show_submit_code_area = True
Merge branch 'dev' into virusdefender-dev * dev: (21 commits) [前端]整理格式,去掉tab(以前用vim,它自己给加的),去掉调试用的console.log[CI SKIP] [前端]统一admin中js命名方式. 为提交列表添加返回按钮[CI SKIP] [前端]修复bug,更正了不恰当的foreach循环,(js里for(var key in array)不仅遍历了数组元素,还将遍历数组其他的属性以及成员方法),修复了显示编辑区函数对选中小组错误的清除方法.(原来的做法将导致某些情况下旧的小组无法移除编辑区域. 增添了切换编辑比赛的提示,防止用户丢失为保存的信息. 添加问题列表对可见比赛的筛选[CI SKIP] [前端-BUG]修复比赛编辑区可见状态显示错误,(忘记加vm.),增加编辑成功隐藏编辑框的行为,更加方便[CI SKIP] [前端]添加比赛题目列表可见字段的显示,方便比赛管理[CI SKIP] [BUG-fix]返回按钮提示确认,修复不能弹出的问题[CI SKIP] 修复typo in submission/views.py Swagger UI docs中的拼写错误[CI SKIP] [前端]修复userList.js中关于翻页按钮状态控制函数参数的错误. 修复刚刚提交的bug[CI SKIP] [前端]修复userList页面avalon重定义问题[CI SKIP] [前端]修复问题管理(后台)页面的avalon重复定义的问题[CI SKIP] [前端]整理js格式. 修复小bugs,关于比赛密码修改变量名称的错误,小组修改变量名称错误(以上都是在修改比赛页面内)[CI SKIP] [后台]修复contestAdmin,比赛和问题API的逻辑问题,主要针对超级管理员和普通管理员的差别.写了测试,是两个api测试覆盖率达100% [migration]改model漏了一个.....[CI SKIP] [前端-后台]比赛管理,对添加,编辑,列表页面的avalon使用方法做了统一的改变,防止出现页内模板改变但页面不刷新的情况下导致avalon功能间歇性异常的问题,但是代码量变大了一些,还算是整洁.具体是所有页面的avalon只在页面第一次加载的时候初始化,再次加载时只对vm内部变量重新初始化,而不调用avalon.define了[CI SKIP] [后端]添加修改比赛题目添加对题目分数的支持 [后端]为比赛problem model添加分数(score)字段,用于记分模式的比赛 [后端]修复typo,工作正常,没写测试还 [前端]修改比赛列表页面,添加了编辑比赛,编辑比赛题目[CI SKIP] [前端]把添加比赛和添加比赛问题分开了,就是把添加问题模块从添加比赛页面删除了 [前端]添加了后台比赛列表对问题的添加修改页面[CI SKIP] ... Conflicts: static/src/js/app/admin/problem/editProblem.js static/src/js/app/admin/problem/submissionList.js submission/views.py
2015-08-25 04:49:05 +00:00
return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, "contest": contest,
2015-08-23 12:45:51 +00:00
"samples": json.loads(contest_problem.samples),
"show_warning": show_warning, "warning": warning,
"show_submit_code_area": show_submit_code_area})
@check_user_contest_permission
def contest_problems_list_page(request, contest_id):
"""
比赛所有题目的列表页
"""
contest = Contest.objects.get(id=contest_id)
2015-09-25 07:04:57 +00:00
contest_problems = ContestProblem.objects.filter(contest=contest).select_related("contest").order_by("sort_index")
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
2015-08-23 11:27:31 +00:00
"contest": {"id": contest_id}})
2015-08-22 08:08:39 +00:00
def contest_list_page(request, page=1):
"""
所有比赛的列表页
"""
2015-08-22 08:08:39 +00:00
# 正常情况
2015-08-25 14:56:07 +00:00
contests = Contest.objects.filter(visible=True).order_by("-create_time")
2015-08-22 08:08:39 +00:00
# 搜索的情况
keyword = request.GET.get("keyword", None)
if keyword:
contests = contests.filter(Q(title__contains=keyword) | Q(description__contains=keyword))
2015-08-22 08:08:39 +00:00
2015-08-22 12:42:21 +00:00
# 筛选我能参加的比赛
join = request.GET.get("join", None)
if request.user.is_authenticated and join:
contests = contests.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())). \
2015-08-22 14:16:08 +00:00
filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now())
2015-08-22 08:08:39 +00:00
paginator = Paginator(contests, 20)
try:
current_page = paginator.page(int(page))
except Exception:
return error_page(request, u"不存在的页码")
previous_page = next_page = None
try:
previous_page = current_page.previous_page_number()
except Exception:
pass
try:
next_page = current_page.next_page_number()
except Exception:
pass
return render(request, "oj/contest/contest_list.html",
2015-08-22 12:42:21 +00:00
{"contests": current_page, "page": int(page),
2015-08-22 08:08:39 +00:00
"previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "join": join})
Merge branch 'dev' into virusdefender-dev * dev: (21 commits) [前端]整理格式,去掉tab(以前用vim,它自己给加的),去掉调试用的console.log[CI SKIP] [前端]统一admin中js命名方式. 为提交列表添加返回按钮[CI SKIP] [前端]修复bug,更正了不恰当的foreach循环,(js里for(var key in array)不仅遍历了数组元素,还将遍历数组其他的属性以及成员方法),修复了显示编辑区函数对选中小组错误的清除方法.(原来的做法将导致某些情况下旧的小组无法移除编辑区域. 增添了切换编辑比赛的提示,防止用户丢失为保存的信息. 添加问题列表对可见比赛的筛选[CI SKIP] [前端-BUG]修复比赛编辑区可见状态显示错误,(忘记加vm.),增加编辑成功隐藏编辑框的行为,更加方便[CI SKIP] [前端]添加比赛题目列表可见字段的显示,方便比赛管理[CI SKIP] [BUG-fix]返回按钮提示确认,修复不能弹出的问题[CI SKIP] 修复typo in submission/views.py Swagger UI docs中的拼写错误[CI SKIP] [前端]修复userList.js中关于翻页按钮状态控制函数参数的错误. 修复刚刚提交的bug[CI SKIP] [前端]修复userList页面avalon重定义问题[CI SKIP] [前端]修复问题管理(后台)页面的avalon重复定义的问题[CI SKIP] [前端]整理js格式. 修复小bugs,关于比赛密码修改变量名称的错误,小组修改变量名称错误(以上都是在修改比赛页面内)[CI SKIP] [后台]修复contestAdmin,比赛和问题API的逻辑问题,主要针对超级管理员和普通管理员的差别.写了测试,是两个api测试覆盖率达100% [migration]改model漏了一个.....[CI SKIP] [前端-后台]比赛管理,对添加,编辑,列表页面的avalon使用方法做了统一的改变,防止出现页内模板改变但页面不刷新的情况下导致avalon功能间歇性异常的问题,但是代码量变大了一些,还算是整洁.具体是所有页面的avalon只在页面第一次加载的时候初始化,再次加载时只对vm内部变量重新初始化,而不调用avalon.define了[CI SKIP] [后端]添加修改比赛题目添加对题目分数的支持 [后端]为比赛problem model添加分数(score)字段,用于记分模式的比赛 [后端]修复typo,工作正常,没写测试还 [前端]修改比赛列表页面,添加了编辑比赛,编辑比赛题目[CI SKIP] [前端]把添加比赛和添加比赛问题分开了,就是把添加问题模块从添加比赛页面删除了 [前端]添加了后台比赛列表对问题的添加修改页面[CI SKIP] ... Conflicts: static/src/js/app/admin/problem/editProblem.js static/src/js/app/admin/problem/submissionList.js submission/views.py
2015-08-25 04:49:05 +00:00
@check_user_contest_permission
def contest_rank_page(request, contest_id):
contest = Contest.objects.get(id=contest_id)
2015-09-27 12:41:56 +00:00
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
r = redis.Redis(host=settings.REDIS_CACHE["host"], port=settings.REDIS_CACHE["port"], db=settings.REDIS_CACHE["db"])
cache_key = str(contest_id) + "_rank_cache"
rank = r.get(cache_key)
if not rank:
rank = ContestRank.objects.filter(contest_id=contest_id).\
select_related("user").\
order_by("-total_ac_number", "total_time").\
values("id", "user__id", "user__username", "user__real_name", "contest_id", "submission_info",
"total_submission_number", "total_ac_number", "total_time")
r.set(cache_key, json.dumps([dict(item) for item in rank]))
else:
rank = json.loads(rank)
try:
paging_rank = paginate_data(request, rank, None)
if request.GET.get("paging", None):
rank = paging_rank["results"]
else:
rank = paging_rank
except Exception as e:
return error_page(request, e.message)
if request.GET.get("paging", None):
paging_info = paging_rank
paging_info["offset"] = paging_rank["page_size"] * (int(paging_rank["current_page"]) - 1)
else:
paging_info = {"previous_page": None, "next_page": None, "count": 0, "total_page": 0, "offset": 0}
2015-09-24 13:25:39 +00:00
return render(request, "oj/contest/contest_rank.html",
{"rank": rank, "contest": contest,
"contest_problems": contest_problems,
"paging_info": paging_info,
"auto_refresh": request.GET.get("auto_refresh", None) == "true",
"show_real_name": request.GET.get("show_real_name", None) == "true",})
2015-09-20 06:11:03 +00:00
class ContestTimeAPIView(APIView):
"""
获取比赛开始或者结束的倒计时返回毫秒数字
"""
def get(self, request):
contest_id = request.GET.get("contest_id", -1)
try:
contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_response(u"比赛不存在")
2015-09-22 04:56:10 +00:00
return success_response({"start": int((contest.start_time - now()).total_seconds() * 1000),
"end": int((contest.end_time - now()).total_seconds() * 1000),
"status": contest.status})