[合并-dev]并修复冲突 contest/views.py

This commit is contained in:
esp
2015-08-24 18:44:36 +08:00
24 changed files with 566 additions and 61 deletions

View File

@@ -1,6 +1,6 @@
# coding=utf-8 # coding=utf-8
from functools import wraps from functools import wraps
from django.http import HttpResponse from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
from utils.shortcuts import error_response, error_page from utils.shortcuts import error_response, error_page
@@ -18,7 +18,7 @@ def login_required(func):
if request.is_ajax(): if request.is_ajax():
return error_response(u"请先登录") return error_response(u"请先登录")
else: else:
return error_page(request, u"请先登录") return HttpResponseRedirect("/login/")
return check return check
@@ -31,5 +31,5 @@ def admin_required(func):
if request.is_ajax(): if request.is_ajax():
return error_response(u"需要管理员权限") return error_response(u"需要管理员权限")
else: else:
return error_page(request, u"需要管理员权限") return error_page(request, u"需要管理员权限,如果没有登录,请先登录")
return check return check

View File

@@ -1,5 +1,6 @@
# coding=utf-8 # coding=utf-8
from django.conf.urls import include, url from django.conf.urls import include, url
from django.views.generic import TemplateView
from .tests import (LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs, from .tests import (LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs,
AdminRequiredCBVTestWithArgs, AdminRequiredCBVTestWithoutArgs) AdminRequiredCBVTestWithArgs, AdminRequiredCBVTestWithoutArgs)
@@ -15,4 +16,5 @@ urlpatterns = [
url(r'^admin_required_test/fbv/(?P<problem_id>\d+)/$', "account.tests.admin_required_FBC_test_with_args"), url(r'^admin_required_test/fbv/(?P<problem_id>\d+)/$', "account.tests.admin_required_FBC_test_with_args"),
url(r'^admin_required_test/cbv/1/$', AdminRequiredCBVTestWithoutArgs.as_view()), url(r'^admin_required_test/cbv/1/$', AdminRequiredCBVTestWithoutArgs.as_view()),
url(r'^admin_required_test/cbv/(?P<problem_id>\d+)/$', AdminRequiredCBVTestWithArgs.as_view()), url(r'^admin_required_test/cbv/(?P<problem_id>\d+)/$', AdminRequiredCBVTestWithArgs.as_view()),
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
] ]

View File

@@ -260,7 +260,7 @@ class LoginRequiredDecoratorTest(TestCase):
def test_fbv_without_args(self): def test_fbv_without_args(self):
# 没登陆 # 没登陆
response = self.client.get("/login_required_test/fbv/1/") response = self.client.get("/login_required_test/fbv/1/")
self.assertTemplateUsed(response, "utils/error.html") self.assertRedirects(response, "/login/")
# 登陆后 # 登陆后
self.client.login(username="test", password="test") self.client.login(username="test", password="test")
@@ -270,7 +270,7 @@ class LoginRequiredDecoratorTest(TestCase):
def test_fbv_with_args(self): def test_fbv_with_args(self):
# 没登陆 # 没登陆
response = self.client.get("/login_required_test/fbv/1024/") response = self.client.get("/login_required_test/fbv/1024/")
self.assertTemplateUsed(response, "utils/error.html") self.assertRedirects(response, "/login/")
# 登陆后 # 登陆后
self.client.login(username="test", password="test") self.client.login(username="test", password="test")
@@ -353,7 +353,7 @@ class AdminRequiredDecoratorTest(TestCase):
def test_cbv_without_args(self): def test_cbv_without_args(self):
# 没登陆 # 没登陆
response = self.client.get("/admin_required_test/cbv/1/") response = self.client.get("/admin_required_test/cbv/1/")
self.assertTemplateUsed(response, "utils/error.html") self.assertRedirects(response, "/login/")
# 登陆后 # 登陆后
self.client.login(username="test", password="test") self.client.login(username="test", password="test")

View File

@@ -1,7 +1,7 @@
# coding=utf-8 # coding=utf-8
from functools import wraps from functools import wraps
from django.http import HttpResponse from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
from django.utils.timezone import now from django.utils.timezone import now
@@ -30,7 +30,7 @@ def check_user_contest_permission(func):
if request.is_ajax(): if request.is_ajax():
return error_response(u"请先登录") return error_response(u"请先登录")
else: else:
return error_page(request, u"请先登录") return HttpResponseRedirect("/login/")
# kwargs 就包含了url 里面的播或参数 # kwargs 就包含了url 里面的播或参数
if "contest_id" in kwargs: if "contest_id" in kwargs:

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contest', '0004_remove_contestproblem_difficulty'),
]
operations = [
migrations.CreateModel(
name='ContestSubmission',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('total_submission_number', models.IntegerField(default=1)),
('ac', models.BooleanField()),
('total_time', models.IntegerField(default=0)),
('contest', models.ForeignKey(to='contest.Contest')),
('problem', models.ForeignKey(to='contest.ContestProblem')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'contest_submission',
},
),
]

View File

@@ -59,3 +59,20 @@ class ContestProblemTestCase(models.Model):
class Meta: class Meta:
db_table = "contest_problem_test_case" db_table = "contest_problem_test_case"
class ContestSubmission(models.Model):
"""
用于保存比赛提交和排名的一些数据,加快检索速度
"""
user = models.ForeignKey(User)
problem = models.ForeignKey(ContestProblem)
contest = models.ForeignKey(Contest)
total_submission_number = models.IntegerField(default=1)
# 这道题是 AC 还是没过
ac = models.BooleanField()
# 总的时间用于acm 类型的,也需要保存罚时
total_time = models.IntegerField(default=0)
class Meta:
db_table = "contest_submission"

View File

@@ -17,7 +17,7 @@ from account.decorators import login_required
from group.models import Group from group.models import Group
from announcement.models import Announcement from announcement.models import Announcement
from .models import Contest, ContestProblem from .models import Contest, ContestProblem, ContestSubmission
from .decorators import check_user_contest_permission from .decorators import check_user_contest_permission
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
CreateContestProblemSerializer, ContestProblemSerializer, CreateContestProblemSerializer, ContestProblemSerializer,
@@ -284,6 +284,40 @@ def contest_page(request, contest_id):
return render(request, "oj/contest/contest_index.html", {"contest": contest}) return render(request, "oj/contest/contest_index.html", {"contest": contest})
@check_user_contest_permission
def contest_problem_page(request, contest_id, contest_problem_id):
try:
contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except ContestProblem.DoesNotExist:
return error_page(request, u"比赛题目不存在")
show_warning = False
try:
submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem)
show_warning = submission.ac
except ContestSubmission.DoesNotExist:
pass
return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem,
"samples": json.loads(contest_problem.samples),
"show_warning": show_warning})
@check_user_contest_permission
def contest_problems_list_page(request, contest_id):
try:
contest_problems = ContestProblem.objects.filter(contest=Contest.objects.get(id=contest_id)).order_by("sort_index")
except Contest.DoesNotExist:
return error_page(request, u"比赛题目不存在")
# 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
"announcements": announcements,
"contest": {"id": contest_id}})
def contest_list_page(request, page=1): def contest_list_page(request, page=1):
# 正常情况 # 正常情况
contests = Contest.objects.filter(visible=True) contests = Contest.objects.filter(visible=True)
@@ -319,10 +353,10 @@ def contest_list_page(request, page=1):
# 右侧的公告列表 # 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
# 系统当前时间
now = datetime.datetime.now()
return render(request, "oj/contest/contest_list.html", return render(request, "oj/contest/contest_list.html",
{"contests": current_page, "page": int(page), {"contests": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "announcements": announcements, "keyword": keyword, "announcements": announcements,
"join": join, "now": now}) "join": join})

View File

@@ -1,10 +1,13 @@
# coding=utf-8 # coding=utf-8
import logging import logging
import redis import redis
from judge.judger_controller.settings import redis_config from judge.judger_controller.settings import redis_config
from judge.judger.result import result from judge.judger.result import result
from submission.models import Submission from submission.models import Submission
from problem.models import Problem from problem.models import Problem
from contest.models import ContestProblem, Contest, ContestSubmission
logger = logging.getLogger("app_info") logger = logging.getLogger("app_info")
@@ -22,17 +25,68 @@ class MessageQueue(object):
submission = Submission.objects.get(id=submission_id) submission = Submission.objects.get(id=submission_id)
except Submission.DoesNotExist: except Submission.DoesNotExist:
logger.warning("Submission does not exist, submission_id: " + submission_id) logger.warning("Submission does not exist, submission_id: " + submission_id)
pass continue
if submission.result == result["accepted"]: if submission.result == result["accepted"] and not submission.contest_id:
# 更新题目的 ac 计数器 # 更新普通题目的 ac 计数器
try: try:
problem = Problem.objects.get(id=submission.problem_id) problem = Problem.objects.get(id=submission.problem_id)
problem.total_accepted_number += 1 problem.total_accepted_number += 1
problem.save() problem.save()
except Problem.DoesNotExist: except Problem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id) logger.warning("Submission problem does not exist, submission_id: " + submission_id)
pass # 普通题目的话,到这里就结束了
continue
# 能运行到这里的都是比赛题目
try:
contest = Contest.objects.get(id=submission.contest_id)
contest_problem = ContestProblem.objects.get(contest=contest, id=submission.problem_id)
except Contest.DoesNotExist:
logger.warning("Submission contest does not exist, submission_id: " + submission_id)
continue
except ContestProblem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
continue
try:
contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest,
problem_id=contest_problem.id)
if submission.result == result["accepted"]:
# 避免这道题已经 ac 了,但是又重新提交了一遍
if not contest_submission.ac:
# 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时
logger.debug(contest.start_time)
logger.debug(submission.create_time)
logger.debug((submission.create_time - contest.start_time).total_seconds())
contest_submission.total_time += int((submission.create_time - contest.start_time).total_seconds() / 60)
# 标记为已经通过
contest_submission.ac = True
# contest problem ac 计数器加1
contest_problem.total_accepted_number += 1
else:
# 如果这个提交是错误的就罚时20分钟
contest_submission.ac = False
contest_submission.total_time += 20
contest_submission.save()
contest_problem.save()
except ContestSubmission.DoesNotExist:
# 第一次提交
is_ac = submission.result == result["accepted"]
# 增加题目总提交数计数器
contest_problem.total_submit_number += 1
if is_ac:
total_time = 0
# 增加题目总的ac数计数器
contest_problem.total_accepted_number += 1
contest_problem.save()
else:
# 没过罚时20分钟
total_time = 20
ContestSubmission.objects.create(user_id=submission.user_id, contest=contest, problem=contest_problem,
ac=is_ac, total_time=total_time)
logger.debug("Start message queue") logger.debug("Start message queue")

View File

@@ -16,7 +16,7 @@ from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
from admin.views import AdminTemplateView from admin.views import AdminTemplateView
from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView
from submission.views import SubmissionAPIView, SubmissionAdminAPIView from submission.views import SubmissionAPIView, SubmissionAdminAPIView, ContestSubmissionAPIView
from monitor.views import QueueLengthMonitorAPIView from monitor.views import QueueLengthMonitorAPIView
@@ -40,7 +40,14 @@ urlpatterns = [
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"), url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"), url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"), url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^contest/(?P<contest_id>\d+)/problem/(?P<contest_problem_id>\d+)/$', "contest.views.contest_problem_page",
name="contest_problem_page"),
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"), url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^contest/(?P<contest_id>\d+)/problems/$', "contest.views.contest_problems_list_page",
name="contest_problems_list_page"),
url(r'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page", url(r'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page",
name="announcement_page"), name="announcement_page"),
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"),
@@ -60,6 +67,10 @@ urlpatterns = [
url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"),
url(r'^problem/(?P<problem_id>\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page", url(r'^problem/(?P<problem_id>\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page",
name="problem_my_submissions_page"), name="problem_my_submissions_page"),
url(r'^contest/(?P<contest_id>\d+)/problem/(?P<contest_problem_id>\d+)/my_submissions/$',
"submission.views.contest_problem_my_submissions_list_page", name="contest_problem_my_submissions_list_page"),
url(r'^my_submission/(?P<submission_id>\w+)/$', "submission.views.my_submission", name="my_submission_page"), url(r'^my_submission/(?P<submission_id>\w+)/$', "submission.views.my_submission", name="my_submission_page"),
url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(),
@@ -71,5 +82,6 @@ urlpatterns = [
url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"), url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"),
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"), url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"), url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_api"),
] ]

View File

@@ -87,4 +87,8 @@ li.list-group-item {
position: relative; position: relative;
outline: none; outline: none;
}
.contest-tab{
margin-bottom: 5px;
} }

View File

@@ -14,8 +14,15 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
//成功登陆 //成功登陆
var ref = document.referrer; var ref = document.referrer;
if(ref){ if(ref){
if(ref.split("/")[2] == location.hostname){ // 注册页和本页的来源的跳转回首页,防止死循环
if(ref.indexOf("register") > -1 || ref.indexOf("login") > -1){
location.href = "/";
return;
}
// 判断来源,只有同域下才跳转
if(ref.split("/")[2].split(":")[0] == location.hostname){
location.href = ref; location.href = ref;
return;
} }
} }
location.href = "/"; location.href = "/";

View File

@@ -56,7 +56,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
var counter = 0; var counter = 0;
function getResult() { function getResult() {
if(counter++ > 10){ if (counter++ > 10) {
hideLoading(); hideLoading();
bsAlert("抱歉,服务器可能出现了故障,请稍后到我的提交列表中查看"); bsAlert("抱歉,服务器可能出现了故障,请稍后到我的提交列表中查看");
counter = 0; counter = 0;
@@ -88,12 +88,32 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
} }
$("#submit-code-button").click(function () { $("#submit-code-button").click(function () {
var problemId = window.location.pathname.split("/")[2];
var code = codeEditor.getValue(); var code = codeEditor.getValue();
if (location.href.indexOf("contest") > -1) {
var problemId = location.pathname.split("/")[4];
var contestId = location.pathname.split("/")[2];
var url = "/api/contest/submission/";
var data = {
problem_id: problemId,
language: language,
code: code,
contest_id: contestId
};
}
else {
var problemId = window.location.pathname.split("/")[2];
var url = "/api/submission/";
var data = {
problem_id: problemId,
language: language,
code: code
};
}
showLoading(); showLoading();
if(!code.trim()){ if (!code.trim()) {
bsAlert("请填写代码!"); bsAlert("请填写代码!");
hideLoading(); hideLoading();
return false; return false;
@@ -101,15 +121,12 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
$("#result").html(""); $("#result").html("");
$.ajax({ $.ajax({
beforeSend: csrfTokenHeader, beforeSend: csrfTokenHeader,
url: "/api/submission/", url: url,
method: "post", method: "post",
data: JSON.stringify({ data: JSON.stringify(data),
problem_id: problemId,
language: language,
code: codeEditor.getValue()
}),
contentType: "application/json", contentType: "application/json",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
@@ -118,7 +135,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
setTimeout(getResult, 2000); setTimeout(getResult, 2000);
} }
else { else {
bs_alert(data.data); bsAlert(data.data);
hideLoading(); hideLoading();
} }
} }
@@ -127,11 +144,11 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro
}); });
$.ajax({ $.ajax({
url : "/api/user/", url: "/api/user/",
method: "get", method: "get",
dataType: "json", dataType: "json",
success: function(data){ success: function (data) {
if(data.code){ if (data.code) {
$("#submit-code-button").attr("disabled", "disabled"); $("#submit-code-button").attr("disabled", "disabled");
$("#result").html('<div class="alert alert-danger" role="alert"><div class="alert-link">请先<a href="/login/" target="_blank">登录</a>!</div> </div>'); $("#result").html('<div class="alert alert-danger" role="alert"><div class="alert-link">请先<a href="/login/" target="_blank">登录</a>!</div> </div>');
} }

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('submission', '0003_auto_20150821_1654'),
]
operations = [
migrations.RemoveField(
model_name='submission',
name='is_counted',
),
]

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('submission', '0004_remove_submission_is_counted'),
]
operations = [
migrations.AddField(
model_name='submission',
name='contest_id',
field=models.IntegerField(null=True, blank=True),
),
]

View File

@@ -11,6 +11,7 @@ class Submission(models.Model):
result = models.IntegerField(default=result["waiting"]) result = models.IntegerField(default=result["waiting"])
language = models.IntegerField() language = models.IntegerField()
code = models.TextField() code = models.TextField()
contest_id = models.IntegerField(blank=True, null=True)
problem_id = models.IntegerField(db_index=True) problem_id = models.IntegerField(db_index=True)
# 这个字段可能存储很多数据 比如编译错误、系统错误的时候,存储错误原因字符串 # 这个字段可能存储很多数据 比如编译错误、系统错误的时候,存储错误原因字符串
# 正常运行的时候存储 lrun 的判题结果比如cpu时间内存之类的 # 正常运行的时候存储 lrun 的判题结果比如cpu时间内存之类的
@@ -18,7 +19,6 @@ class Submission(models.Model):
accepted_answer_time = models.IntegerField(blank=True, null=True) accepted_answer_time = models.IntegerField(blank=True, null=True)
# 这个字段只有在题目是accepted 的时候才会用到,比赛题目的提交可能还会有得分等信息,存储在这里面 # 这个字段只有在题目是accepted 的时候才会用到,比赛题目的提交可能还会有得分等信息,存储在这里面
accepted_answer_info = models.TextField(blank=True, null=True) accepted_answer_info = models.TextField(blank=True, null=True)
is_counted = models.BooleanField(default=False)
class Meta: class Meta:
db_table = "submission" db_table = "submission"

View File

@@ -19,4 +19,11 @@ class SubmissionSerializer(serializers.ModelSerializer):
fields = ["id", "result", "create_time", "language", "user"] fields = ["id", "result", "create_time", "language", "user"]
def _get_submission_user(self, obj): def _get_submission_user(self, obj):
return User.objects.get(id=obj.user_id).username return User.objects.get(id=obj.user_id).username
class CreateContestSubmissionSerializer(serializers.Serializer):
contest_id = serializers.IntegerField()
problem_id = serializers.IntegerField()
language = serializers.IntegerField()
code = serializers.CharField(max_length=3000)

View File

@@ -1,21 +1,25 @@
# coding=utf-8 # coding=utf-8
import json import json
import redis
import redis
from django.shortcuts import render from django.shortcuts import render
from django.core.paginator import Paginator
from rest_framework.views import APIView from rest_framework.views import APIView
from judge.judger.result import result
from judge.judger_controller.tasks import judge from judge.judger_controller.tasks import judge
from judge.judger_controller.settings import redis_config from judge.judger_controller.settings import redis_config
from account.decorators import login_required from account.decorators import login_required
from account.models import SUPER_ADMIN from account.models import SUPER_ADMIN
from contest.decorators import check_user_contest_permission
from problem.models import Problem from problem.models import Problem
from contest.models import Contest, ContestProblem
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
from .models import Submission from .models import Submission
from .serializers import CreateSubmissionSerializer, SubmissionSerializer from .serializers import CreateSubmissionSerializer, SubmissionSerializer, CreateContestSubmissionSerializer
from django.core.paginator import Paginator
class SubmissionAPIView(APIView): class SubmissionAPIView(APIView):
@@ -79,6 +83,22 @@ def problem_my_submissions_list_page(request, problem_id):
{"submissions": submissions, "problem": problem}) {"submissions": submissions, "problem": problem})
@login_required
def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id):
try:
Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except Problem.DoesNotExist:
return error_page(request, u"比赛问题不存在")
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by("-create_time"). \
values("id", "result", "create_time", "accepted_answer_time", "language")
return render(request, "oj/contest/my_submissions_list.html",
{"submissions": submissions, "contest_problem": contest_problem})
@login_required @login_required
def my_submission(request, submission_id): def my_submission(request, submission_id):
try: try:
@@ -136,3 +156,43 @@ def my_submission_list_page(request, page=1):
return render(request, "oj/submission/my_submissions_list.html", return render(request, "oj/submission/my_submissions_list.html",
{"submissions": current_page, "page": int(page), {"submissions": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20}) "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20})
class ContestSubmissionAPIView(APIView):
@check_user_contest_permission
def post(self, request):
"""
创建比赛的提交
---
request_serializer: ConestSubmissionSerializer
"""
serializer = CreateContestSubmissionSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
contest = Contest.objects.get(id=data["contest_id"])
except Contest.DoesNotExist:
return error_response(u"比赛不存在")
try:
problem = ContestProblem.objects.get(contest=contest, id=data["problem_id"])
# 更新题目提交计数器
problem.total_submit_number += 1
problem.save()
except Problem.DoesNotExist:
return error_response(u"题目不存在")
submission = Submission.objects.create(user_id=request.user.id, language=int(data["language"]),
contest_id=contest.id, code=data["code"], problem_id=problem.id)
try:
judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception:
return error_response(u"提交判题任务失败")
# 增加redis 中判题队列长度的计数器
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
r.incr("judge_queue_length")
return success_response({"submission_id": submission.id})
else:
return serializer_invalid_response(serializer)

View File

@@ -0,0 +1,104 @@
{% extends 'oj_base.html' %}
{% block body %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li>
<li role="presentation"><a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/my_submissions/">我的提交</a></li>
</ul>
<h2 class="text-center">{{ contest_problem.title }}</h2>
<p class="text-muted text-center">发布时间 : {{ contest_problem.create_time }}&nbsp;&nbsp;
时间限制 : {{ contest_problem.time_limit }}ms&nbsp;&nbsp;
内存限制 : {{ contest_problem.memory_limit }}M
</p>
<div>
<div class="problem-section">
<label class="problem-label">描述</label>
<p class="problem-detail">{{ contest_problem.description|safe }}</p>
</div>
<div class="problem-section">
<label class="problem-label">输入</label>
<p class="problem-detail">{{ contest_problem.input_description }}</p>
</div>
<div class="problem-section">
<label class="problem-label">输出</label>
<p class="problem-detail">{{ contest_problem.output_description }}k</p>
</div>
{% for item in samples %}
<div class="problem-section">
<label class="problem-label">样例输入{{ forloop.counter }}</label>
<pre>
{{ item.input }}</pre>
</div>
<div class="problem-section">
<label class="problem-label">样例输出{{ forloop.counter }}</label>
<pre>
{{ item.output }}</pre>
</div>
{% endfor %}
{% if problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<p class="problem-detail">{{ contest_problem.hint|safe }}</p>
</div>
{% endif %}
{% if contest_problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<p class="problem-detail">{{ contest_problem.hint|safe }}</p>
</div>
{% endif %}
<div>
<label>选择语言</label>
<div>
<label class="radio-inline">
<input type="radio" name="language" value="1" checked> C (gcc 4.8)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="2"> C++ (g++ 4.3)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="3"> Java (jre 1.7)
</label>
</div>
</div>
<div id="code-field">
<label class="problem-label">提交代码</label>
<textarea id="code-editor"></textarea>
</div>
<hr>
<div id="submit-code">
<button type="button" class="btn btn-primary" id="submit-code-button">
提交代码
</button>
<img src="/static/img/loading.gif" id="loading-gif">
</div>
{% if show_warning %}
<div class="alert alert-success" role="alert">您已经提交过本题的正确答案了!</div>
{% endif %}
<div id="result">
</div>
<hr>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/problem/problem.js"></script>
{% endblock %}

View File

@@ -0,0 +1,82 @@
{% extends "oj_base.html" %}
{% block body %}
{% load problem %}
<div class="container main">
<div class="row">
<div class="col-lg-12 contest-tab">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation">
<a href="/contest/{{ contest.id }}/">比赛详情</a>
</li>
<li role="presentation" class="active">
<a href="/contest/{{ contest.id }}/problems/">题目</a>
</li>
<li role="presentation">
<a href="/contest/{{ contest.id }}/submissions/">提交</a>
</li>
<li role="presentation">
<a href="/contest/{{ contest.id }}/rank/">排名</a>
</li>
</ul>
</div>
<div class="col-lg-9">
<div>
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th>#</th>
<th>题目</th>
<th>通过率</th>
</tr>
</thead>
<tbody>
{% for item in contest_problems %}
<tr>
<th>
<span class="glyphicon glyphicon-ok ac-flag"></span>
</th>
<th scope="row">
<a href="/contest/{{ item.contest.id }}/problem/{{ item.id }}/" target="_blank">{{ item.sort_index }}</a>
</th>
<td>
<a href="/contest/{{ item.contest.id }}/problem/{{ item.id }}/" target="_blank">{{ item.title }}</a>
</td>
<td>{{ item|accepted_radio }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<nav>
<ul class="pager">
{% if previous_page %}
<li class="previous"><a
href="/problems/{{ previous_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页</a></li>
{% endif %}
{% if next_page %}
<li class="next"><a
href="/problems/{{ next_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">下一页 <span
aria-hidden="true">&rarr;</span></a></li>
{% endif %}
</ul>
</nav>
</div>
</div>
<div class="col-lg-3">
{% include "oj/announcement/_announcement_panel.html" %}
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/problem/problem_list.js"></script>
{% endblock %}

View File

@@ -0,0 +1,52 @@
{% extends 'oj_base.html' %}
{% block body %}
{% load submission %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li>
<li role="presentation" class="active">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/my_submissions/">
我的提交</a>
</li>
</ul>
<h2 class="text-center">{{ contest_problem.title }}</h2>
<p class="text-muted text-center">发布时间: {{ contest_problem.create_time }}&nbsp;&nbsp;
时间限制: {{ contest_problem.time_limit }}ms&nbsp;&nbsp;
内存限制: {{ contest_problem.memory_limit }}M</p>
<table class="table table-bordered">
<thead>
<tr class="" success>
<th>#</th>
<th>提交时间</th>
<th>结果</th>
<th>运行时间</th>
<th>语言</th>
</tr>
</thead>
<tbody>
{% for item in submissions %}
<tr class="{{ item.result|translate_result_class }}">
<th scope="row"><a href="/my_submission/{{ item.id }}/">{{ forloop.counter }}</a></th>
<td>{{ item.create_time }}</td>
<td>{{ item.result|translate_result }}</td>
<td>
{% if item.accepted_answer_time %}
{{ item.accepted_answer_time }}ms
{% else %}
--
{% endif %}
</td>
<td>
{{ item.language|translate_language }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@@ -1,15 +0,0 @@
{% extends "oj_base.html" %}
{% block body %}
<div class="container main">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active">
<a href="problem.html">题目</a></li>
<li role="presentation"><a href="my_solutions_list.html">我的提交</a></li>
<li role="presentation"><a href="#">排名</a></li>
</ul>
<h2 class="text-center">第一次比赛</h2>
<p class="text-muted text-center"><b>开始时间:</b> 2015-6-8 19:00 <b>结束时间:</b> 2015-9-1 12:00</p>
</div>
{% endblock %}

View File

@@ -90,7 +90,7 @@
<body> <body>
<div id="header"> <div id="header">
<span id="name">qduoj</span> <span id="name">qduoj</span>
<a href="/problems/">题目</a>&nbsp;&nbsp;<a href="#">比赛</a>&nbsp;&nbsp;<a href="#">小组</a> <a href="/problems/">题目</a>&nbsp;&nbsp;<a href="/contests/">比赛</a>&nbsp;&nbsp;<a href="#">小组</a>
</div> </div>
<div id="fullpage"> <div id="fullpage">

View File

@@ -22,17 +22,17 @@
<th>#</th> <th>#</th>
<th>题目</th> <th>题目</th>
<th><a href="/problems/?order_by=difficulty">难度</a></th> <th><a href="/problems/?order_by=difficulty">难度</a></th>
<th><a href="/problems/?order_by=aceptance">通过率</a></th> <th><a href="/problems/?order_by=acceptance">通过率</a></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for item in problems %} {% for item in problems %}
<tr> <tr>
<th><span class="glyphicon glyphicon-ok ac-flag"></span></th> <th><span class="glyphicon glyphicon-ok ac-flag"></span></th>
<th scope="row"><a href="/problem/{{ item.id }}/">{{ item.id }}</a></th> <th scope="row"><a href="/problem/{{ item.id }}/" target="_blank">{{ item.id }}</a></th>
<td><a href="/problem/{{ item.id }}/">{{ item.title }}</a></td> <td><a href="/problem/{{ item.id }}/" target="_blank">{{ item.title }}</a></td>
<td>{{ item.difficulty }}</td> <td>{{ item.difficulty }}</td>
<td>{{ item|accepted_radio }}%</td> <td>{{ item|accepted_radio }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -2,13 +2,13 @@
def get_problem_accepted_radio(problem): def get_problem_accepted_radio(problem):
if problem.total_accepted_number: if problem.total_submit_number:
return int((problem.total_accepted_number * 100) / problem.total_submit_number) return str(int((problem.total_accepted_number * 100) / problem.total_submit_number)) \
return 0 + "% (" + str(problem.total_accepted_number) + "/" + str(problem.total_submit_number) + ")"
return "0%"
from django import template from django import template
register = template.Library() register = template.Library()
register.filter("accepted_radio", get_problem_accepted_radio) register.filter("accepted_radio", get_problem_accepted_radio)